DC Motor Speed Control Using PID and IR Sensor as feedback

Hello Everyone,

I am newbie to Arduino coding and i been trying to do project work. As the title says i am trying to control the DC motor speed using PID and IR sensor as a feedback system. Currently the coding can be uploaded but the reading in serial monitor is not stable and the system is not working properly. I am using LM298N to control the motor speed. I attach my coding together with reading i am getting. Please help me resolve the issue.

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <PID_v1.h>


int sensorvalue;
int state1 = HIGH;
int state2;
float rps;
float rpm;
long prevMillis = 0;
long interval = 200;
long currentTime;
long prevTime = 1;
long diffTime;
int sensorthreshold = 30;

 int IN1 = 7; // DC motor pin 
 int IN2 = 8; // DC motor pin 
 int ENA = 9; // PWM DC motor pin 
 
double Setpoint, Input, Output;

double Kp=2, Ki=5, Kd=1;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// this value indicates the limit reading between dark and light,
// it has to be tested as it may change acording to the 
// distance the leds are placed.
// to see what number is good, check the sensorvalue variable value
// as printed out in the serial monitor

void setup()
{
  Serial.begin(9600);
 
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);
  pinMode (ENA, OUTPUT);

  Input = analogRead(rpm);
  Setpoint = 100;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

}

void loop()
{
   analogWrite(ENA,150);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2,LOW);
  
  sensorvalue = analogRead(0); // read from pin 0
  if(sensorvalue < sensorthreshold)
    state1 = HIGH;
   else
    state1 = LOW;
   
   if(state2!=state1){      //counts when the state change, thats from (dark to light) or 
                            //from (light to dark), remember that IR light is invisible to us.
     if (state2>state1){
       currentTime = micros();   // Get the arduino time in microseconds
       diffTime = currentTime - prevTime;  // calculate the time diff from the last meet-up
       rps = 1000000/diffTime;   // calculate how many rev per second, good to know
       rpm = 60000000/diffTime;  // calculate how many rev per minute
       
       unsigned long currentMillis = millis();
       
       // print to serial at every interval - defined at the variables declaration
       if(currentMillis - prevMillis > interval){ // see if now already an interval long
       prevMillis = currentMillis;       
       Serial.print(rps); Serial.print(" rps  "); Serial.print(rpm); Serial.println(" rpm");
       }
       
       prevTime = currentTime;
     }
     state2 = state1;
   }
{
  Input = analogRead(rpm);
  myPID.Compute();
  analogWrite(9, Output);
}
 /* 
 //only for testing to determine the sensorthreshold value
 delay(500);
 Serial.println(sensorvalue);
 */
}

I have also attach an image of the setup as reference and the serial monitor reading.

Thank you for time and your help is appreciated very much

Capture.PNG

  Input = analogRead(rpm);

'rpm' is not an analog input pin number. I think you mean:

  Input = rpm;

All variables that contain millis() or micros() values should be declared "unsigned long", not just "long".

If you are detecting the time for every revolution then don't bother to calculate RPS or RPM for speed control purposes - just work with millisecs or microsecs per revolution - just remember that a bigger number = slower speed.

This link has the code I use to detect motor speed. In another program I use PID with this to maintain speed.

...R

Mr Johnwasher,

I made the changes to the coding as mentioned but the reading in the serial monitor is still being the same. I having doubt about my PID coding part could sir help me in that part please. Thanks

The changes in the coding

int sensorvalue;
int state1 = HIGH;
int state2;
float rps;
float rpm;
unsigned long prevMillis = 0;
unsigned long interval = 200;
unsigned long currentTime;
unsigned long prevTime = 1;
unsigned long diffTime;
int sensorthreshold = 30;
Input = rpm;
  Setpoint = 100;

The changes in the coding

Please post all the code, not just what you think the deltas are.

Mr Robin2,

I seen the link attached and try to run it with slight chances. I am getting the rpm reading. Can sir show me how to attach PID control in the coding.

The coding i am using

const byte IN1 = 7;
const byte IN2 = 8;
const byte ENA = 9;
const byte potPin = A0;

int potVal;
int pwmVal;

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;

