The glaring issue I see at the moment is that you are forcing polarity on the I and D terms. In general, you are using a lot of sign checks, sign assignments, and conditional programming.
None of that belongs in a PID controller. The entire controller should look like:
pError = Input - Output;
iError = iError + pError*dt;
dError = (pError - previousError);
previousError = pError;
ControlSignal = kp*pError + ki*iError + kd*dError;
That's it. No need for checks, assignments, etc. See my related answer on splitting the control signal as opposed to control each motor pair individually (which is what I believe your goal is with the if elseif code).
Imagine a PID controller as three people watching your vehicle, all trying to give advice based on their "experience" (mode). Each "person" will give you one of three "statements" So you have:
Mr. Proportional - This person looks at where the output is and compares it to what you asked for. The statements given by this person are:
- There is a large difference between what you want and what you have - take a big action.
- There is a small difference between what you want and what you have - take a small action.
- What you have is what you asked for - take no action.
Mr. Integral - This person looks at the same error value Mr. Proportional does, but compares it to how long it's been that way. The statements given by this person are:
- You have chronic/acute error (small error for a long time or big error for a small time) - take a big action.
- You have mild error (small error for a short time) - take a small action.
- Your error history is neutral (time * positive error is equal to time * negative error) - take no action.
Mr. Derivative - This person looks at the same error value Mr. Proportional does, but compares it to how it's changing. The statements given by this person are:
- Your error is getting bigger - take a bigger action.
- Your error is getting smaller - take a negative action.
- Your error is not changing - take no action.
It's important to note statement 2 of Mr. Derivative up there - imagine you're driving a car behind someone who is already at a stop. As you get closer to them (error is getting smaller), not only do you want to let off the gas, but you also want to brake! Derivative action is what gives you "braking" - no other term (P or I) gives you negative action until you're past the setpoint. Derivative is the only term that tells you to slow down because you're getting close.
Another way to help understand these terms is to understand physically what they mean. Say your reference is a speed. This means:
- Proportional error compares your speed to your target speed. Want to be going 60 and you're going 55? Speed up. Going 65? Slow down. This is easy.
- Integral error compares the integral of target speed to the integral of actual speed. This means that it's comparing a target position to your actual position. Were you supposed to be in the city already? Then you need to speed up. Were you supposed to be in the city and you're still at your house? SPEED THE ENTIRE WAY.
- Derivative error compares the derivative of the difference between target and actual speeds. Is the person in front of you pulling away? Speed up! Is the person in front of you pushing their brakes? Slow down! As I mentioned above, if your target is to be immediately behind the person in front of you, then proportional and integral will both "tell" you to speed up. Derivative is the only "person" to tell you you're going to rear-end them if you don't start braking.
SO, what happens when you force signs on the error terms?
Let's suppose you're in a car, trying to follow a friend to a restaurant. Here the friend's speed represents the speed reference and your speed represents your speed feedback. Speed limit on the road is 35mph (55kph). Here's what happens:
- Your friend begins to move.
- You are still stationary, so the following errors happen:
- Proportional error is positive (you want to go 35 and are actually going 0).
- Integral error is a little positive (your friend is farther from you).
- Derivative error is large and positive (your friend is quickly pulling away from you).
- You force signs: force integral error to be positive (already is) and derivative error to be negative.
- This means that derivative error is "telling" you that the car is getting farther away from you, but you invert that and assume that derivative error meant to say that you are getting closer to your friend. This is wrong.
- Actions: Proportional - press the gas a moderate amount. Integral - Press the gas pedal a little bit. Derivative - Should be press the gas pedal a lot, but you inverted it, so instead you press the brake a lot.
- Eventually your friend gets far enough away that the proportional and integral error becomes large enough that it overrides your (incorrectly inverted) derivative term. At this point:
- Proportional error is large (still going zero and want to go 35).
- Integral error is very large (your friend is very, very far in front of you).
- Derivative term is still large (friend is still actively getting farther from you), but you are still forcing it to be negative.
- Actions: Proportional - press the gas a lot. Integral - Floor the gas pedal. Derivative - Should be push the gas pedal, but you inverted it, so instead you press the brake.
- After some time, you get to 34.999 mph. Proportional error is still (slightly) positive because you want to go 35 and you're actually at 34.999, so proportional error is 0.001
- Proportional error is barely positive (still going slower than 35mph)
- Integral error is at its largest (you are farthest from your friend thus far because your friend has been going 35 the whole time)
- Derivative error is roughly zero (you're almost the same speed your friend is, so now the proportional error stabilizes)
- You force signs: Force integral error to be positive (already is) and derivative error to be negative (it's almost zero, so negligible change).
- Action: Proportional - No action because you're almost at 35mph. Integral - You are now really far from your friend, so you floor the gas. Derivative - No action because the proportional error is almost stable.
- Now, because you were flooring the gas, you pass 35mph and hit 35.01mph. Now, your proportional error becomes negative (want 35, are going 35.01, so error is -0.01).
- Proportional error is almost zero (going just over the speed limit)
- Integral error is very large and still very positive because you are actually very far behind your friend.
- Derivative error is almost zero (because proportional error is still almost zero).
- You force signs: Force derivative error to be positive - little change because it's almost zero anyways. The problem here comes when you force integral error to be negative - it was very large and very positive! Now you're forcing it to be negative. This means Mr. Integral was telling you you're very far behind your friend, but you invert that and assume integral error meant to say that you are very far in front of your friend.
- Action: Proportional - No action because you're going 35mph. Integral - You are very far behind your friend and should floor the gas pedal, but you inverted that and now think that you are very far ahead of your friend, so you stomp the brake pedal instead! Derivative - No action because proportional error is pretty stable.
At this point you hit a loop - you slam on brakes when your speed just passes 35mph because you invert integral error, then you floor the gas when you fall below 35mph because you un-invert integral error. This should (violently!) jerk the car (aircraft) around and prevent you from ever eliminating any steady state error.
Worse, I'm not sure how this would behave once you get to the set position, but I think the constant sign flipping might prevent you from stopping anywhere close to where you wanted (if it ever stabilizes at all).