PID control of two motors speed via serial port By Alejandro Alonso-Puig
January 2.005
mundobot.com
The presented system is a feedback control loop of type PID (Proportional Integral Differential) that allows to control the speed of two motors (controls PD) and to slave them (control I) so that both motors maintain a speed relation in case one is forced by external agents to vary it.This system of speed control is applicable for example to robots with traction of differential type. Simply one indicates to the system the wished speed (SetPoint) and the difference of speed (Bias) that is to have between both motors (to be able to make turns). The system takes care of all the rest.
The system is controllable by commands sent via serial port. It is possible to specify not only the speed and Bias, but also the gains and other parameters that we will see later.
Additionally the system allows to obtain the values of several of its parameters via serial port.
HARDWARE
Motors: Although the system is adaptable to different types of motors, those used in the test bench and for which PID gains are fit by defect in the program are the motors with gearhead and encoders from Solution Cubed (http://www.solutions-cubed.com). Features: 12V, 200RPM, 3,6kgcm of torque, quadrature encoder (2 channels for detection of rotational direction) of 200CPR, that after the gearhead reduction allows to obtain 6000CPR.
Important note: The system is prepared to use quadrature encoders. If they are used incremental encoders of only one channel it will be necessary to connect them to RA4 and RC0, putting RB2 and RB3 to mass. It will not be possible to control negative speeds (reverse mode or absolute turns), obtaining an unstable result of the controller in his attempt. In any case it would be preferable to make modifications in the code to adapt it, since it can become unstable in values near zero.
Control Card: We have used the Skypic control card from IEARobotics with the PIC16F876 micro (http://www.iearobotics.com/proyectos/skypic/skypic.html). Such card have an RS232C port with the standard voltage levels (-12v, +12v) that could be directly connected to a PC serial port and control the system by using a terminal emulator application like hiperterminal.
Driver: In order to give the motors the current needed we have used the MSE-A100 driver from Microsystems Engineering (http://www.msebilbao.com), based on the L293B chip, that supports a couple of motors with 1A continuous and 2A peak current draw each.
SOFTWARE
The program that is in the controller has been developed in C++ using the BoostC cross compiler from Pavel Baranov (http://www.picant.com/c2c/c.html). We have used libraries for the control of the serial port of the microcontroller, developed by Yann Hendrikx. The complete self explanatory program can be downloaded from the following Link. It includes the sources in C, the object "motor.hex" (ready to burn in the microcontroller) and the intermediate version in assembler "motor.asm" generated by the cross compiler:
Control program motor.zip (.ZIP 84Kb)
For the control from the PC a terminal emulator application like Hiperterminal is used, configured as 9600, n, 8, 1
From the hyperterminal the following commands are used for the control of the system:
Set Speed A first byte of value 32 is sent (space). Next the speed is sent in the following byte. value 50 (character "2") indicates null speed. Inferior values correspond to negative speed (reverse mode) and greater values correspond to positive speed (forward mode). The program takes the sent code and multiplies it by 10. Therefore the speed (SetPoint) used by the program will be (code-50)*10 This speed is measurement in cycles from encoder by each 10 msg (Kpause parameter in the program). Thus, depending on the encoder used, the speed can vary. For motors with encoder of N cycles per revolution of the axis (CPR), in order to get a speed of V (RPM), the speed (SetPoint) would be (V * N * Kpause)/60.000. As code=50+SetPoint/10 then, the code to send would be code=50+ V * N * Kpause/600.000
Thus for example, for the configuration used in the test bench, where N=6.000CPR and Kpause=10. In order to make the motors go at 100RPM would be necessary to send the speed code 50+100*6.000*10/600.000=60 (character "<" of ASCII table).
In summary, pressing the space key and then the key "<" would set the motor to 100RPM forward.
Change the default value of Kpause The value by defect of Kpause can be modified by sending first the code 37 (symbol % in the ASCII table) and then the value, considering that value 50 corresponds to a null Kpause. Thus, the value assigned to Kpause will be the one sent minus 50. For example, if code 60 is sent (character "<" of ASCII table), Kpause will be 10 (60-50). If values smaller than 50 are sent, Kpause will become null. It could be necessary to modify Kpause depending on the CPR of the encoder used. The program internally uses a register of 8 bits for each motor, that counts the cycles coming from encoder. If the CPR of the encoder are very high and Kpause is also high, they could happen that the registers overflow giving wrong speed values to the control loop. Therefore Kpause has to have a value (in msg) so that the motor at max speed does not produce more than 255 cycles of encoder. For example, for a motor of max speed V (RPM) at the shaft and encoder of N cycles per revolution of the shaft (CPR), the value of Kpause does not have to be greater than (60.000*255)/(V*N)
Thus for example, for the configuration used in the test bench, where N=6.000CPR and V=200RPM, the value of Kpause had to be less than (60,000 * 255)/(200*6.000)= 12'75. Kpause is number of one byte, so it would be 12 at the most.
In summary, to establish a Kpause of 20, key % would be pressed (code 37) and then key F (code 70)
Bias The value of the Bias indicates the relative speed of the left motor respect to the right motor. A positive Bias makes the left motor to go faster than right motor and a negative Bias do the opposite. Basically for a specific speed (SetPoint), the motor speed would be: Vi =SetPoint + Bias/2
Vd =SetPoint - Bias/2Gains In order to vary the gains by default of the PID controller, first it is necessary to send a code that indicates the gain to vary:
- Code 34 (Character quotes (") of ASCII table) for the Proportional gain (Kpro)
- Code 35 (Character hash (#) of ASCII table) for the Integral gain (Kint)
- Code 36 (Character dollar ($) of ASCII table) for the Differential gain (Kdif)
After this code is sent, the value of the gain should be sent in 1/50 units, starting in code 50 (code 50 indicates gain 0). Thus for example, to establish a Kint gain of 0.02 first would be sent code 35 and then code 0,02*50+50 = 51
Request of data Sending code 38 (character & of ASCII table) the request of data status is activated and sending code 39 (character ' of ASCII table) the request of data is deactivated. When activating the request of data, the controller starts sending continuously the following control values via serial port:
- SetPoint: Speed of reference
- PVLeft : Real speed as per left encoder
- PVRight: Real speed as per right encoder
- Kpro: Proportional Gain in 1/50 units, therefore if it shows 20, the real gain used would be 20/50=0,4
- Kdif: Differential Gain in units of 1/50
- Kint: Integral Gain in units of 1/50
- Bias
- Kpause
Next we will show how useful is to collect these data in the computer to see the behavior of the system and to adjust the gains suitably.
Note: The units used are a bit complex, but it has been necessary to do it in this way to adapt the program to the limitations of the compiler, between whom it is the lack of floating point variables and the limitation of long numbers to values of 16 bits.
Activating the request of data we obtain a continuous list of feedback values from the system that could be used by PC applications to have permanent information of the system and to make the adjustments needed.
Next it is showed a manual method to adjust the gains of the controller.
The method consists on maintaining the motor stopped. Send to the system via serial port the values of Kpro, Kint and Kdif wished. Activate the capture of data from the hyperterminal and indicate the system to set the motor speed up to 100RPM. After a couple of seconds we could stop the motor and stop the capture
The following excel spreadsheet could be used to visualize the captured data:
pidtest.xls (.xls 120Kb)
The data of the capture will be copied and pasted in the B17 cell, with which the graph of behavior of both motors will be drawn automatically. See the following example:
In a visual way it is possible to see the behavior of the controller and adjust the gains until obtaining interesting results:
Gains tuning
There are several theories for the manual adjustment of gains of PID controller with no need to get into mathematics formulation. There are several links at referring these theories at the end of the page. Basically the methodology that I have followed has been the following one, extracted of the mentioned theories.
Start with a value of proportional gain equal or smaller than one and set the other gains to zero. When annulling the integral gain is annulled the influence of the speed of a motor on the other. Check the results. Example:
Raise sluggishly the value of the proportional gain maintaining the other gains zero until the graph shows oscillations. Example:
Keep the integral gain to zero and set a differential gain 10 times inferior to the proportional one.
See the results raise de differential gain until the graph shows no oscillations. Example:
Having the P and D gains already established, start setting a minimum integral gain (0.02 are the minim supported by the program) and observe the results. Raise the integral gain little by little until both motors begin to oscillate and get into resonance.
![]()
Then divide the integral gain by 10 or 20. Gain will be correct when the graph doesn't show oscillations and when trying to force by hand one motor to stop, the other tends also to stop by itself (Keep in mind that the current draw of a stacked motor tends to go up very fast. The motor driver used has to give sufficient power or it will overheat). Example:
BIBLIOGRAPHY
![]()
Book "Building your Robot Drive Trains". Dennis Clark and Michael Owings. Ed. TAB Robotics. ISBN 0-007-140850-9. Chapter 8 - Motor Control 201. Closing the loop with feedback. ![]()
Book "Mobile Robots". Josephe L. Jones et al. Ed. A.K.Peters. ISBN 1-56881-097-0. Chapter 7; 7.8.2 - Feedback Control Loops ![]()
PID Without a PhD (Easy guide to PID control algorithms and gains tuning) : http://www.embedded.com/2000/0010/0010feat3.htm ![]()
Adjusting PID Gains: http://www.ctc-control.com/customer/elearning/servotut/adjus.asp Documentation of SkyPic control card: http://www.iearobotics.com/proyectos/skypic/skypic.html