unsigned long prevDisplayMillis;
unsigned long  displayInterval = 1000;

    // variables for the ISR
volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile bool newIsrMicros = false;



void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");
    pinMode (IN1, OUTPUT);
    pinMode (IN2, OUTPUT);

    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();
        readPot();
        updateMotorSpeed();
    }
}

//===========

 void readPot() {
    potVal = analogRead(potPin);
}

//===========

void updateMotorSpeed() {
    pwmVal = potVal >> 2;

    digitalWrite(IN1,HIGH);
    digitalWrite(IN2,LOW);

 }

//===========

void getIsrData() {
    if (newIsrMicros == true) {
        prevRevMicros = revMicros; // save the previous value
        noInterrupts();
            revMicros = isrMicros;
            revCount = isrCount;
            newIsrMicros = false;
        interrupts();
        revDuration = revMicros - prevRevMicros;
    }
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("PWM Val "); Serial.println(pwmVal);
    Serial.print("  Rev Duration ");
    Serial.print(revDuration);
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

I have edited this line where the PMW value is preset in the coding but previously it was pmwVal

analogWrite(ENA, 150)

I have also attached the serial monitor reading.

MR AWOL

Sorry here is the full coding,

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <PID_v1.h>


int sensorvalue;
int state1 = HIGH;
int state2;
float rps;
float rpm;
unsigned long prevMillis = 0;
unsigned long interval = 200;
unsigned long currentTime;
unsigned long prevTime = 1;
unsigned long diffTime;
int sensorthreshold = 30;

 int IN1 = 7; // DC motor pin 
 int IN2 = 8; // DC motor pin 
 int ENA = 9; // PWM DC motor pin 
 
double Setpoint, Input, Output;

double Kp=2, Ki=5, Kd=1;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// this value indicates the limit reading between dark and light,
// it has to be tested as it may change acording to the 
// distance the leds are placed.
// to see what number is good, check the sensorvalue variable value
// as printed out in the serial monitor

void setup()
{
  Serial.begin(9600);
 
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);
  pinMode (ENA, OUTPUT);

  Input = rpm;
  Setpoint = 100;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

}

