DC Motor PID controller with inconsistent speed

Hello,

I am trying to position control a DC motor using PID controller using Arduino and a L298N driver. Motor connections is complete and I can send PWM values from 0 to 250 to the motor and it is working fine. The motor does not spin when the PWM value is smaller that around 130 which is expected. I wrote my PID code by calculating PID value and adding it to 130 to generate voltage when there is any position errors. Here is the issue:

If the motor is at complete stop, it starts spinning only with PWM values larger than 130 (it is at stop at 130). However, while the motor is spinning let's say with PWM value of 150, if I switch the PWM value from 150 to 60 it still continue spinning. In other words, while it is spinning, commanding lower PWM values (as low as 50 or 60) can keep it rotating. This makes sense to some extend because of friction and inertia.

Here is my question:

For PID to work, it should generate PWM values of around 130 when there is no position error. However, in real application when the motor spinning toward the target position, it won't stop by commanding the PWM values around 130 even when the error is very small. So, it keeps spinning and this makes the motor to oscillate back and forth around the target position. If I lower the PWM value corresponding to the stop position of the motor, let's say to 50 or 60 (instead of 130), then the issue is that the motor won't start rotating until the error is very big and the generated PWM value is more than 130.

Is there a solution for this? I appreciate your opinion.

Thank you!

PID relies on having proportional control, which it appears you don’t.

That suggests that perhaps you should write a controller of your own that understands that increases in speed need to be handled differently to decreases.

Alternatively, perhaps you can keep the PID but keep track of whether it’s asking for more speed or less and when decreasing, add a smaller number to the PID output to get your PWM.

b0x4it:
If the motor is at complete stop, it starts spinning only with PWM values larger than 130 (it is at stop at 130). However, while the motor is spinning let's say with PWM value of 150, if I switch the PWM value from 150 to 60 it still continue spinning.

I think you need to treat start-from-stationary as a special case. IIRC correctly I did this a while back by steadily increasing the PWM (from 0) until the motor starts rotating and then handing over to PID control.

Also get the motor started and figure out what is the lowest PWM value at which it will work and use that as the minimum that your PID calculations produce.

ALWAYS set the PWM to 0 when you want to stop the motor. Otherwise there is current flowing and causing the motor coils to heat up.

...R

wildbill:
PID relies on having proportional control, which it appears you don't.

That suggests that perhaps you should write a controller of your own that understands that increases in speed need to be handled differently to decreases.

Alternatively, perhaps you can keep the PID but keep track of whether it's asking for more speed or less and when decreasing, add a smaller number to the PID output to get your PWM.

Can you elaborate? I am using proportional control as well.
Regarding your comment about adjusting PID output: I have been trying this for a while now. I tries different approaches including applying a kick-start to the motor when it is spinning from stop position. It works to some extend, but it produces a jagged motion. Since I am trying to position-control the motor, I am dealing for stop positions at the beginning of the motions and also at the end of the motion.
To add to the issue, the amount of PWM value to keep the motor spinning varies depending on the angle. For example, on the first quadrant, it needs less PWM to rotate compared to the second quadrant. This also makes sense in real world because there might be different torques present in different locations.
The question is whether what I am observing is a known issue with position control of DC motors, OR this is just happening to me because I am not using high-end DC motors? (I am using LEGO Power Functions XL-Motor.)

Robin2:
I think you need to treat start-from-stationary as a special case. IIRC correctly I did this a while back by steadily increasing the PWM (from 0) until the motor starts rotating and then handing over to PID control.

Also get the motor started and figure out what is the lowest PWM value at which it will work and use that as the minimum that your PID calculations produce.

ALWAYS set the PWM to 0 when you want to stop the motor. Otherwise there is current flowing and causing the motor coils to heat up.

...R

That is exactly what I have done. This is how I ended up with the PWM value of 130. So, I calculate PID and then add it to 130 (PID + 130 is what being commanded to the motor as PWM value). This cause the motor to start spinning. But when it comes to reaching the target, the motor won't stop even when the error is zero (this means PID value is zero and PWM value is 130). Do you have any thoughts?

b0x4it:
That is exactly what I have done. This is how I ended up with the PWM value of 130. So, I calculate PID and then add it to 130 (PID + 130 is what being commanded to the motor as PWM value). This cause the motor to start spinning.

That's not what I'm suggesting.

If the motor is stopped then get it started without using PID. Only bring PID into play once the motor is moving. And. as you found out for yourself, 130 may be needed to get the motor started but it is much more than the minimum needed to keep the motor moving so the minimum value from PID must be less than 130.

...R

Robin2:
That's not what I'm suggesting.

If the motor is stopped then get it started without using PID. Only bring PID into play once the motor is moving. And. as you found out for yourself, 130 may be needed to get the motor started but it is much more than the minimum needed to keep the motor moving so the minimum value from PID must be less than 130.

...R

Thanks for your suggestion. I have been working on this and it still does not work. I believe, the reason is that even 130 is not consistent. At some positions, the motor starts spinning a lot easier that other location. It might be because of friction or even weight of the wires. Is this a typical issue with DC motors? Do you have any other suggestion?

If the motor is stopped, give it maximum power briefly to get it going, then use your add 130 method. When you want the motor stopped set power to zero.

b0x4it:
Thanks for your suggestion. I have been working on this and it still does not work. I believe, the reason is that even 130 is not consistent.

Read, again, what I said in Reply #2 "I did this a while back by steadily increasing the PWM (from 0) until the motor starts rotating and then handing over to PID control"

That does not mention 130 (or any other number above 0).

...R

Robin2:
Read, again, what I said in Reply #2 "I did this a while back by steadily increasing the PWM (from 0) until the motor starts rotating and then handing over to PID control"

That does not mention 130 (or any other number above 0).

...R

Right, I have been trying to implement this. There might be two cases:

  1. Going from angle A to angle B with relatively large difference: This approach works for this case if the steady increase is performed anywhere along the way that the motor is stopped. Like I said, the motion doesn't seem to be consistent. The motor might start with PWM value of 130 in one position, but might need more in other position. The same is true on the minimum side, meaning that the motor might keep rotating with PWM value of 50 from one angle, but stopped at another angle which might need PWM of 55. So, the steady increase might need to be repeated along the way as well. Which makes the motion not smooth.
  2. Tracking motion when small changes are being commanded to the motor. If the PWM value increases steadily, when it reaches the value that can overcome the friction, it gets up to speed quickly and will pass the target position (because the displacement is small). This again cause jagged motion and osculation.
    Any opinion is appreciated.

It sounds like your project would be more suited to a stepper motor if you need small positional updates. What are you actually doing?

I'm inclined to agree with @wildbill. And with a stepper motor you probably won't need PID because you set the speed by adjusting the interval between step pulses.

...R
Stepper Motor Basics
Simple Stepper Code

Thanks for your suggestions. I am making a 1-DOF arm robot that is being controlled by a potentiometer. I am new to Arduino and wanted to start with a simple project which turned out to be more complicated.

I have an MPU-6050 module that is mounted on a very simple and lightweight LEGO arm. This arm is mounted on the LEGO motor shaft. When the motor spins, it spins the arm and I get the exact angle using MPU-6050.

This project was for me to learn the basics of using Arduino. But, my main objective is to make a wheeled inverted pendulum. It will have similar concept that an MPU-6050 is mounted on the body which is on two wheels controlled by DC motors. The angle of the body (obtained from MPU-6050) would be the input to the PID controller and it will adjust the PWM value of the motor to keep the robot balanced vertically.

I agree that step motor and servo motors would be easier what I am with the simple robot arm. But, my objective is to do this using DC motor so that I can continue with next project that requires DC motor.

The behavior that I am observing with DC motor makes sense to some extend due to friction and inertia. So, I am curious to hear from others whether they have observed similar behavior and how they resolved it. A simple PID doesn't seem to be the answer.

Again., appreciate your thoughts and ideas.

EDIT: regarding the incremental displacement commands: this would be similar to a wheeled inverted pendulum that is balanced in vertical position and you command it to move forward slowly while it is keeping its upright balance.

b0x4it:
But, my objective is to do this using DC motor so that I can continue with next project that requires DC motor.

Then going back to Point-2 of your Reply #9 perhaps the solution is to have a bigger gear reduction between the motor and the output shaft so that more turns of the motor are needed.

What gear ratio do you have at the moment?

...R

PS ... or a much better quality (and more expensive) DC motor that behaves more consistently.

Robin2:
Then going back to Point-2 of your Reply #9 perhaps the solution is to have a bigger gear reduction between the motor and the output shaft so that more turns of the motor are needed.

What gear ratio do you have at the moment?

...R

PS ... or a much better quality (and more expensive) DC motor that behaves more consistently.

I am using LEGO® Power Functions XL-Motor 8882. It is geared, but I couldn't find the gear ratio.
I am going to use these geared DC motors with 1:48 gear ration:

Do you know if they are any better for this purpose?

We know nothing about your code or the mechanical system. So, the code would be a great place to start. Please post your code using code tags.

Why? Because the PID calculation could be at fault or things like the integral is either much too slow, set to zero or you’ve got derivative in the mix when you don’t need it.

I think you need to change to synchronous rectification mode for the motor rather than
the standard decay mode.

In synchronous rectification the motor is always powered/enabled, but the direction is PWM'd
instead. This gives much more linear motor-speed response and provides active braking for free.

Also the I term in a PID controller automatically deals with any offset needed, although you
usually don't want too much of it (more chance of oscillations).

You have a choice of position feedback or velocity feedback, each has advantages and disadvantages.

WattsThat:
We know nothing about your code or the mechanical system. So, the code would be a great place to start. Please post your code using code tags.

Why? Because the PID calculation could be at fault or things like the integral is either much too slow, set to zero or you’ve got derivative in the mix when you don’t need it.

Fair enough! Here is my crazy simple setup:

I am basically using the code from here:

I removed anything related to encoder and change them to read from MPU-6050. I also played with PID gains. Here is the code:

#include <PID_v1.h>

// MPU-6050
double AngleX, AngleY;

// motor
#define mMotor1pin1 0
#define mMotor1pin2 1
#define mMotor1int 2

double kp = 4 , ki = .1 , kd = 0;
double input = 0, output = 0, setpoint = 0, outP = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);

void setup() {
  pinMode(mMotor1pin1, OUTPUT);
  pinMode(mMotor1pin2, OUTPUT);
  pinMode(mMotor1int, OUTPUT);
  Serial.begin(9600); //initialize serial comunication

  mGyro_setup();
  mGyro_loop();

  myPID.SetMode(AUTOMATIC);   //set PID in Auto mode
  myPID.SetSampleTime(1);  // refresh rate of PID controller
  myPID.SetOutputLimits(-150, 150); // this is the MAX PWM value to move motor, here change in value reflect change in speed of motor.
}

void loop() {

  mGyro_loop();
  setpoint = 110;            // target angle
  input = AngleY ;           // data from MPU-6050

  myPID.Compute();                 // calculate new output

  // motor start spinning at around PWM value of 110
  if (output > 0) {                         
    outP = output + 110;
  }
  else {
    outP = output - 110;
  }

  // avoid collision if the arm is going out of workspace
  if (AngleY < 40 || AngleY > 140)
    outP = 0;

  pwmOut(outP);
}


void pwmOut(int out) {

  if (out > 0) {                         // if REV > encoderValue motor move in forward direction.
    analogWrite(mMotor1int, out);         // Enabling motor enable pin to reach the desire angle
    forward();                           // calling motor to move forward
  }
  else {
    analogWrite(mMotor1int, abs(out));          // if REV < encoderValue motor move in forward direction.
    reverse();                            // calling motor to move reverse
  }
}


void forward () {
  digitalWrite(mMotor1pin1, LOW);
  digitalWrite(mMotor1pin2, HIGH);

}

void reverse () {
  digitalWrite(mMotor1pin1, HIGH);
  digitalWrite(mMotor1pin2, LOW);

}
void finish () {
  digitalWrite(mMotor1pin1, LOW);
  digitalWrite(mMotor1pin2, LOW);

}

The original code does not have the section I added to add and subtract PWM value of 110. This is what I don’t understand. It seems my motor is similar to the one used in the original system. However, he set his PWM values to be within -125 and 125 and in the youtube video the motor moves very slowly and smoothly. But in my case, the motor doesn’t even start spinning until the PWM value gets up to around 150 to 180. And when it starts spinning, it spins fast. Question: what am I doing wrong?
I am using 4XAA new batteries for power supply.

MarkT:
I think you need to change to synchronous rectification mode for the motor rather than
the standard decay mode.

In synchronous rectification the motor is always powered/enabled, but the direction is PWM'd
instead. This gives much more linear motor-speed response and provides active braking for free.

Also the I term in a PID controller automatically deals with any offset needed, although you
usually don't want too much of it (more chance of oscillations).

You have a choice of position feedback or velocity feedback, each has advantages and disadvantages.

This seems very important. When you say "mode", is this something I can change in the setting/configuration? If, yes, how?

I have been working on this and been trying to have a bit of kick whenever PID is commanding movement but the motor does not move or does not have enough movements. The code seems to be dosing what it is intended. I have captured the results in this excel sheet:

Parameter Kick shows when the motor needs some help and so the "output" that is PWM value is 140. This works and the motor keeps spinning and decreasing the error (between Input and setpoint) until it reaches the setpoint. The issue is it keep spinning and passes the setpoint. The PID generate negative PWMs, but the generated input is not big enough to keep the motor from spinning.

Increase the P gains results in even more acceleration and therefore, it needs even more negative PWM after passing the setpoint.

I appreciate any thoughts.