# PWM strategy for motor with variable load

I have a Raspberry Pi and PWM motor controller driving a DAGU chassis with tracks. The driven wheels each have their own 8-pole encoder on them, and I count a rising or falling edge as a 'tick'. So there are 22.5 degrees/tick. The motors are geared 48:1.

The tracks and wheels give uneven load to the motor every full rotation. In other words, over the course of a full rotation the motor (or your hand) will find the tracks easier to turn at some points, and harder at others. This is due to the wheels/treads being a bit out of round.

This results in my PWM loop oscillating as well. I'm not sure how to "tune" this out though. First thought is to calculate the error over a longer period of time, but then the motors take forever to get up to speed.

Is there a general strategy for this, or should I just use something like:

if motor is stopped
use fast PWM rate
otherwise if motor has been moving for a while
use a slower rate


Apologies if this is the incorrect SE site. I wasn't sure if here or SO would be correct.

Main Loop:

    #define MOTOR_TICK_DELAY    1000000000LL //1000ms
motorA.target = 1.5;
motorB.target = 1.5;

debug("Standby off.\n");
bcm2835_gpio_set(STBY);

struct timespec time;
uint64_t cTime = 0, lTime = 0, refreshTime = 0, lTimeA = 0, lTimeB = 0;
int timeCount = 0;

clock_gettime(CLOCK_MONOTONIC_RAW, &time);
lTime = time.tv_sec * MOTOR_TICK_DELAY + time.tv_nsec;

while (quit == 0)
{
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
cTime = time.tv_sec * MOTOR_TICK_DELAY + time.tv_nsec;
refreshTime = cTime - lTime;

motorUpdate(&motorA, &cTime, &lTimeA);
motorUpdate(&motorB, &cTime, &lTimeB);

if (refreshTime > REFRESH_SPEED)
{//Refresh the ncurses output}
}


Motor PWM calculation method:

void motorUpdate(struct Motor* motor, uint64_t* cTime, uint64_t* lTime)
{
if (encoderTick(motor->encoderPin)) ++motor->encoderCount;

uint64_t dt = (uint64_t)(*cTime) - (uint64_t)(*lTime);

if (dt > MOTOR_TICK_DELAY)
{
//debug("ctime: %llu\n", *cTime);
//debug("ltime: %llu\n", *lTime);
//debug("diff: %llu\n", dt);

//actual speed = tick count / (dt in ???seconds)
motor->actual = (float)motor->encoderCount / (dt / 100000000LL);
motor->encoderCount = 0; //TODO need rolling average
*lTime = *cTime; //reset motor last tick time

//calculate raw PWM
motor->lastError = motor->error;
motor->error = (motor->target - motor->actual);
motor->dError = motor->error - motor->lastError;
motor->iError += motor->error;
motor->pwm = (motor->error * 75 + motor->iError * 120 + motor->dError * 5);
//motor->pwm = (motor->error * 50 + motor->iError * 100 + motor->dError * 10); //pretty smooth, takes a while
//motor->pwm = (motor->error * 60 + motor->iError * 120 + motor->dError * 10); //a bit jerky, but doesn't overshoot

//clamp PWM
if (motor->pwm > PWM_RANGE) motor->pwm = PWM_RANGE;
else if (motor->pwm < 0) motor->pwm = 0;

//set PWM
bcm2835_pwm_set_data(motor->pwmChannel, motor->pwm);
}
}


Full code is also available here: https://github.com/nearwood/rpi-drd/blob/master/controller/src/controller.c

• What speed (in terms of stiff-easy-stiff per second) are you aiming for? Do you have position/speed feedback, or are you doing open loop control? If you do, what's your sensor bandwidth? – Jack B Aug 13 '16 at 21:56
• How is the loop closed? – pjc50 Aug 13 '16 at 21:56
• I've updated the question with encoder information. It's closed loop. There's no real speed I'm aiming for. The motors will drive the tracks at a max of about 50rpm in the air. I'd like to be able to utilize, say, anywhere from 10-40 rpm (the desired speed will eventually be remote controlled). – Nick Aug 13 '16 at 22:09
• (1) Are the encoders single channel or dual? (2) Are the encoders on the wheels or on the motors? Motors would be 48 times better. – Transistor Aug 13 '16 at 22:14
• can you post the code for your PID algorithm? – Techydude Aug 13 '16 at 22:21

First of all, add a feedforward loop with LUT. For first experiment a LUT can be calculated as $U_{static}= k_{PWM}\cdot k_u\cdot v_{SP}$ Where $k_u$ is the motor constant $[\dfrac{V}{rpm}]$, $v$ is the speed $[rpm]$, $U_{static}$ are %PWM and $k_{PWM}$ is a constant of your H-bridge $[\dfrac{\% PWM}{V}]$
You can tune whatever controller with Ziegler-Nichols method. At very low speed your encoder is not capable to deliver good information, so you can implement a switch that disables the controller. Begin to set up with small steps, first use P-controller only, then turn to PI, PID could be just a headache. IMO the P-controler is what will suit your demand.

Figure 1. This is the Wheel Encoder Kit from DAGU, a simple add-on to any wheeled robot that can help measure the speed or distance the chassis travels. Each wheel encoder kit consists of two neodymium 8-pole magnets with rubber hubs and two hall-effect sensors terminated with 150mm cables and 3-pin female servo headers. These wheel encoders require a supply voltage of 3-24V with a supply current of 4mA. Source: Sparkfun.

The encoder has 8 poles. The encoder comes with two sensors so by putting these an odd multiple of 360/16 = 22.5° apart we can double the number of pulses and double the encoder resolution from 8 to 16 pulses per revolution.

simulate this circuit – Schematic created using CircuitLab

Figure 2. Using two sensors we can double the resolution.

simulate this circuit

Figure 3. Getting 32 pulses per revolution is possible using two sensors and both rising and falling edge detection.

By offsetting the sensors an odd multiple of 360/32 = 11.25° apart you could sense 32 encoder edges per revolution. This will take two micro inputs per encoder and a little debouncing and edge detection code but should not be too difficult.

This may not solve all your problems but it should make them at least four times easier.