void loop()
{
   analogWrite(ENA,150);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2,LOW);
  
  sensorvalue = analogRead(0); // read from pin 0
  if(sensorvalue < sensorthreshold)
    state1 = HIGH;
   else
    state1 = LOW;
   
   if(state2!=state1){      //counts when the state change, thats from (dark to light) or 
                            //from (light to dark), remember that IR light is invisible to us.
     if (state2>state1){
       currentTime = micros();   // Get the arduino time in microseconds
       diffTime = currentTime - prevTime;  // calculate the time diff from the last meet-up
       rps = 1000000/diffTime;   // calculate how many rev per second, good to know
       rpm = 60000000/diffTime;  // calculate how many rev per minute
       
       unsigned long currentMillis = millis();
       
       // print to serial at every interval - defined at the variables declaration
       if(currentMillis - prevMillis > interval){ // see if now already an interval long
       prevMillis = currentMillis;       
       Serial.print(rps); Serial.print(" rps  "); Serial.print(rpm); Serial.println(" rpm");
       }
       
       prevTime = currentTime;
     }
     state2 = state1;
   }
{
  Input = analogRead(rpm);
  myPID.Compute();
  analogWrite(9, Output);
}
 /* 
 //only for testing to determine the sensorthreshold value
 delay(500);
 Serial.println(sensorvalue);
 */
}
   if(state2!=state1){      //counts when the state change, thats from (dark to light) or

That code is not counting anything. It is (perfectly obvious that it is) comparing two values. Get rid of useless comments, or make them right.

  Input = analogRead(rpm);

EXACTLY which pin are you reading from? Why do you think that reading from that pin will give you ANYTHING useful?

  analogWrite(9, Output);

How long, after this statement, is it until you change the PWM value on this pin?

kalai14:
Can sir show me how to attach PID control in the coding.

There is no need to use the word "sir". The word "you" or my name "Robin" will be fine.

In my short program the potentiometer is used directly to control the PWM value and hence the speed of the motor.

To work with PID the potentiometer should be used to change the value of Setpoint and the value of revDuration should be used as the other input for the PID calculation. Then the output of the PID calculation will be the value for PWM.

...R

Hi,

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :slight_smile:

Paul. S

Sorry for late reply as i was in no internet zone.

I used it to read the current rpm of the motor speed.

EXACTLY which pin are you reading from? Why do you think that reading from that pin will give you ANYTHING useful?

I did not understand the question sorry.

How long, after this statement, is it until you change the PWM value on this pin?

Robin 2,

Sorry for late reply i was in no internet zone.

To work with PID the potentiometer should be used to change the value of Setpoint and the value of revDuration should be used as the other input for the PID calculation. Then the output of the PID calculation will be the value for PWM.

I want to control the value of PMW through Arduino coding and the PID too. What if i set the Setpoint value same as my PMW value. Will it work to control the speed ?

Hi Tom

Sorry for late reply, i have attached hand written drawing of the wiring diagram

IMG_20180830_071541[1].jpg

kalai14:
What if i set the Setpoint value same as my PMW value. Will it work to control the speed ?

That question leads me to think that you don't understand how PID works.

If you already know the PWM value so you can put it in the Setpoint then what would be the value of the PID calculations?

The idea underlying PID is that you have a Setpoint representing the speed you want to achieve, and also a measurement of the actual speed which is updated frequently. The PID calculations continually compare the actual speed with the desired speed and try to adjust the motor power so as to keep the speed constant.

...R

Hi,
OPs Circuit;


Where is the gnd and 5V connection between the Arduino and the 298 board.
Where is the power supply for the motor driver.

What is the IR device, link to specs/data?
What are you using as an IR source to aim at the IR device?
Please show your power connections please.

Thanks.. Tom.. :slight_smile:

I used it to read the current rpm of the motor speed.

Used what?

       rpm = 60000000/diffTime;  // calculate how many rev per minute
  Input = analogRead(rpm);

You calculate a value for rpm. Then you use that value as a pin number. There is just NO way that THAT makes ANY sense.

I did not understand the question sorry.

void loop()
{
   analogWrite(ENA,150);

<some piss-poorly indented code snipped>
{
  Input = analogRead(rpm);
  myPID.Compute();
  analogWrite(9, Output);
}
}

You set the PWM value for pin 9 to the garbage value that Compute() calculated (based on complete nonsense as input), then loop() ends. Then, you set the PWM value of the ENA pin to 150.

 int ENA = 9; // PWM DC motor pin

So, nanoseconds after you change the PWM value of pin 9, you set the PWM value back to 150.

It's no wonder that your motor never changes speed.

Hi Tom,

Where is the gnd and 5V connection between the Arduino and the 298 board.
Where is the power supply for the motor driver.

In the actual wiring i supplied 12V to the motor driver and the output 5V supplied back to Arduino and ground for both has been looped together. I did not drew in the diagram and sorry for that.

What is the IR device, link to specs/data?
What are you using as an IR source to aim at the IR device?

IR device input signal in connected to A0 in Arduino. I bought it through online so i do not know about the specs of the IR sensor.

kalai14:
IR device input signal in connected to A0 in Arduino.

For detecting motor speed it would be better to have a device that interfaces digitally, rather than needing analogRead() which is slow.

I bought it through online so i do not know about the specs of the IR sensor.

Post a link to the web-page where you got it.

...R

HI Robin2

The idea underlying PID is that you have a Setpoint representing the speed you want to achieve, and also a measurement of the actual speed which is updated frequently. The PID calculations continually compare the actual speed with the desired speed and try to adjust the motor power so as to keep the speed constant.

I need to study about how PID works before i do the coding part. I am sorry for that. The idea was to set a PWM value for the motor then provide feedback through IR Sensor to achieve the actual PWM signal set in the coding using PID

Post a link to the web-page where you got it.

Hi Paul. S

So, nanoseconds after you change the PWM value of pin 9, you set the PWM value back to 150.

How can i solve this ? i am using pin 9 as pwm signal and i taught after computing the pid can be set again to the same pin to change the motor spin