While working on a CNC with servo motors controlled by the +LinuxCNC +PID +controller, I recently had to learn how to tune the collection of values +that control such mathematical machinery that a PID controller is. It +proved to be a lot harder than I hoped, and I still have not succeeded +in getting the Z PID controller to successfully defy gravity, nor X +and Y to move accurately and reliably. But while climbing up this +rather steep learning curve, I discovered that some motor control +systems are able to tune their PID controllers. I got the impression +from the documentation that LinuxCNC were not. This proved to be not +true
+ +The LinuxCNC +pid +component is the recommended PID controller to use. It uses eight +constants Pgain, Igain, Dgain, +bias, FF0, FF1, FF2 and +FF3 to calculate the output value based on current and wanted +state, and all of these need to have a sensible value for the +controller to behave properly. Note, there are even more values +involved, theser are just the most important ones. In my case I need +the X, Y and Z axes to follow the requested path with little error. +This has proved quite a challenge for someone who have never tuned a +PID controller before, but there is at least some help to be found. + +
I discovered that included in LinuxCNC was this old PID component +at_pid claiming to have auto tuning capabilities. Sadly it had been +neglected since 2011, and could not be used as a plug in replacement +for the default pid component. One would have to rewriting the +LinuxCNC HAL setup to test at_pid. This was rather sad, when I wanted +to quickly test auto tuning to see if it did a better job than me at +figuring out good P, I and D values to use.
+ +I decided to have a look if the situation could be improved. This +involved trying to understand the code and history of the pid and +at_pid components. Apparently they had a common ancestor, as code +structure, comments and variable names were quite close to each other. +Sadly this was not reflected in the git history, making it hard to +figure out what really happened. My guess is that the author of +at_pid.c +took a version of +pid.c, +rewrote it to follow the structure he wished pid.c to have, then added +support for auto tuning and finally got it included into the LinuxCNC +repository. The restructuring and lack of early history made it +harder to figure out which part of the code were relevant to the auto +tuning, and which part of the code needed to be updated to work the +same way as the current pid.c implementation. I started by trying to +isolate relevant changes in pid.c, and applying them to at_pid.c. My +aim was to make sure the at_pid component could replace the pid +component with a simple change in the HAL setup loadrt line, without +having to "rewire" the rest of the HAL configuration. After a few +hours following this approach, I had learned quite a lot about the +code structure of both components, while concluding I was heading down +the wrong rabbit hole, and should get back to the surface and find a +different path.
+ +For the second attempt, I decided to throw away all the PID control +related part of the original at_pid.c, and instead isolate and lift +the auto tuning part of the code and inject it into a copy of pid.c. +This ensured compatibility with the current pid component, while +adding auto tuning as a run time option. To make it easier to identify +the relevant parts in the future, I wrapped all the auto tuning code +with '#ifdef AUTO_TUNER'. The end result behave just like the current +pid component by default, as that part of the code is identical. The +end result +entered the LinuxCNC master branch a few days ago.
+ +To enable auto tuning, one need to set a few HAL pins in the PID +component. The most important ones are tune-effort, +tune-mode and tune-start. But lets take a step +back, and see what the auto tuning code will do. I do not know the +mathematical foundation of the at_pid algorithm, but from observation +I can tell that the algorithm will, when enabled, produce a square +wave pattern centered around the bias value on the output pin +of the PID controller. This can be seen using the HAL Scope provided +by LinuxCNC. In my case, this is translated into voltage (+-10V) sent +to the motor controller, which in turn is translated into motor speed. +So at_pid will ask the motor to move the axis back and forth. The +number of cycles in the pattern is controlled by the +tune-cycles pin, and the extremes of the wave pattern is +controlled by the tune-effort pin. Of course, trying to +change the direction of a physical object instantly (as in going +directly from a positive voltage to the equivalent negative voltage) +do not change velocity instantly, and it take some time for the object +to slow down and move in the opposite direction. This result in a +more smooth movement wave form, as the axis in question were vibrating +back and forth. When the axis reached the target speed in the +opposing direction, the auto tuner change direction again. After +several of these changes, the average time delay between the 'peaks' +and 'valleys' of this movement graph is then used to calculate +proposed values for Pgain, Igain and Dgain, and insert them into the +HAL model to use by the pid controller. The auto tuned settings are +not great, but htye work a lot better than the values I had been able +to cook up on my own, at least for the horizontal X and Y axis. But I +had to use very small tune-effort values, as my motor +controllers error out if the voltage change too quickly. I've been +less lucky with the Z axis, which is moving a heavy object up and +down, and seem to confuse the algorithm. The Z axis movement became a +lot better when I introduced a bias value to counter the +gravitational drag, but I will have to work a lot more on the Z axis +PID values.
+ +Armed with this knowledge, it is time to look at how to do the +tuning. Lets say the HAL configuration in question load the PID +component for X, Y and Z like this:
+ ++ ++loadrt pid names=pid.x,pid.y,pid.z +
Armed with the new and improved at_pid component, the new line will +look like this:
+ ++ ++loadrt at_pid names=pid.x,pid.y,pid.z +
The rest of the HAL setup can stay the same. This work because the +components are referenced by name. If the component had used count=3 +instead, all use of pid.# had to be changed to at_pid.#.
+ +To start tuning the X axis, move the axis to the middle of its +range, to make sure it do not hit anything when it start moving back +and forth. Next, set the tune-effort to a low number in the +output range. I used 0.1 as my initial value. Next, assign 1 to the +tune-mode value. Note, this will disable the pid controlling +part and feed 0 to the output pin, which in my case initially caused a +lot of drift. In my case it proved to be a good idea with X and Y to +tune the motor driver to make sure 0 voltage stopped the motor +rotation. On the other hand, for the Z axis this proved to be a bad +idea, so it will depend on your setup. It might help to set the +bias value to a output value that reduce or eliminate the +axis drift. Finally, after setting tune-mode, set +tune-start to 1 to activate the auto tuning. If all go well, +your axis will vibrate for a few seconds and when it is done, new +values for Pgain, Igain and Dgain will be active. To test them, +change tune-mode back to 0. Note that this might cause the +machine to suddenly jerk as it bring the axis back to its commanded +position, which it might have drifted away from during tuning. To +summarize with some halcmd lines:
+ ++ ++setp pid.x.tune-effort 0.1 +setp pid.x.tune-mode 1 +setp pid.x.tune-start 1 +# wait for the tuning to complete +setp pid.x.tune-mode 0 +
After doing this task quite a few times while trying to figure out +how to properly tune the PID controllers on the machine in, I decided +to figure out if this process could be automated, and wrote a script +to do the entire tuning process from power on. The end result will +ensure the machine is powered on and ready to run, home all axis if it +is not already done, check that the extra tuning pins are available, +move the axis to its mid point, run the auto tuning and re-enable the +pid controller when it is done. It can be run several times. Check +out the +run-auto-pid-tuner +script on github if you want to learn how it is done.
+ +My hope is that this little adventure can inspire someone who know +more about motor PID controller tuning can implement even better +algorithms for automatic PID tuning in LinuxCNC, making life easier +for both me and all the others that want to use LinuxCNC but lack the +in depth knowledge needed to tune PID controllers well.
+ +As usual, if you use Bitcoin and want to show your support of my +activities, please send Bitcoin donations to my address +15oWEoG9dUPovwmUL9KWAnYRtNJEkP1u1b.
+