Let's start by a video
the idea
I had an old wiper motor made by Bosch. It's the same type as this one.
They are very powerful, they draw 2 to 3A, do not run very fast but have really a lot of torque, much more than what I would ever need to rotate a solar tracker (even a big size one !).
However these motors are DC ones without any solution to control their position.
So I decided to add a cheap 12bits magnetic encoder AS5600
This chip is equiped with a precise hall sensor and allows to detect the angualr position of a diametric magnet "flying" above it (ideally less than 3mm above the chip).
I decided to install the magnet directly on the motor shaft (not the wiper shaft) to get the maximum precision.
mounting the magnet
The first operation was to cut the end cap of the motor to access the motor shaft.
Unmount the motor, remove the cover. Then drill the bottom (not the bearing...) and finish grinding it so that the shaft will be visible.
Now, print the sensor holder and glue it in place.
Also on thingiverse: https://www.thingiverse.com/thing:5552935
And finally glue the magnet and fix the sensor in place
Your motor is now a servo motor !
wiring the AS5600
this device is an I2C sensor and needs only 4 pins to interface with ESP32 MCU
Adding a driver board
To drive this motor you will need a quite powerful H bridge driver. I chose the IBT-2 chineese driver.
It's a very powerful one said to handle 43A.
It's composed of two half bridges BTN7970
Using this chip is quite easy. I followed this excellent tutorial but replaced the arduino by an ESP32.
Wiring IBT-2 with ESP32
The big terminal blocks are connected to the motor and the DC power (12V in reality !)
The pin headers are connected as on the drawing to the ESP32 pins
- IBT-2 pins 7 (VCC) to ESP32 3.3V (yes it works at 3.3V)
- IBT-2 pin 8 (GND) to GND (both 12V ground and ESP32 ground)
- IBT-2 pins 5 (R_IS) and 6 (L_IS) not connected
- IBT-2 pin 1 (RPWM) to ESP32 pin 18 (PWM output pin for Right half bridge)
- IBT-2 pin 2 (LPWM) to ESP32 pin 5 (PWM output pin for Right half bridge))
- IBT-2 pin 3 (EN_R) to ESP32 pin 17 ( Enable pins R )
- IBT-2 pin 4 (EN_L) to ESP32 pin 16 ( Enable pins L )
ESP32 firmware
Example firmware is available on my Github
The code is extremely simple and only shows how to read the sensor, drive the motor and apply a PID control loop to precisely position the motor at any number of turn you want (integer + fractional turn of the motor shaft).
reading the AS5600
I do use Rob Tillard's AS5600 library. It is really simple and does the job !
A few lines are enough to read the sensor and detect "zero crossing" for full rotations counting:
//AS5600
rawValue = as5600.readAngle();
if (((rawValue - prevRawValue) < -999) && CW) nbRot++ ; //apply hysteresis to detect each full rotation (4095 <--> 0)
if (((rawValue - prevRawValue) > 999) && !CW) nbRot-- ;
prevRawValue = rawValue; //save the rawValue for next iteration
driving the motor
no library but simple code as well !
two PWM signals enter Left and Right H bridges.
void runMotor(void)
{
if (pwmSpeed > 0)
{
ledcWrite(0, pwmSpeed);
ledcWrite(1, 0);
CW = true;
}
else
{
ledcWrite(0, 0);
ledcWrite(1, -pwmSpeed);
CW = false;
}
}
the pwmSpeed variable can be positive or negative. Positive is for CW rotation and negative CCW.
each pwm signal has a range 0-2047 on 11 bits and a frequancy of 24kHz
They are "hardware PWM" embeded into the ESP32
ledcAttachPin(RPWM_PIN, 0); // assign PWM pins to channels
ledcAttachPin(LPWM_PIN, 1); // assign PWM pins to channels
// Initialize channels : ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
ledcSetup(0, 24000, 11); // 24 kHz PWM, 11-bit resolution (range 0-2047)
ledcSetup(1, 24000, 11);
Now that we can rotate the motor, we simply have to control its motion.
This is done with a PID control loop
- setpoint is the number of rotations we want to achieve
- output is the pwmSpeed that will drive the motor
- the current number of rotation is substracted to the setpoint to produce the error signal
I do use Brett Beauregard's excellent PID library. It is provided with very clever tutorial that I highly recommend.
Using this library is really simple.
I have tuned the PID to have a "soft" behavior and to minimize overshoot. This is done taking advantage of the motor "deadband". Voltage at which the motor stalls.
I do start with a fully proportonnal PID tuning and when entering the deadband I do switch to an almost "integral" PID to recover the positionning error.
Kp = 50; // start with a fully proportionnal PID Ki = 0.0; if (abs(pwmSpeed) < DEADBAND) //switch to almost full integral, needed to skip "fast" the motor deadband with no motion... { Kp = 5; Ki = 100; } myPID.SetTunings(Kp, Ki, Kd); myPID.Compute();
the PID is refreshed every 1ms and this garantees a very accurate control of the shaft position.
The overshoot is minimal and almost constant to 0.46 rotation of the motor.
And it gives really good results. The motor stops almost exactly at the desired value.
Precision of the control loop
I have run the code several times, logging setpoint, reached number of rotation, sensor value and fractionnal number of rotation got.
The program consists in positionning the shaft at +300 rotations (absolute position) then going back to -300 (absolute).... and so on.
We finally get an average of 0,33 turn (motor shaft position) and a standard deviation of 0,09 turn
Knowing that 600 rotations give 11,5 rotation of the wiper shaft and assuming that I will attach a 8mm leadscrew with standard 1,25mm per turn, we get and absolute position (average) on the leadscrew of:
0.46*11.5/600*1.25 = 0,011mm
0,011 mm on the leadscrew |
Not so bad :-) (but for sure we will never get it as the mechanics will never be "perfect": backlash, dilatation...).
However we can consider that we have an "absolute" servo motor with a totally controlled position.
improving the control loop
I have tuned the PID loop with a much stronger set of parameters and with an "inertia" coefficient to precisely balance the little overshoot when the motor is stopping.
Details can be found into this log.
The position of the shaft is now controlled in a much accurate way.
The target postion can be "decimal" (-150,82)
- column C gives the reached value. (number of rotations of the motor shaft)
- Column A gives the AS5600 reading for each attempt. The initial value was 722.
- Column D gives the error (in sensor value) which is quite low!
- As an average on the motor shaft I get an error of 0.00096 turn
And if I drive a 8mm leadscrew with 1.25mm step I will get a positionning error of 23µm
-2,3-05 mm on the output shaft driving a 8mm leadscrew |
Well as usual this is theoritical as I don't take into account backlash, thermal dilatation and so on.
But this is impressively good !
Conclusion
This very simple project demonstrates that we can modify this cheap (10 to 20€) motor to transform it into a very precise and powerful servo motor. It could easily be used to act as a "digitally controlled" linear actuator.
If you don't believe me and If you want to see how strong could be a wiper motor linear actuator, have a look at this video: