Saturday, 31 March 2018

DC motor control with an RPi - writing the software

This explains some of the lessons learned and techniques I have used to develop Raspberry Pi software to provide accurate and responsive control for DC motors using feedback from rotary encoders.

It follows on from my previous post, showng the results of some initial testing on the feasability of good motor control using just a Raspberry Pi (i.e. without an arduino or other micro controller).

I very much liked the approach described by David Anderson explaining his approach to basic robot control, so the overall approach I used was to write software that is called from a regular timer function to run the control loop.

Such software can then be run directly from some higher level control loop, or wrapped up to provide a freestanding process controlled through a pipe or web service.

My usual approach to time / function critical software is to start with Python and get to a point where:
  • it all works well - great - job done.
  • it is hopeless - its never going to work this way - try something completely different or give up.
  • its working, but not quite well enough - perhaps a couple of small key parts can be re-implemented -possibly in C.
In this case - so far at least - the pure python version looks to be doing the job well.

I have a couple of repositories on github with the code for this, but these are still very much a work in progress.

motor drivers are here.

simple robot control is here. - So far this only does remote control from a web browser.



building from the bottom up

monitoring the rotary encoder

One key part of the problem is capturing the output from the rotary encoder such that it is reliable, accurate and doesn't consume too much resource.

While pulse counting was key, I didn't need to track the phasing as I already know which way the motor is turning, this means all I need to do is detect and count edges.

Pigpio's tally capability - part of the callback function - does exactly this and should provide accurate counting up to at least 50,000Hz pulses which is a good margin above what I expect to need (10,000 rpm, 3 pulses per rev).

My control loop can then read this counter 20 times (or thereabouts) per second and any minor jitter will not result in lost pulses - merely some jitter in the feedback which should correct itself next time around the loop.

low level control - duty cycle and pulse frequency

I found early on that at high frequencies meant that the motor would not move at all until quite high duty cycles were used - and consequently quite fast motor speeds. At very low pulse frequencies, low duty cycles - and low rpm - could be achieved, but the motion even through a fairly big reduction ratio gearbox was noticably jerky.

The motor controller therefore provides low level methods to set both the frequency and duty cycle used to drive the motor.

providing a linear speed control

DC motors usually provide a very non-linear response to increasing pulse width - in fact the curve is pretty much asymptotic. This means that at low %age duty cycles a small increase gives a huge increase in speed, whereas at high %ages  there is hardly any change.
This became a particular problem when I started on the PID feedback control as the PID settings that worked at around 700 ticks per second were hopeless at 200 ticks per second and vice-versa.

Also there is variation between forwards and backwards as well as the differences in response at different frquencies:
This plot shows the response of a single motor with error bars showing the results of several runs at each setting of duty cycle and pulse frequency.




1 comment: