quad copter coding

So I have successfully built my own quadcopter, that is, the hardware. Now I need to program it. From the research I have done, it seems that I'll need to use a PID loop to stabilize the vehicle. My thoughts are having one PID loop for the x axis, one for the y axis, and one for the z (yaw) axis. These four PID loops would each produce a variable which I would cut in half, and assign the new number to each control group (for the x pid, the control group would be x +, and x-, for the y pid, it would be y+ and y-) now my problem comes in when I need to Test. I have no idea what a ball park range for the pid variables should look like. So could someone give me a rough estimate for them (I'll tweak them, of course) and perhaps a check on my coding concept so far

Thanks all!

Just read up on PID tuning. Most say to start with P=1, I=0, D=0. Tune P down until the system doesn’t oscillate. Then tune P up until it causes a constant oscillation. Then there are algorithms to calculate the three parameters based on P and the frequency of oscillation.

Build a spare set of hardware first.

…R

What do you mean by ossilacion?

Honorwalk: What do you mean by ossilacion?

Have a look here !

thanks. that makes sense. So for now, I am just changing the proportional variable. But when I throttle up (slowly), the copter will flip over. Im not sure how to fix this. Is it perhaps my method of applying the PID outputs? Im adding the x out, y out, z out, and throttle together. Is there a better way to do it?

Honorwalk: But when I throttle up (slowly), the copter will flip over.

It sounds like maybe you are applying the correction in the wrong direction. If the Output is applied in the wrong direction it will make the error worse, not better. What are you using for Setpoint, Input, and Output for each of your PWM's? What is your Output range set to?

that could be. I will check my coding, and double check my motor connections and get back to you. For now, My set points for x, y, and z are just 0. Eventually, I will change that with a controller, but I'm just trying to stabilize first. My input is from a gyroscope/accellorometer in degrees, and my out put is xOut of x, yOut for y, and zOut for z. For each motor, I have an if statement calculating weather the degree is a negative, and makes the Out variable negative or positive depending on the outcome. Then I add those three to a throttle variable that i change using the serial monitor. So I'm not just adding always. half the time, its adding a negative.

The output range is set from 50 (where the ESC's on the motors read 0) to 255, or 5v.

Your setpoint for z is 0, that means the drone facing north. If you start it in another direction, the regulator might overshoot well, explaining a brutal reaction of the drone. Check your connexions, the direction of the propellers, use simple test cases to check if they react the way they should (slowing down when expected, etc.).

Honorwalk:
For each motor, I have an if statement calculating weather the degree is a negative, and makes the Out variable negative or positive depending on the outcome.

Shouldn’t you let the PID decide that?
I’d write something like this:

#include <PID_v1.h>
#include <TinyGPS.h>
#define USING_PWM
enum MotorIndex {MOTOR_FL_CW, MOTOR_FR_CCW, MOTOR_BL_CCW, MOTOR_BR_CW};
void MotorSpeed(MotorIndex motor, int throttle);
#ifdef USING_PWM
const int THROTTLE_MIN = 0;
const int THROTTLE_MAX = 255;
const int MotorPins[4] = {3, 5, 6, 9};
#else  // USING_ESC
const int THROTTLE_MIN = 1000;
const int THROTTLE_MAX = 2000;
Servo MotorFL, MotorFR, MotorBL, MotorBR;
Servo *ESCs[MotorIndex] = {MotorFL, MotorFR, MotorBL, MotorBR};
const int ESCPins[4] = {4, 5, 6, 7};
#endif
// Set Pitch/Roll/Yaw limits to 10% of throttle range.
const double LIMIT_MIN = -0.1 * (THROTTLE_MAX - THROTTLE_MIN);
const double LIMIT_MAX = 0.1 * (THROTTLE_MAX - THROTTLE_MIN);
TinyGPS GPS;
const double Kp = 1.0, Ki = 0.0, Kd = 0.0;
double AltitudeSetpoint = 0;  // Meters above start point
double AltitudeInput, ThrottleOutput;
PID AltitudePID(&AltitudeSetpoint, &AltitudeInput, &ThrottleOutput, Kp, Ki, Kd, DIRECT);
double PitchSetpoint = 0, PitchInput, PitchOutput;
PID PitchPID(&PitchSetpoint, &PitchInput, &PitchOutput, Kp, Ki, Kd, DIRECT);
double RollSetpoint = 0, RollInput, RollOutput;
PID RollPID(&RollSetpoint, &RollInput, &RollOutput, Kp, Ki, Kd, DIRECT);
double YawSetpoint = 0, YawInput, YawOutput;
PID YawPID(&YawSetpoint, &YawInput, &YawOutput, Kp, Ki, Kd, DIRECT);
struct Waypoint {
  double altitude;
  unsigned long latitude;
  unsigned long longitude;
} StartPoint, Destination, CurrentLocation;
void setup() {
#ifndef USING_PWM
  for (int i = 0; i < 4; i++)
    ESCs[i].attach(ESCPins[i]);
#endif
  AltitudePID.SetOutputLimits(THROTTLE_MIN, THROTTLE_MAX);
  AltitudePID.SetMode(AUTOMATIC);
  PitchPID.SetOutputLimits(LIMIT_MIN, LIMIT_MAX);
  PitchPID.SetMode(AUTOMATIC);
  RollPID.SetOutputLimits(LIMIT_MIN, LIMIT_MAX);
  RollPID.SetMode(AUTOMATIC);
  YawPID.SetOutputLimits(LIMIT_MIN, LIMIT_MAX);
  YawPID.SetMode(AUTOMATIC);
//  StartPoint.altitude = Barometer.altitude();
//  StartPoint.latitude = GPS.location.();
//  StartPoint.longitude = GPS.GetLongitude();
}
void loop() {
//  AltitudeInput = Barometer.altitude() - StartPoint.altitude;
  AltitudePID.Compute();
//  PitchInput = Accelerometer.pitch();
  PitchPID.Compute();
//  RollInput = Accelerometer.roll();
  RollPID.Compute();
//  YawInput = Magnetometer.heading();
  YawPID.Compute();
  MotorSpeed(MOTOR_FL_CW , ThrottleOutput + PitchOutput - RollOutput + YawOutput);
  MotorSpeed(MOTOR_FR_CCW, ThrottleOutput + PitchOutput + RollOutput - YawOutput);
  MotorSpeed(MOTOR_BL_CCW , ThrottleOutput - PitchOutput - RollOutput - YawOutput);
  MotorSpeed(MOTOR_BR_CW, ThrottleOutput - PitchOutput + RollOutput + YawOutput);
}
void MotorSpeed(MotorIndex motor, int throttle) {
  throttle = constrain(throttle, THROTTLE_MIN, THROTTLE_MAX);
#ifdef USING_PWM
  // Using PWM pins
  analogWrite(MotorPins[motor], throttle);
#else
  // Using ESC control
  ESCs[motor]->writeMicroseconds(throttle);
#endif
}

I think he talked about the way the PID output is applied to the propeller. For example, let y be the pitch axis. You'd have a PID operating on the pitch angle, with an output Fy. In order to actually act on the pitch angle, this output should be applied with a + sign on the 2 front propellers and with a - sign on the 2 rear propeller to create a torque on this axis.

Sacha22: I think he talked about the way the PID output is applied to the propeller.

If the PID Output limits allow both negative and positive outputs there is no need to "have an if statement calculating whether the degree is a negative, and makes the Out variable negative or positive depending on the outcome." The PID should produce a negative Output if it wants to pitch down and a positive Output if it wants to pitch up.

From the description it sounds like the Output limits were set to produce positive values only (perhaps the default 0 to 255 limits) and he looked at the Setpoint and Input values to decide if the Output should be negative or positive. That is going to mess up the PID which MAY produce positive values even when it is pitching down, to keep from overshooting.

In my code I'm setting the limits to -/+10% of full throttle range (255 for PWM and 1000 for ESC).

yes, but for some reason, the PID does not want to work with negatives. as in it just returns a 0 when the input is a negative. So I created a code that uses the absolute value of the angle, calculates the PID, and assigns the value + or -, depending on if the original axis is + or -, and adds that value to each propeller, like Sacha stated.

johnwasser: If the PID Output limits allow both negative and positive outputs there is no need to "have an if statement calculating whether the degree is a negative, and makes the Out variable negative or positive depending on the outcome." The PID should produce a negative Output if it wants to pitch down and a positive Output if it wants to pitch up.

That was not my point. The PID output is not directly the throttle for the propellers since you don't have a PID per propeller but a PID per variable you want to control (pitch, roll, probably yaw and maybe rate of climb). For example if the pitch PID output is 10 (arbitrary unit), the positive sign means you want the front end of the drone to rise. In order to create a torque to achieve that, the two front propeller must turn faster and the two rear propellers must turn slower. You have only one PID output in that case, by you need to apply it on the propellers with a sign for each. It does not depend on the output value but on the drone hardware conception and on the axis definition and is thus set hard in code, once and for all.

Honorwalk: yes, but for some reason, the PID does not want to work with negatives. as in it just returns a 0 when the input is a negative. So I created a code that uses the absolute value of the angle, calculates the PID, and assigns the value + or -, depending on if the original axis is + or -, and adds that value to each propeller, like Sacha stated.

Your PID must be able to output negative values. If it doesn't, it might be a variable type problem (output is unsigned, for example). What PID do you use? A library or a personal code? If you use a library, I suggest you to look at the source code if you can. If you did the PID code, maybe let us have a look at it?

Honorwalk: for some reason, the PID does not want to work with negatives. as in it just returns a 0 when the input is a negative.

Have you tried PID.setOutputLimits(-25,25); in setup()? The default limits are 0 to 255 which is fine for PWM but not for every other use case.

Yes, that is how my program is working, Sacha. And I am using a pretty coded library. Changing the output variables may help. I will try that too. The library is found here. http://playground.arduino.cc/Code/PIDLibrary

As johnwasser said, you'll have to change the output limits since your regulators should be able to output negative value.

Alright, the pid seems to be all taken care of but, like always, a new problem Arises. I have made sure all of my pins are connected to the right places. And that the gyroscope is working just fine, but when I go to test it, the copter always ends up flipping over Quite violently. And the pid is affecting the right motors the right way, I have checked. But for some reason, it still wants to flip over. Anyone have any ideas why?

Is it possible to test it while connected to a computer? Or do you have a wireless module able to communicate with a computer? For this kind of application, it is often useful to record data (attitude angles, PID outputs and others if needed) each time the loop is executed. Then you can analyse data after an experiment and understand what's going on. The first question is: does the PID outputs first in the right direction and then becomes instable, or does it act the wrong direction from the beggining? From what you describe, it seems the second option is what happens...

If so, the command is applied backwards, but you say that you've checked that.

Which units are your input variables for the PID and what are the PID gains you're using?

I am controlling the device currently tethered by anumber extra long USB cable to my computer via the serial window. My variables are P=1.5 I=0 D=0. The input variables are the x, y, and z variables from the MPU6050. I then take the outputs from all three pid loops and add them to a variable I assign from my compute. 8 start with the variable at 1, and slowly make my way up to 30ish where it looks like it's almost ready to take off, and then at around 35 or 40, the quad flips over.