Arduino - Motor PID speed control

Saturday, December 31, 2016

With PID control, speed of motor can be archived exactly. This article mainly introduces about making program in Arduino Pro mini, program in Computer (Visual Studio) to control motor speed by PID algorithm.




Fig1. General connection

Arduino Pro mini is used to store motor control, PID algorithm, communicating with PC (through COM Port)
Computer will have HMI made by Visual Studio to communicate with Arduino. HMI will show motor speed graph, change motor speed setting
Fig2. HMI on Computer (made by Visual Studio)

Let's go detail:
Hardware needed:
1. Motor with Encoder
2. H-bridge PCB
3. Arduino Pro mini
4. UART PCB
5. Computer (with Visual Studio)

Step 1. Hardware connection
This article doesn't mention about detail connection, it is based on previous article which can be found at this link





Step 2. Code works at Arduino
void loop() {
  if (stringComplete) {
    // clear the string when COM receiving is completed
    mySt = "";  //note: in code below, mySt will not become blank, mySt is blank until '\n' is received
    stringComplete = false;
  }

  //receive command from Visual Studio
  if (mySt.substring(0,8) == "vs_start"){
    digitalWrite(pin_fwd,1);      //run motor run forward
    digitalWrite(pin_bwd,0);
    motor_start = true;
  }
  if (mySt.substring(0,7) == "vs_stop"){
    digitalWrite(pin_fwd,0);
    digitalWrite(pin_bwd,0);      //stop motor
    motor_start = false;
  }
  if (mySt.substring(0,12) == "vs_set_speed"){
    set_speed = mySt.substring(12,mySt.length()).toFloat();  //get string after set_speed
  }
  if (mySt.substring(0,5) == "vs_kp"){
    kp = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_kp
  }
  if (mySt.substring(0,5) == "vs_ki"){
    ki = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_ki
  }
  if (mySt.substring(0,5) == "vs_kd"){
    kd = mySt.substring(5,mySt.length()).toFloat(); //get string after vs_kd
  }
}

void detect_a() {
  encoder+=1; //increasing encoder at new pulse
  m_direction = digitalRead(pin_b); //read direction of motor
}
ISR(TIMER1_OVF_vect)        // interrupt service routine - tick every 0.1sec
{
  TCNT1 = timer1_counter;   // set timer
  pv_speed = 60.0*(encoder/200.0)/0.1;  //calculate motor speed, unit is rpm
  encoder=0;
  //print out speed
  if (Serial.available() <= 0) {
    Serial.print("speed");
    Serial.println(pv_speed);         //Print speed (rpm) value to Visual Studio
    }


  //PID program
  if (motor_start){
    e_speed = set_speed - pv_speed;
    pwm_pulse = e_speed*kp + e_speed_sum*ki + (e_speed - e_speed_pre)*kd;
    e_speed_pre = e_speed;  //save last (previous) error
    e_speed_sum += e_speed; //sum of error
    if (e_speed_sum >4000) e_speed_sum = 4000;
    if (e_speed_sum <-4000) e_speed_sum = -4000;
  }
  else{
    e_speed = 0;
    e_speed_pre = 0;
    e_speed_sum = 0;
    pwm_pulse = 0;
  }


  //update new speed
  if (pwm_pulse <255 & pwm_pulse >0){
    analogWrite(pin_pwm,pwm_pulse);  //set motor speed
  }
  else{
    if (pwm_pulse>255){
      analogWrite(pin_pwm,255);
    }
    else{
      analogWrite(pin_pwm,0);
    }
  }

}

At the beginning of program, it will receive command from Computer (start/stop motor, motor speed setting, kP, kI, kD gain of PID)
Next is void detect_a(): encoder sum calculation -> used for speed calcluation in Timer interrupt routine.
Timer interrupt routine ISR(TIMER1_OVF_vect): every 0.1 this program is called, content includes:
   (1) Calculate motor speed
   (2) Send motor speed to Computer
   (3) Calculate PWM pulse (base on PID algorithm)
   (4) Push result of PWM to H-brigde
The entire of code for Arduino Pro mini can be download at this link

Step 3. Code works at Computer
Visual Studio 2012 is used to make HMI program, in which:
   (1) Send speed setting to Arduino
   (2) Send PID gain (kP, kI, kD) to Arduino
   (3) Receive motor speed -> show on graph
Fig3. Visual studio program

