Velocity control of a DC motor without any encoder
The new OpenServo V3 boards offer a new nifty feature for measuring the velocity of the driven motor without any additional sensors. The measurement principle is called Back-EMF (Back ElectroMotive Force). Here the electro-magnetic field which induces a voltage back to the turning coil is measurement. While supplying the motor with the needed current most of the time, the supply voltage is switched of periodically for a very short interval. For this interval the motor is used as a generator and the generated voltage is measured. This new feature provides the possibility to control the motor velocity without any additional sensor. Based on the current P(I)D algorithm for position control of the OpenServo I've implemented a PI+FF controller for velocity control like proposed by Barry Carter in this thread.
Some readers may not be familiar with the technical terms in the following document so I will start with a short description of the important ones:
Setpoint velocity is the velocity which is commanded to the controller. The algorithm tries to drive the motor in such manner that the measured velocity will be equal to the setpoint velocity.
Motor PWM is the pulse width modulated signal which drives the H-bridge amplifier. It's more or less proportional to the voltage applied to the dc motor.
Actuating variable is a general term for the variable a controller can steer to obtain a desired behavior. In our case the actuating variable is the motor PWM.
The PI+FF Controler
Explaining the PI+FF algorithm I wanted to start with a description of the letters PI+FF:
P is the proportional component. It contributes to the actuating variable (motor PWM in our case) with the weighted difference between setpoint velocity and actual velocity.
I is the integral component. It sums up the difference between setpoint and measured velocity of the past. If there is a steady difference the integrator will increase over time until the steady error vanishes.
FF is the feed forward component. It contributes to the acuating variable proportional to the setpoint velocity. This takes account to the fact that we need a certain motor PWM to reach a given velocity even in the absence of any disturbance. This component provides the biggest proportion to the actuating variable while the P and I components "only" smoothing out the disturbance like external load.
Another function you need if you implement an I-component is an anti-windup. It's the maximum value the integrator can charge to compensate a steady error. If this limitation is omitted the integrator charges to very high values whenever the actuating variable exceeds it's natural limit. For example if we command a jump from zero to a high velocity the motor PWM will most likely exceeds it's maximum value until the motor has reached the setpoint velocity (the real signal to the motor is truncated of course to protect the hardware). Meanwhile the I-component will still increase because of the persisting difference between setpoint and measured velocity. When the motor reaches the setpoint velocity, the P-component vanishes and the FF-component should be exactly the PWM value needed to hold the setpoint velocity but the I-component causes an overshoot. To reduce this effect the I-component has to be limited.
The complete implementation can be found on the OpenServo CVS in /OpenServo/AVR_VelocityControl.
1 // The proportional component to the PD+FF is the velocity error. 2 p_component = seek_velocity - current_velocity; 3 // Increment the integral component 4 i_component += (int32_t) p_component * (int32_t) i_gain; 5 // Saturate the integrator for anti wind-up 6 if (i_component > anti_windup) i_component = anti_windup; 7 if (i_component < -anti_windup) i_component = -anti_windup; 8 // Start with zero PWM output. 9 pwm_output = 0; 10 // Apply the feed forward component of the PWM output. 11 pwm_output += (int32_t) seek_velocity * (int32_t) feed_forward_gain; 12 // Apply the proportional component of the PWM output. 13 pwm_output += (int32_t) p_component * (int32_t) p_gain; 14 // Apply the integral component of the PWM output. 15 pwm_output += i_component; 16 // Shift by 8 to account for the multiply by the 8:8 fixed point gain values. 17 pwm_output >>= 8;
To see the quality of the measured back-emf signal and to evaluate the velocity control I've connected the OpenServo board to a test bench I've build for my balancing robot Hektor. The robot drive consists of a 3-6V DC motor and a gearbox. The shaft of the gearbox (where normally the wheel is mounted) is connected to a rotational sensor (in fact just an old ball mouse). Both signals are polled by an extra ATMEGA168 (back-emf over I2C from the OpenServo and angle over PS2 from the old mouse) and are send over RS232 to the PC.
In the right plot you can see the motor velocity plotted over a short time inverval. The dashed curves is the velocity measured by the mouse and the dotted curves is the back-emf signal. The different colors are different PWM values started from 20 to 60. The fluctuation in both curves (back-emf and mouse signal) is because of the friction in the gearbox varies. You can even hear that the motor is not turning with constant speed.
Closed control loop
In the following plot you can see a comparison between the behavior of the controlled and the uncontrolled motor.
The green curve is the velocity of the controlled motor (velocity is commanded). At time step 100 the setpoint velocity is set to 150 and at time step 300 it's reset to zero again. The blue curve shows the velocity of the uncontrolled motor (PWM is commanded). At time step 100 the motor PWM is set to a value which correspond to a velocity of 150 (I've just tried different PWM values). At timestep 300 it's reset to zero again.
For a further discussion of the velocity control see the following thread: