How can an Arduino-Quadcopter be stabilized? I have built an arduino quad-copter and I am using an MPU6050 to read the rotational position and velocity of the aircraft. The problem is that I cannot get the thing to stabilize. Watch this video here to get an idea of what I mean.

So as you can see from the footage (and you did watch it, right?), I'm not even getting steady oscillations, it's just random. What I don't understand is how people get their quad-copters to oscillate very quickly (maybe 10 times per second) and then trim up their PID gains to smooth it out, whereas if I raise my p-gain high enough for it to adjust that quickly, the quad-copter just goes berserk and tries to kill me.

And, speaking of p-gains, here's the PID I wrote:

float KrP = 0.002000;
float KrI = 0.000001;
float KrD = 0.200000;

void adjustRoll(float currentRoll, int newRoll) {
  if (thrust &lt;= 1200){
    inAutoRoll = false;
    NWPower = thrust;
    NEPower = thrust;        //PID not ready to turn on yet? Just set motor powers
    SWPower = thrust;        //equal to the throttle.
    SEPower = thrust;

    I_roll = 0;              // Reset the integral.
    return;                  // Don't do anything else
  }

  float newRate = 2 * (newRoll - currentRoll); // How fast should the drone try to
                                                                       // adjust, based on the error.

  if (newRate &gt; 50)newRate = 50;        // Don't adjust too fast!
  else if (newRate &lt; -50)newRate = -50;

  float offset = newRate - rollRate;

  I_roll += KrI * offset;

  if(!inAutoRoll) {        // Is the PID just now turning on? (Throttle requirement met).
    lastRollRateOffset = offset;  //Smooth transition.
    inAutoRoll = true;
  }


  float adjust = (KrP * offset) + (KrI * I_roll) + (KrD * (offset - lastRollRateOffset));

  NWPower += adjust;
  NEPower -= adjust;
  SWPower += adjust;
  SEPower -= adjust;

  if (NWPower &gt; maxOut) NWPower = maxOut; 
   else if (NWPower &lt; minOut) NWPower = minOut;

  if (NEPower &gt; maxOut) NEPower = maxOut;
   else if (NEPower &lt; minOut) NEPower = minOut;

  if (SWPower &gt; maxOut) SWPower = maxOut;
   else if (SWPower &lt; minOut) SWPower = minOut;

  if (SEPower &gt; maxOut) SEPower = maxOut;
   else if (SEPower &lt; minOut) SEPower = minOut;

  lastRollRateOffset = offset;
}


And in case you were wondering... yes, I do know about integral windup. The reason I have not dealt with that in my code is because the quadcopter is no where near stable, so I'm not worrying about perfection just yet. Also, I have my I-gain set to 0.000001, so it doesn't matter. The reason I have done this is because (as far as I know) a quadcopter can fly with only P and D, and with the way mine is behaving, I think a larger I-gain would only make things worse.

More detail about my PID: The PID corrects for the rotational velocity of the quad-copter, which, as I understand it, is the best way to control a quadcopter. The correction-rotation-rate is determined by multiplying the error (in degrees) by two. Why two? I dunno. I experimented with some other multipliers and two seems to be okay. Its also a prime number, so, yeah...

So anyways! What do I need to do to make the quad-copter stable? I have seen amazingly perfect quad-copters using the same arduino and the same MPU6050, but mine just tilts all over the place.

Some possible problems off the top of my head:


Bad algorithm for calculating the needed rotational velocity based on error.
Bad PID algorithm.
Bad PID gains.
I didn't balance my props, but seriously, would a couple of pieces of electrical tape stuck to the props really fix this? It doesn't seem like it would.


Thanks for any help you have to offer!
For stability and tuning you need to make the adjustment cycle time compatible with the physical system.  How fast does this adjustment code get called relative to how fast the physical system oscillates?  Without that under control, tuning the gains will be difficult, particularly for krI and krD.

For instance, if you are updating every millisecond, and it takes a 1/10th of a second to physically flop over from -90 to 0 at the current roll_thrust setting, there's only enough kP to adjust the roll_thrust kP*(RollDelta/2)*DT/dt=0.002*(90/2)*0.1/0.001=9  Is that a sane adjustment for a thrust/motorPower of 1200+?  If you sample fast enough to make it capable of a full-throttle (+/-800?) adjustment with this particular kP, you might need to sample 88x faster (1ms/(800/9)=0.01125ms).  From the other direction, I'd figure out how fast you can sample, then choose a krP that could get you a full-bias adjustment in reasonable time.

Maybe think of adjust as roll_thrust state variable measured in units of ESC_microseconds, and krP is the conversion factor from whatever error you are measuring.  Is it roll angle error? roll rate error? Absolute roll rate error?  It looks like this bit:

float newRate = 2 * (newRoll - currentRoll); // How fast should the drone try to
if (newRoll - currentRoll &lt; 0) {             // adjust, based on the error.
   newRate = 0 - newRate;
}


is essentially:

float newRate = 2 * abs(newRoll - currentRoll); 


which might make control discontinuous.  If currentRoll is a positional roll angle setpoint, it won't be able to tell if it is on one side or the other.

If you want a roll angle of -90deg to make full throttle one way and 90deg to full throttle the other, then krP should be on the order of 900us/90deg=10us/degree.   And krI=krD=0 to start with.

I'd code up separate PIDs for thrust, roll_thrust, pitch_thrust, yaw_thrust control variables and then sum them together to get the  {NE,NW,SE,SW}Power outputs.  Then you could monitor and control in {thrust,roll_angle,pitch_angle,yaw_rate} space and the coding will be more straightforward and interpretable.