I have a servo motor with quad optical encoder and I'm trying to control its position and velocity. By controlling both I meant that if I input that the motor should reach 90° at 200rpm then it should. How can I do that? I am using an Arduino Uno. Kindly share some code if possible.

Though I have implemented the PID, I don't think it is correct because I didn't implement the feedforward controller (because I have no idea what that is) and I have not been able to find suitable gains for PID. The gains I find for small steps (or say degree rotation) do not work out well for large steps and vice versa. I have also not used a limit for integral sum (because I don't how much it should be).

I am using a Pittman motor.

  • $\begingroup$ Under what circumstances are you trying to control your flight systems with a PID? For example: 1) to track an object and trail at a fixed distance, 2) to maintain a target path, 3) to control a planned ascent/descent? $\endgroup$ Jul 7 '15 at 19:07

There are different ways to approach this problem, but in a general sense you could try this:

You have a PID controller for velocity. It'll look something like this (untested code):

// PID Controller
struct PID
  float kP, kI, kD;
  float error, integral, derivative, previousError;
  float dT;
int computePID(PID* pid, float error) { pid->error = error; pid->integral += error*dT; pid->derivative = (error - pid->previousError)/dT; pid->previousError = error; return pid->kP*pid->error + pid->kI*pid->integral + pid->kD*pid->derivative; } //Somewhere else setArmMotor(computePID(&pid, some_error));

When you tune the constants right, you should be able to run your arm at a constant speed. However, this is dependent that you have a fairly high resolution encoder, if you don't you won't have much of a way to controlling how fast your motors are spinning.

Now to control position, this will depend on your application. The "easy" solution is to have another PID controller that controls position, where this new controller outputs a velocity instead of a motor power.

If you want a more "controlled" method of controlling velocity, you can use an acceleration profile. For instance: PID Velocity on an trapezoidal acceleration profile

The code for this will look something like this: float time = 0; float enterSpeed = 0; float maxSpeed = 10; float exitSpeed = 0; float speed = 0; // This is the variable that your PID's should correct to. float acceleration = 1.0; float distance = 100;
// Acceleration while (speed < maxSpeed) { speed = acceleration*time + enterSpeed; time+=1; delay(1); }
// Kinematic Equations to calculate how far we need to maintain top speed for float timeToStop = (exitSpeed-maxSpeed) / acceleration; float distanceToStop
= maxSpeed * timeToStop + 0.5 * acceleration * timeToStop * timeToStop;
// Maintain top velocity while (encodersPosition + distanceToStop > distance) { speed = maxSpeed; }
// Deceleration time = 0; while (speed > exitSpeed) { speed = maxSpeed - acceleration * time; time+=1; delay(1); }

I did a lot of that memory, correct me if I messed up something. There are a lot of edge cases in that profile that isn't checked for, but that's the basic idea.

  • 1
    $\begingroup$ I see a lot of people using derivativeError = error - previousError, which, of note first is that you have previousError - error, which means that the sign of your derivativeError is going to be flipped. The problem here is that this does NOT give the true derivative error; for that you need to divide by the sample time! derviativeError = (error - previousError)/dT. Likewise, for the integral error you need integralError = integralError + error * dT, as += is just an accumulation not an integration. $\endgroup$
    – Chuck
    Jul 13 '15 at 20:15
  • $\begingroup$ You could argue that dT just serves to scale the integral/derivative errors, but (1) that's only the case if your sample time is consistent, and (2) you can't use tuning methods like Ziegler-Nichols if you don't calculate the errors correctly. If you have any kind of logic in your controller that branches then you have variable sweep times in your code and thus a variable amount of time between error checks. The best way to avoid this is to call error checking on an interrupt, but you're still left with point (2) to deal with. $\endgroup$
    – Chuck
    Jul 13 '15 at 20:18
  • 1
    $\begingroup$ Good catch on the derivative! I was indeed assuming consistent sample times for the purpose of this example, but I edited my answer to include change in time. As for the integral, I'm not too sure I understand what you mean "+= is just an accumulation not an integration". In C atleast, integral += error*dt is equivalent to integral = integral + error*dt $\endgroup$
    – JMarple
    Jul 13 '15 at 21:06
  • $\begingroup$ Yeah, you're correct, if you do += error*dT, but I think initially you just had +=error. That dT is key! $\endgroup$
    – Chuck
    Jul 14 '15 at 1:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.