Moving motor control PID to a faster base-thread?

More
23 Oct 2014 04:39 - 23 Oct 2014 04:40 #52312 by DaBit
I tried to move some parts of the motor control loop to a faster base thread using a variant of this:
loadrt motmod base_period_nsec=250000 base_thread_fp=1 servo_period_nsec=1000000 blah
..
addf motion-command-handler          servo-thread
addf motion-controller                          servo-thread
..
addf hm2_5i25.0.read     base-thread
addf hm2_5i25.0.write    base-thread
addf some_pid.do-pid-calcs           base-thread
..
net x-pos-cmd axis.0.motor-pos-cmd => some_pid.command
net x-motor-cmd some_pid.output => hm2_5i25.0.7i77.0.1.analogout0
net x-pos-fb hm2_5i25.0.encoder.03.position => axis.0.motor-pos-fb
..
..

Now, this does not produce errors during loading. But when I try to move a joint, I hear a squealing sound and I receive a following error.
base-thread and servo-thread frequencies do not matter. Only when you defint the base-thread frequency equal to the servo-thread frequency, there is no base-thread and the fist addf that uses it fails.

Do I need to do something special with the nets/pins/signals when I cross 'thread domains'?
Last edit: 23 Oct 2014 04:40 by DaBit.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 08:24 #52318 by andypugh

I tried to move some parts of the motor control loop to a faster base thread


PID calcs require floating-point calculations. Normally the base thread does not allow FP.

There is an INI switch to allow it, now. But there are other things to consider....

The Hostmot2 outputs, other than GPIO and inputs (other than GPIO) only update at the servo rate.

If you run the PID calcs 100x faster then the PGain stays the same, but you need to divide IGain by 100 and multiply Dgain by 100.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 14:28 #52321 by DaBit

I tried to move some parts of the motor control loop to a faster base thread


PID calcs require floating-point calculations. Normally the base thread does not allow FP.


I thought that is what the 'base_thread_fp=1' switch while loading motmod was for?
Would it be better to create the threads separately from motmod (loadrt threads name1=servo-thread period1=1000000 fp1=1 name2=motorcontrol-thread period2=200000 fp2=1)?

There is an INI switch to allow it, now. But there are other things to consider....
The Hostmot2 outputs, other than GPIO and inputs (other than GPIO) only update at the servo rate.


Isn't that dependant on the thread where the hm2_5i25.0.read/hm2_5i25.0.write functions are assigned to?
So if I addf() them to the faster thread their outputs should run at the faster rate? (up to certain limits of course; PCW already indicated that the SSLBP at 2,5MBaud saturates around 6-8kHz)

If you run the PID calcs 100x faster then the PGain stays the same, but you need to divide IGain by 100 and multiply Dgain by 100.


That makes no sense since that does not improve loop speed.
When running a torque-mode servo using a nested velocity and position loop it makes a lot of sense to move the velocity loop and the analog/encoder I/O to a faster thread. The position loop, trajectory planner, etc. work fine at the default 1kHz.

Currently I run everything at a 5kHz servo thread, which is too fast for the motion core (UI is becoming a tad sluggish from time to time), but a bit on the slow side for the servo loop.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 16:06 #52325 by andypugh

The Hostmot2 outputs, other than GPIO and inputs (other than GPIO) only update at the servo rate.


Isn't that dependant on the thread where the hm2_5i25.0.read/hm2_5i25.0.write functions are assigned to?

Yes.

If you run the PID calcs 100x faster then the PGain stays the same, but you need to divide IGain by 100 and multiply Dgain by 100.


That makes no sense since that does not improve loop speed.[/quote]
What I am saying is that the I term is a discrete integral in the time domain of the error and the D term is a discrete differential. If you change the time-base then the gains need to change assuming that the controlled system remains the same.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 16:47 - 23 Oct 2014 16:51 #52327 by DaBit

What I am saying is that the I term is a discrete integral in the time domain of the error and the D term is a discrete differential. If you change the time-base then the gains need to change assuming that the controlled system remains the same.


I did not look into the PID source code, but I would expect them to be dependant on wall clock time instead of period, so an error of 1.0 would integrate with a rate of 1.0/sec and not 1.0/fperiod. Thus 'Integrator += error * fperiod' instead of 'Integrator += error'.

But of course the gains should still change slightly because when the sampling period changes, the phase relationships do so too.

However, I started with a servo-thread only configuration that has its servo-period at 250000nsec (4kHz) and modified it into a 2-thread configuration with a 4kHz base-thread and 1kHz servo-thread, so coefficients should stay the same.

