Moving motor control PID to a faster base-thread?
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'?
Please Log in or Create an account to join the conversation.
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.
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.
Yes.
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?
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.
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.
Please Log in or Create an account to join the conversation.
It appears that the LinuxCNC PID does do this: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'.
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.
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.But possibly there is a bit of 'yeah, well, you can cross signals from thread X to thread Y, but...' hidden somewhere.
Please Log in or Create an account to join the conversation.
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.
- Todd Zuercher
- Offline
- Platinum Member
- Posts: 5007
- Thank you received: 1441
Please Log in or Create an account to join the conversation.
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.