The whole code of Visual Studio program can be downloaded at this link
To make a Visual Studio program -> see detail step at this article
In general, the code will have:
#pragma endregion
private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
serialPort1->Open();
timer1->Start();
mStr = "0";
i=300;
}
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_set_speed"+textBox1->Text); //send set_speed to Arduino
serialPort1->WriteLine("vs_kp"+textBox2->Text); //send kP to Arduino
serialPort1->WriteLine("vs_ki"+textBox3->Text); //send kI to Arduino
serialPort1->WriteLine("vs_kd"+textBox4->Text); //send kD to Arduino
}
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
String^ length;
length=mStr->Length.ToString();
if(mStr->Substring(0,5)=="speed"){
speed=mStr->Substring(5,System::Convert::ToInt32(length)-6);
label1->Text=speed;
//print motor speed into Chart
this->chart1->Series["Series1"]->Points->AddXY(i,System::Convert::ToDouble(speed));
i++;
this->chart1->ChartAreas["ChartArea1"]->AxisX->Minimum=i-300; //shift x-axis
}
}
private: System::Void serialPort1_DataReceived(System::Object^  sender, System::IO::Ports::SerialDataReceivedEventArgs^  e) {
mStr=serialPort1->ReadLine();
}
private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_start"); //start motor
}
private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
serialPort1->WriteLine("vs_stop"); //stop motor
}


Result:



15 comments

  1. Thanks, You wrote awesome, I have learn lots of things from your article. It's really helpful for any readers.
    Battery Operated Flow Meter

    ReplyDelete
  2. Will it work on arduino uno

    ReplyDelete
    Replies
    1. so we just didn't need the UART PCB and we don't need to edit arduino code even when using arduino uno?

      sorry to bother you but i really need a fast answer

      Delete
    2. Right, Arduino UNO already had UART PCB inside. When Arduino UNO connect to Computer via USB Port, an COM Port will be shown -> use this COM Port No. to communicate between Arduino UNO and Computer.
      About the code, i try to compile for UNO, it run perfectly. Let's try it!

      Delete
  3. How can i try to input the value of speed, kp, ki, kd by using serial monitor in arduino program?

    thank you
    Fungkyking01@gmail.com

    ReplyDelete
    Replies
    1. Hi. Unfortunately i already removed this model (motor, circuit,...) -> can't make project again as your mention.
      You should find an example to read data input from serial port -> then, import parameters (kP, kI, kD) to formular. It's not difficult, let's do it

      Delete
  4. Bro Can you kindly Provide the whole circuit diagram. its quite confusing which connection goes where. thnx in advance

    ReplyDelete
  5. Please come to thí link to see connection
    http://engineer2you.blogspot.com/2016/12/arduino-motor-speed-control-pwm.html?m=1

    ReplyDelete
  6. Bro I followed all the steps you mentioned and made the project with Arduino Uno. The problem I get is that the PID is not working, the motor keeps on rotating with a constant speed of 530 RPM and when I apply some load to the motor the speed reduces and does not go back to 538 RPM. Also I can't send commands to arduino. like i want the motor to rotate with a speed for 700 RPM and 300 RPM for example, it does not work when i press the send button on HMI.

    ReplyDelete
    Replies
    1. Hi,
      (1) First step is make sure you can send command (and convert it to integer) into Arduino. You can search on web this kind of project, or see here http://engineer2you.blogspot.com/2016/12/arduino-uart-communication.html
      (2) Tuning PID parameter is not easy. Each motor will have each parameter. First tuning, you should make kP, kI some small value, kD=0, then see motor reaction. Then gradually increase or decrease kP, kI. Normally, no need kD.

      Delete
  7. Is there an equivalent motor that can be used with this system, it is very difficult to find the one you used.

    ReplyDelete
    Replies
    1. You can use any DC motor, then find kP kI kD for it

      Delete
  8. Hi this is one s brilliant!! I'm want to design kind of this but slightly different..
    There is LCD or 7 segment using the rotary encoder rpm 0-8000 per 1 rpm changes, first we need to a rpm then once after pressing start arduino need to rpm of the motor
    Can you suggest me any enocder motor up to 8000rpm n how to control the motor?
    Help me pls

    ReplyDelete
    Replies
    1. Hi, There's rearly motor with encoder 8000 ppr (pulse per round). If you need 8000ppr encoder, you have to use external encoder, connect to motor shaft by coupling. I was wondering if what application you need 8000ppr encoder? Could you share? We will make it together (because i'm free now haha)

      Delete

 

Search This Blog

Most Reading

Google+ Followers