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