# PID package strange behavior

Hello everyone,

I'm working with a self made differential drive robot prototype, using ROS Kinetic. The rover is equipped with a Raspberry Pi 3 where ROS runs, an Arduino 2 to read from the encoders (hall sensor) and to control two DC motors. The bot works fine with navigation, I managed to tune a smooth pid control using ros pid package but the problem that I've encountered is that the pid starts losing the settings over time; if in the first minutes it behave smoothly and with almost zero overshooting (except some spike caused by the noisy and cheap encoders), after some time it starts to detune alone without changing any parameters. Here are three pics attached to understand better (black line is the set point for both the motors, red for the left motor and cyan for the right one):

minute [0.0 to 3.0] minute [18.0 to 20.0] minute [33.0 to 35.0]

After turning off the raspberry everything works fine again but degenerate again over time. Here is the launch of the pid:

<launch>
<node name="controller" pkg="pid" type="controller" ns="left_wheel" output="screen" >
<param name="node_name" value="left_wheel_pid" />
<param name="Kp" value="0.27" />
<param name="Ki" value="2.2" />
<param name="Kd" value="0.01" />
<param name="upper_limit" value="70" />
<param name="lower_limit" value="-70" />
<param name="windup_limit" value="100" />
<param name="max_loop_frequency" value="50.0" />
<param name="min_loop_frequency" value="50.0" />
<param name="setpoint_timeout" value="0.1" />
<remap from="state" to="/rover1/vel_measured_left" />
</node>

<node name="controller" pkg="pid" type="controller" ns="right_wheel" output="screen" >
<param name="node_name" value="right_wheel_pid" />
<param name="Kp" value="0.27" />
<param name="Ki" value="2.2" />
<param name="Kd" value="0.01" />
<param name="upper_limit" value="70" />
<param name="lower_limit" value="-70" />
<param name="windup_limit" value="100" />
<param name="max_loop_frequency" value="50.0" />
<param name="min_loop_frequency" value="50.0" />
<param name="setpoint_timeout" value="0.1" />
<remap from="state" to="/rover1/vel_measured_right" />
</node>
</launch>


First I was thinking about some misunderstanding related to the unit of the windup limit (actually I still don't understand if it refers to time or samples), but changing the parameters hasn't varied the behavior. Instead, using 2 different values for the setpoint_timeout parameter of each motor, something changes so maybe this could be the core. I tried to set also setpoint_timeout=0 but obviously the pid doesn't work.

Are there any solution or I just have to use a brutal method like auto kill and reopen the pid node every X minutes?

Thank you for your time, any suggestions or advice is very well accepted!

EDIT 1: So as @davekroezen suggested I checked the behavior of the control_effort, as you can see in the image it starts resonating with the vel_measured around the setpoint. That is actually ...

edit retag close merge delete

1

What is the control_effort produced by the controller? You now focus on the controller, but it could actually be the input to the controller (vel_measured) which is to blame here. looking at the control effort you can hopefully see if the controller or the state starts to deviate first and how the controller responds.

( 2020-01-10 08:47:28 -0600 )edit

As for windup, that is basically a limit for the integrator part of the PID controller

( 2020-01-10 08:58:06 -0600 )edit
1

it's definitely a weird problem. My first guess would be the Raspberry Pi slowing down due to over heating and the control updates becoming further apart. Investigate that. Anything that takes 30 minutes to happen should make you consider temp.

( 2020-01-11 11:17:05 -0600 )edit

@davekroezen@billy thank you for you suggestions, I updated the question

( 2020-01-13 03:43:49 -0600 )edit
1

Maybe check the CPU load with top (or whatever the RPi equivalent is). Displaying the plots puts a very heavy load on the CPU, maybe that's the cause.

To check if the PID package is the cause, you could install the PID package on a regular PC and run the demo for 30 minutes or more. That would make me feel more confident.

( 2020-01-13 08:52:14 -0600 )edit

Sort by » oldest newest most voted

After checking the CPU usage, which is stable to 46% with 2 vnc opened (the graph is plotted by the ros master, which is a regular pc) I tried changing PID parameters as @AndyZe suggested. Well I solved everything setting the Kd term from 0.01 to 0 and leaving Kp and Ki terms same as before. All the spikes in the control_effort, which I thought were generated by the encoder itself, are now disappeared and the pid works smoothly and stably over time.

Then I tried to modify the parameters to fine tune the PID as @AndyZe suggested, but I found no local minimum with a Kp higher than the Ki, the motors keep oscillating around the setpoint, with very small convergence. I changed the windup_limit too, but lowering it to 10 create a phase shift between the desired and the commanded velocity, which doesn't stabilize on the desired value from a certain point. The new setting which can promptly follow the setpoint are: Kp = 0.4, Ki = 5.5, Kd = 0.0.

I think the reason behind this strange tuning parameters should be sought in this specific hardware configuration: the combination of Arduino poor time handling and a low step encoder introduces fast varying measurements due to the large quantization step; the faster measurements are taken the greater quantization step becomes. Using a filter to take an average of the speed introduces delay which again lead to oscillation and instability. In the end, the integral component of the PID uses values from past measurements to correct the error, acting like a filter which introduces lower delay and which lower the gap between measurements.

more

Your Ki term is very large with respect to Kp. Usually Kp should be larger than Ki and Kd.The wikipedia PID page has a good section on tuning PID controllers.

I think windup_limit is unreasonably large, too. You want to make sure windup_limit is in the same range as max(error) so it doesn't completely dominate the other terms. From your example plots, I'd try something like windup_limit = 10 (that's about the largest error when the controller is working well).

more