been working on adding an encoder to a model railroad turntable to control position and looking at approaches for controlling motor speed to stop close to the desired position.
considering a calibration sweep that measures distance traveled when a motor is stopped, braked and reversed.
an isr is used to process encoder events. it updates position, captured timestamps and the changes in timestamps (dt ~= speed).
the calibration code has three options: off, braking and reversed. for each, it turns the motor on for 300 msec, then stops the motor based on the option and finally waits for the motor to stop turning when it sees two consecutive equal time stamps from the isr.
the cal code captures the position, timestamp and dt when it starts the motor, applies the option to stop the motor and when it finally stops.
for option one, it simply sets the motor speed (PWM) to zero. the following output shows the results. the 3 measurements are shown, reporting timestamp, dt from the isr and the position.
calRate reports deltas between the 2nd and 3rd measurement. calRate dt is the delta between msec values and rate is difference in pos * 100 / difference in mses.
a quick answer is dPos, 1282, the distance traveled before stopping
calStart: 1
calTimes: 0 msec 4529, dUsec 0, pos 0
calTimes: 1 msec 4830, dUsec 196, pos 1242
calTimes: 2 msec 5350, dUsec 11124, pos 2524
calRate: dPos 1282 dt 520 rate 246 coast
the 2nd option is to brake in addition to turning the motor off. my understanding of braking is to create a short path across the motor leads. i set the h-bridge to connect both motor leads to ground.
i see no difference by doing braking, dPos 1300 vs 1282 above
2
calStart: 1
calTimes: 0 msec 7303, dUsec 11124, pos 2524
calTimes: 1 msec 7604, dUsec 200, pos 3749
calTimes: 2 msec 8164, dUsec 6464, pos 5049
calRate: dPos 1300 dt 560 rate 232 brake
and to verify the measurement, as well as trying something else, the 3rd option is to reverse the motor. this requires an additional step which is to set the speed to zero just before stopping. The code does this when the isr dt (difference in timestamps) is > 1msec, 1000 usec. for an encoder with 30 vanes which is 1800 rpm.
as expected, the motor stopped much quicker, 69 steps compared to 1282 above.
3
calStart: 1
calTimes: 0 msec 11274, dUsec 6464, pos 5049
calTimes: 1 msec 11575, dUsec 204, pos 6286
calTimes: 2 msec 11674, dUsec 9476, pos 6355
calTimes: 3 msec 11594, dUsec 5900, pos 6366
calRate: dPos 69 dt 99 rate 69 reverse
i'm surprised how effecting reversing the motor is, but don't understand why braking didn't have the expected affect.
what don't i understand?
// -------------------------------------
// just rising edge of B and check A
static unsigned long isrMsec = 0;
static unsigned long isrUsec = 0;
static unsigned long isrDtUsec = 0;
static long pos = 0;
static long pos1 = 0;
static int capture = 1;
void
isr (void)
{
byte encA = (digitalRead(PIN_E_A));
byte encB = (digitalRead(PIN_E_B));
unsigned long usec = micros ();
unsigned long msec = millis ();
isrDtUsec = usec - isrUsec;
isrUsec = usec;
isrMsec = msec;
pos1 = pos;
if (encA) {
pos++;
dir = 1;
}
else {
pos--;
dir = -1;
}
enc = encB << 1 | encA;
// -----------------------------------------------
// initial debug
// if (debug || (C_RUN == cond1 && C_STOP == cond)) {
if (debug) {
Serial.print ("# isr: enc ");
Serial.print (enc);
Serial.print (", encLast ");
Serial.print (encLast);
Serial.print (", pos ");
Serial.println (pos);
}
cond1 = cond;
// -----------------------------------------------
if (capture) {
// buf [bufIdx++] = micros() & 0xFFFF;
buf [bufIdx++] = usec & 0xFFFF;
buf [bufIdx++] = pos;
buf [bufIdx++] = hPwm;
buf [bufIdx++] = enc << 4 | hDir;
bufIdx = BUF_SIZE <= bufIdx ? 0 : bufIdx;
}
}
// ---------------------------------------------------------
typedef enum {
CAL_NONE,
CAL_COAST_RUNUP,
CAL_COAST,
CAL_BRAKE_RUNUP,
CAL_BRAKE,
CAL_DONE,
CAL_ERR,
} CalSt_t;
CalSt_t calSt = CAL_NONE;
typedef struct {
unsigned long msec;
long pos;
long dt;
} CalDat_t;
const char* optStr [] = { "", " coast", " brake", " reverse", " unk", " unk" };
int calOpt = 0;
#define CalDatSize 6
CalDat_t calDat [CalDatSize] = {};
// -------------------------------------
void
calRate (
int idx,
const char* lbl)
{
unsigned long dt = calDat [idx+2].msec - calDat [idx+1].msec;
unsigned long dPos = calDat [idx+2].pos - calDat [idx+1].pos;
Serial.print ("calRate: ");
Serial.print (" dPos ");
Serial.print (dPos);
Serial.print (" dt ");
Serial.print (dt);
Serial.print (" rate ");
Serial.print (100 * dPos / dt);
Serial.println (lbl);
}
// -------------------------------------
void
calTimes (
int opt )
{
for (int i = 0; i < CalDatSize; i++) {
Serial.print (" calTimes: ");
Serial.print (i);
Serial.print (" msec ");
Serial.print (calDat [i].msec);
Serial.print (", dUsec ");
Serial.print (calDat [i].dt);
Serial.print (", pos ");
Serial.print (calDat [i].pos);
Serial.print ("\n");
}
calRate (0, optStr [opt]);
// calRate (3, " brake");
}
// -------------------------------------
unsigned long calMsec = 0;
bool
calStopped (void)
{
delay (20);
noInterrupts ();
unsigned long msec = isrMsec;
interrupts ();
bool res = false;
if (calMsec) {
res = calMsec == msec;
}
calMsec = msec;
return res;
}
// -------------------------------------
void
calDatUpdate (
int idx,
unsigned long msec )
{
calDat [idx].msec = msec;
noInterrupts ();
calDat [idx].pos = pos;
calDat [idx].dt = isrDtUsec;
interrupts ();
}
// -------------------------------------
void
calStart (
CalSt_t st,
unsigned long msec,
int idx )
{
setDir (D_CW);
SetSpeed (H_MAX);
calSt = st;
calMsec = 0;
calDatUpdate (idx, msec);
Serial.print ("calStart: ");
Serial.print (calSt);
Serial.print ("\n");
}
// -------------------------------------
#define MIN_RUNUP 300
CalSt_t
cal (void)
{
unsigned long msec = millis ();
switch (calSt) {
case CAL_DONE:
case CAL_NONE:
calStart (CAL_COAST_RUNUP, msec, 0);
break;
case CAL_COAST_RUNUP:
if (msec - calDat [0].msec > MIN_RUNUP) {
calDatUpdate (1, msec);
switch (calOpt) {
case 3:
#if 0
SetSpeed (200);
setDir(D_CCW);
#else
Reverse()
#endif
break;
case 2:
SetSpeed (0);
setDir(D_SHORT);
break;
case 1:
SetSpeed (0);
break;
case 0:
default:
break;
}
calSt = CAL_COAST;
}
break;
case CAL_COAST:
if (calStopped()) {
calDatUpdate (2, msec);
calTimes (calOpt);
calSt = CAL_DONE;
calOpt = 0;
}
else if (3 == calOpt) {
noInterrupts ();
long dtUsec= isrDtUsec;
interrupts ();
if (1000 < dtUsec && 0 < hPwm) {
setSpeed (0);
calDatUpdate (3, msec);
}
}
break;
case CAL_ERR:
break;
default:
calSt = CAL_ERR;
break;
}
return calSt;
}