How to tune a PID controller

I'm testing Arduino's PID Library as a black box. I've mocked Arduino's functions (analogRead, millis, delay) and coded a small engine to simulate the vertical movement of a quadcopter. For simplicity I'm considering only 1 dimension, no wind, and no lateral movement A simulated altitude sensor is feeding the input of the PID controller, and the output controls the thrust of the engine. The quadcopter has a 2:1 thrust-to-weight ratio.

I'm trying to tune the Kp, Ki and Kd gains using any of the manual methods described in wikipedia .
Both the manual and the Ziegler-Nichols method require to set Ki and Kd to zero at first, and set Kp to a value where the output starts to oscillate.

The problem here is that when doing that, no matter what value I give to the proportional gain, the oscillation always becomes unstable, like this (not my image):

In the manual method, the wiki says:

increase the Kp until the output of the loop oscillates

What I understand from that sentence is that I should start with a really low value of Kp, then increase untill it oscillates (and I guess it would be an unstable oscillation). To begin with, I don't know how the non-oscillating case would be. But in my simulation it always oscillates (in an unstable way) no matter how low the gain is.

So I switched to Ziegler-Nichols, where according to the wiki :

The "P" (proportional) gain, Kp is then increased (from zero) until it reaches the ultimate gain Ku, at which the output of the control loop oscillates with a constant amplitude.

Here it is more clear that i should aim for a constant amplitude oscillation. But then again it doesn't matter what value I try, the oscillation is always unstable. Which makes sense to me, because its thanks to Kd that the PID controller "sees" the future, and with only the proportional term it only reacts when crossing the setpoint.

For the simulation, I started having the quadcopter take off from an altitude of 0 meters, but as the amplitude of the oscillation increases with time, it ends up ballooning and crashing in the floor. I tried to compensate for this by rising the setpoint higher so that there is more space to the ground, and launching it from this altitude, hoping the thing stabilized even with a really large amplitude, but the result is the same. I've also tested with really small setpoints (2-3 meters), and this produces lower initial amplitudes and a shorter period, but in the end it goes to the floor.

How should approach this? And BTW, is the PID an appropiate controller for quadcopters? What do the pros use in real life drones?

Are you sure you have initialized the PID with the proper direction of control either DIRECT or INDIRECT your specific loop requires?

A loop that always oscillate can be because you are forcing positive feedback action instead of negative feedback via the control direction directive.

retrolefty:
Are you sure you have initialized the PID with the proper direction of control either DIRECT or INDIRECT your specific loop requires?

A loop that always oscillate can be because you are forcing positive feedback action instead of negative feedback via the control direction directive.

I'm using DIRECT mode. I've verified that when the altitude goes over the setpoint, the throttle is decreased (often set to 0%). But as Kd is zero as well, the controller does not take into account the current trend, and the engine remains turned off until it crosses again the setpoint in free fall. Then it keeps falling at 100% throttle until it starts climbing again.

I think I might have misunderstood the tuning methods. What they say is to change Kp until the output oscillates (the output is the throttle in my case).

But in my case the output always oscillates because it is the response to an unstable oscillation (the quadcopter movement). The controller switches the engine on and off when it overshoots or falls below the setpoint altitude, and as the controller only reacts to the error and not the trend in the error (Kd is set to zero for tunning), the amplitude of the movement is always increasing.

So I don't know what should I observe. Every time I run the program with a different Kp, initial altitude or setpoint results in an unstable oscillating movement. And this in turn produces an oscillating output of the same frequency, and an amplitude limited because throttle must be in the range of 0 to 100%.

I can get a convergent movement if I set Kd to some value greater than zero, but that would be an arbitrary suboptimal value, and I want to understand and properly apply manual tunning.

(BTW is this the correct subforum or should I use the engines one?)

this one might be informative - PID Theory -

robtillaart:
this one might be informative - PID Theory -

Thats a good link, thanks man.

According to the example there, it looks like every Kp I've tested is still too high. I'll try again with ridiculously low values.

if you cannot get an oscillation, your control element might be too sensitive.

if you have a throttle on a gas engine, look to the throttle linkage. move that further from the axis of the butterfly shaft.
more control movement from your actuator will results in less movement on your butterfly. move it too far and you will not be able to get to one end or the other (cannot idle or cannot reach maximum)

I've modified the program to get rid of the sleep in the loop, and to enable millisecond precission in the millis mock function.

I've tried to get an oscillation to no avail. (Setpoint was 100m, don't know if this is too unrealistic). Started with smaller Kps close to zero, but the thing didn't even takeoff. Then about Kp=0.05 it was able to takeoff, but after a parabollic flight of about 20 seconds it came to the ground.

I observed that the vertical velocity when it was about to land was close to 0 m/s, and I thought that if I was able to get it to near 0 in the moment it makes ground contact, then I'd have achieved an oscillation, and since it was starting from 0 meters, it would be the stable oscillation required for Ziegler-Nichols. So I made a lot of tests giving values to Kp trying to minimize v:

Kp		V at y=0
-------------------------
0.0053   	-0.17
0.0052   	-0.12
0.0051   	-0.06
0.00505   	-0.03
0.00503   	-0.02
0.00502   	-0.01
0.005015   	-0.01
0.005013   	-0.01
0.005011   	-0.006
0.0050105   	-0.006
0.00501025  	-0.006
0.005010125 	-0.005
0.0050101125	-0.005	
0.0050100625	-0.006

At that time I didn't want to waste more time adjusting decimals, so I took Ku = 0.00501011125 as the ultimate gain and calculated the gains as per the "classic" Ziegler-Nichols:

Kp = 0.60*Ku = 0.00300606675
Ki = 2Kp/Tu = 0.000300606675
Kd = KpTu/8 = 0.007515166875

With those values, the simulated object cannot take off. So I just multiplied all the gains by a factor of 100, and got the PID holding altitude at the desired level. I thought that, more than the gains themselves, what was important was the proportion between them. I don't know whether this is true or not, but the calculated gains are better than the arbitrary ones I tried beffore attempting to tune the loop.

I'd like to use a PID controller in a real process rather than a simulated ideal helicopter than never tilts, but I can't find anything suitable for a project now (and real quadcopters are too complicated for the time I'm willing to invest).

Anyway, thanks everybody.

My vague understanding (No personal experience though) is that PID loops are too simplistic for quadcopters, at least by themselves. I've heard mention of using a Kalman filter on your inputs to help filter down your measurements. As to implementation, there are a number of posts in the arduino forums.

An easier use is controlling motor speed based on an encoder input.

mirith:
My vague understanding (No personal experience though) is that PID loops are too simplistic for quadcopters, at least by themselves.

Usually you'd use Kalman or equivalent to work out from the sensor inputs what the vehicle is doing, and a PID loop for each degree of freedom to work out the necessary control outputs.