I have a car chassis with L298N motor controller . I am using an LM393 speed sensor module to sense the speed. Using the standard arduino pid library. The output from PID is directly sent as power (PWM) to the left and right motor wheels.
The value of the output reaches till 255 first , probably full power to get to the target speed , then there's an overshoot .
#include <PID_v1.h>
int sensor = 3;
int myPins[6] = {5, 4, 7, 8, 9, 6};
unsigned long start_time = 0;
unsigned long end_time = 0;
int steps=0;
float steps_old=0;
float temp=0;
float rps=0;
int sensorRpm=0;
int targetRpm=0;
const int buffersize = 10;
int buffer[buffersize] ;
int index;
//PID variables
double Setpoint, Input, Output;
double Kp=5;
double Ki=0;
double Kd=0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
void setup()
{
Serial.begin(9600);
Serial.begin(9600);
pinMode(sensor,INPUT_PULLUP);
for (int i = 0; i < 6; i++) {
pinMode(myPins[i], OUTPUT);
}
//turn the PID on
myPID.SetSampleTime(2);
myPID.SetOutputLimits(0, 255); // Standard PWM Range
myPID.SetMode(AUTOMATIC);
}
void loop()
{
int sensorspeed;
start_time=millis();
end_time=start_time+1000;
while(millis()<end_time)
{
if(digitalRead(sensor))
{
steps=steps+1;
while(digitalRead(sensor));
}
}
temp=steps-steps_old;
steps_old=steps;
sensorspeed = average();
rps=(sensorspeed/20); // no of slots in the encoder wheel
sensorRpm=rps * 60;
//update average buffer
buffer[index]=temp;
index++;
if(index>=buffersize)
{
index=0;
}
//PID Control
//target RPM
targetRpm = 660;
Setpoint = targetRpm;
Input = sensorRpm;
myPID.Compute();
Serial.print("Output=");
Serial.println(Output);
Serial.print("targetRpm:");
Serial.print(targetRpm);
Serial.print("\t");
Serial.print("sensorRpm:");
Serial.println(sensorRpm);
if(Output>0)
{
moveRobot(Output,Output);
}
}
void moveRobot(int leftSpeed, int rightSpeed)
{
if (leftSpeed >= 0) {
digitalWrite(myPins[1], 0);
digitalWrite(myPins[2], 1);
}
else {
digitalWrite(myPins[1], 1);
digitalWrite(myPins[2], 0);
}
if (rightSpeed >= 0) {
digitalWrite(myPins[3], 0);
digitalWrite(myPins[4], 1);
}
else {
digitalWrite(myPins[3], 1);
digitalWrite(myPins[4], 0);
}
analogWrite(myPins[0], abs(leftSpeed));
analogWrite(myPins[5], abs(rightSpeed));
}
//sensor reading averaging function
int average()
{
long sum = 0;
int i;
int avg;
for(i=0;i<buffersize;i++)
{
sum += buffer[i] ;
}
avg = (sum/buffersize);
return avg;
}
What type of power supply are you using? Also I would suggest you use a motor driver with MOSFET outputs, the L298 has bipolar outputs which will give you about 3 volts less then the power supply.
This is the o/p for kp=0.2. The output sort of oscillates. The behavior is the same for different Kp values , only the range of output changes , making me wonder if there's something wrong with how I am giving the output directly to PWM.
Keep going down. Kp is the constant that changes units of input error into units of the output value, so with Kp=1, and analogWrite(pin,Output), it means outputPWMcount = 1*Kp = 1PWMCount/RPMerror.
If you had a potentiometer attached to A0 and ran something like
loop(){
int potVal = analogRead(A0);
int motorPWM_0_255 = map(potVal,0,1023,0,255);
analogWrite(MyPins[1],motorPWM);
Serial.print("Pot:");
Serial.print(potVal);
Serial.print(" MotorPWM:");
Serial.println(motorPWM_0_255);
delay(250);
}
What sort of PWM value would you need to get 600RPM out of your motor?
Also, is the oscillation synchronized with this second-long loop?
Unless I'm reading that code wrong, that code will never work. You are tying up the CPU for one full second each time you take a speed reading. During that time, you are NOT updating the PID. Then you update the PID. The PID is updating once per second, NOT every 2 mSec as you set in the setSampleTime call. You CANNOT block the processor while the PID is running. myPID.Compute needs to be called, at an absolute minimum, every 2 mSec. ALWAYS.
And, even if you make the speed measurement non-blocking it still will not work properly unless the measurement update rate is at least nearly as fast as the PID update rate. Ideally, both will update at the same rate.
The sampling/adjustment time should depend on the "time constant" of the system. If the speed can't
physically change significantly in 2ms, making calculations or adjustments at that rate could amplify noise in the system. As a rule, setting the sampling/compute/adjustment time at 1/10 of the system time constant is a good start.
The PID_v1 code takes into account some level of slop, but for the time-based parameters, it acts as if the exact chosen sampling period has occurred, which would slow down the integral contribution, and amplify the derivative contribution.