I did not investigate the issue further since I was actually completely modifying the servo control architecture and having too many unknowns is a bad thing.

But I noticed that HAL components that use both a base thread and servo thread like stepgen have some form of 'capture-position' function. In stepgen the reason seems to be atomic read of a 64-bit value and convert things to floating point which cannot be done in a normal non-FP basethread.

But possibly there is a bit of 'yeah, well, you can cross signals from thread X to thread Y, but...' hidden somewhere.
Probably the correct method of testing 2-thread vs 1-thread is to setup a separate test bench which could run using halcmd -I -f testbench.hal instead of modifying a 600+ lines existing HAL file.
Last edit: 23 Oct 2014 16:51 by DaBit.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 17:02 #52328 by andypugh

I did not look into the PID source code, but I would expect them to be dependant on wall clock time instead of period, so an error of 1.0 would integrate with a rate of 1.0/sec and not 1.0/fperiod. Thus 'Integrator += error * fperiod' instead of 'Integrator += error'.

It appears that the LinuxCNC PID does do this:
git.linuxcnc.org/gitweb?p=linuxcnc.git;a...b1ebb0d;hb=HEAD#l346
I have seen other implementations that don't because it is normally an unnecessary computation.

But possibly there is a bit of 'yeah, well, you can cross signals from thread X to thread Y, but...' hidden somewhere.

Part of the point of HAL is that the pins allow the seamless and atomic sharing of values between threads and between userspace and realtime. If the values are transfererred through HAL pins then you don't have anything to worry about.

Please Log in or Create an account to join the conversation.

More
23 Oct 2014 17:39 #52329 by DaBit

I have seen other implementations that don't because it is normally an unnecessary computation.


That is only true if there is no jitter at all. In real life systems there is jitter, and that effectively changes the coefficients (PID&co) and assumptions (TP) if the actual period is not taken into account.

Let's assume we have a 1-second PID period.
If we evaluate the PID function exactly once a second and we have a 1.0 error, the integrator does what we expect. At t=0 it is 0.0, at t=1 it is 1.0, at t=2 it is 2.0, etc.
Now we have a 50% jitter. That might result in t=0 -> 0.0, t=0.5 ->1.0. So effectively we doubled the influence of the integrator for that cycle.

In LinuxCNC this does not matter anyway since fperiod is a constant and not 'actual time elapsed since previous invocation', which it should have been. In RT components generated with 'comp' at least. Modifying 'comp' to use rtapi_get_time() to calculate the actual fperiod would make many components behave more accurate.

But I doubt it will cause any measureable improvement in real life.

Part of the point of HAL is that the pins allow the seamless and atomic sharing of values between threads and between userspace and realtime. If the values are transfererred through HAL pins then you don't have anything to worry about.


OK, that is the answer I was hoping to get.

Please Log in or Create an account to join the conversation.

More
29 Oct 2014 11:08 #52519 by Todd Zuercher
I think I am able to get a base thread working with a pid loop running in it. It seams to work anyway. I haven't had much of a chance to play with it. though. I'll try to see if I can actually get a decent tune on it. I think I need to increase the filtering on the velocity feed back.

Please Log in or Create an account to join the conversation.

More
29 Oct 2014 15:31 #52521 by DaBit
Seems to work here too; the problem was probably a less than optimal order of addf()'s since I tend to group things logically in the files. That is a bad thing since LinuxCNC executes those in the order given.

But so far there are other things preventing a higher base-thread from being useful; much higher than 5kHz and I am getting Mesa sserial errors.
Using two different threads might cause some extra delay here and there since synhronisation is needed and when the base-thread speed increase is only 'a few percent' that only worsens the situation over a single slightly slower thread.

Regarding filtering of the velocity signal: be careful with that. Every advantage has a disadvantage too. Filtering reduces noise and jitter on the velocity signal and therefore allows a higher Kd and much less (if any) noise from the motors due to the Kp and Kd term modulating the output in response to the noisy error signal. But in return for doing so the filter adds a delay, and the control loop phase margin lost due to the introduced extra delay quickly defeats the advantage of the filter.

I setted it low enough to get rid of some high frequency jitter, but not low enough to cause much extra delay. A biquad lowpass in the range 800Hz-1500Hz with Q-factor 0,577-0,707 seemed the best compromise in my case, but yours might be different.
I also tried a sliding cutoff frequency so more averaging happens at lower velocities and less filtering at higher velocities, but once again that only reduced performance. Especially at low velocities the position control loop has to work hard and the higher the response speed of the velocity loop the better.

Please Log in or Create an account to join the conversation.

Time to create page: 0.119 seconds
Powered by Kunena Forum