measuring speed of the ActivityBot
What is the best way to calculate the speed of the ActivityBot?
My idea was to measure the time between two edges of the encoder.
This is my code:
The output is shown below, as you can see it deviates quite a bit.
I expect times around 15 milliseconds, is my method not accurate enough?
Maybe i can use the encoder function from the abdrive library, but it's really hard to understand with all the short variable names.
Does anyone have some tips for me?
My idea was to measure the time between two edges of the encoder.
This is my code:
#include "simpletools.h"
#include "abdrive.h"
#ifdef __PROPELLER_USE_XMM__
#define STACK_LONGS (1024+128+32+sizeof(_thread_state_t))/4
#else
#define STACK_LONGS (16+sizeof(_thread_state_t))/4
#endif
void calculateSpeed(void*);
volatile static unsigned int timeInBetween;
int main()
{
low(26);
low(27);
print("Starting\n");
unsigned int stack[STACK_LONGS + 25];
int cog = cogstart(&calculateSpeed, NULL, stack, sizeof(stack));
print("Started cog %d\n", cog);
drive_speed(64,64);
while(1)
{
print("dTime= %u\n", timeInBetween / (CLKFREQ/1000));
pause(100);
}
}
void calculateSpeed(void*)
{
unsigned int prevTime;
while(1)
{
prevTime = CNT; //Remember the current time
while(!input(14)); //wait when pin 14 is low
timeInBetween = CNT - prevTime; //Calculate the difference in time
while(input(14)); //wait when pin 14 is high
}
}
The output is shown below, as you can see it deviates quite a bit.
I expect times around 15 milliseconds, is my method not accurate enough?
dTime= 17 dTime= 15 dTime= 17 dTime= 15 dTime= 22 dTime= 18 dTime= 19 dTime= 23 dTime= 18 dTime= 17 dTime= 17 dTime= 15
Maybe i can use the encoder function from the abdrive library, but it's really hard to understand with all the short variable names.
Does anyone have some tips for me?

Comments
Can i just divide the "abd_dlca" or "abd_dlc" with the "td" variable to get the correct speed?
A further improvement is to calculate the average over multiple samples.
EDIT: How do i use the "interactive_development_mode" function? My xbee pins are 9 and 8.
This particular measurement is in ticks per second, because the sample is over 1/10 second, and the distance traveled is multiplied by 10. At constant speeds, a larger sample time will reduce measurement uncertainty, but it also takes longer to measure. Your code can also add details like waiting for a transition before starting the measurement to reduce measurement error.
Ah, so you found the hidden development tools. Uh, they're kinda broken. ...but I'll bet there's some arlodrive test code that'll work well for you if harvested and adjusted for the ActivityBot. Let me go hunt that down. Be back in a while...
Circuits, libraries, and test code examples for TV remote, XBee, and ActivityBot are here:
TV Remote
http://learn.parallax.com/propeller-c-simple-devices/ir-receiver-and-remoteXBee
http://learn.parallax.com/propeller-c-simple-protocols/full-duplex-serialActivityBot
http://learn.parallax.com/activitybotWhen they are all verified to work, try the code below.
Note: The comments in the code have more info on remote control buttons and the values in the XBee display.
/* XBee displays serial updates of calculated and traveled ticks with error info. XBee DO -> P9 IR receiver -> P10 Use XBee + USB adapter or XStick for PC connection. Set PC terminal to 9600 bps. Use 2 on TV remote to go 256 ticks forward at top speed Use 8 on TV remote to go 256 ticks backward at top speed CH+ -> forward, tap for slow, hold to go to full speed. CH- -> backward, tap for slow, hold to go to full speed. VOL- -> left rotate, tap for slow, hold to go to full speed. VOL+ -> right rotate, tap for slow, hold to go to full speed. Variables displayed by XBee: - i index - eL error Left (difference between calculated and measured ticks) - eL error Right - LSC Left Speed Calculated - LTC Left Ticks Calculated - LT Left Ticks (Measured) - RSC Right Speed Calculated - RTC Right Ticks Calcualted - RT Right Ticks (measured) */ #include "simpletools.h" #include "wavplayer.h" #include "sirc.h" #include "ping.h" #include "abdrive.h" #include "fdserial.h" int key; // Global var for remote key int ticksLeft, ticksLeftCalc, ticksRight, ticksRightCalc; int ticksLeftOld, ticksLeftCalcOld, ticksRightOld, ticksRightCalcOld; int t, dt, i = 0; volatile int running = 0; static int speedLeft, speedRight; fdserial *xbee; void gofwd(void *par); static int fstack[128]; void gobkwd(void *par); static int bstack[128]; void getTicks(); void displayTicks(); void drive_getSpeedCalc(int *left, int *right); int main() // Main - execution begins! { xbee = fdserial_open(9, 8, 0, 9600); drive_speed(0,0); // Start servos/encoders cog drive_setRampStep(10); // Set ramping at 10 ticks/sec per 20 ms sirc_setTimeout(50); // Remote timeout = 50 ms dt = CLKFREQ/10; t = CNT; while(1) // Outer loop { int button = sirc_button(10); // check for remote key press // Motion responses - if key pressed, set wheel speeds if(button == 2) { if(!running) cogstart(&gofwd, NULL, fstack, sizeof fstack); } if(button == 8) { if(!running) cogstart(&gobkwd, NULL, bstack, sizeof bstack); } if(button == CH_UP)drive_rampStep(128, 128); // Left turn if(button == CH_DN)drive_rampStep(-128, -128); // Right turn if(button == VOL_DN)drive_rampStep(-128, 128); // Left turn if(button == VOL_UP)drive_rampStep(128, -128); // Right turn if(button == MUTE)drive_rampStep(0, 0); // Stop if(button == ENTER) { getTicks(); displayTicks(); } if(CNT - t > dt) { t+=dt; i++; getTicks(); if ( ticksLeftCalc != ticksLeftCalcOld || ticksRightCalc != ticksRightCalcOld || ticksLeft != ticksLeftOld || ticksRight != ticksRightOld ) { displayTicks(); } } } } void gofwd(void *par) { running = 1; drive_goto(256, 256); running = 0; cogstop(cogid()); } void gobkwd(void *par) { running = 1; drive_goto(-256, -256); running = 0; cogstop(cogid()); } void getTicks(void) { ticksLeftOld = ticksLeft; ticksRightOld = ticksRight; ticksLeftCalcOld = ticksLeftCalc; ticksRightCalcOld = ticksRightCalc; drive_getTicks(&ticksLeft, &ticksRight); drive_getTicksCalc(&ticksLeftCalc, &ticksRightCalc); drive_getSpeedCalc(&speedLeft, &speedRight); } void displayTicks(void) { writeStr(xbee, "i = "); writeDec(xbee, i); writeStr(xbee, " eL = "); writeDec(xbee, ticksLeftCalc - ticksLeft); writeStr(xbee, " eR = "); writeDec(xbee, ticksRightCalc - ticksRight); writeStr(xbee, " LSC = "); writeDec(xbee, speedLeft); writeStr(xbee, " LTC = "); writeDec(xbee, ticksLeftCalc); writeStr(xbee, " LT = "); writeDec(xbee, ticksLeft); writeStr(xbee, " RSC = "); writeDec(xbee, speedRight); writeStr(xbee, " RTC = "); writeDec(xbee, ticksRightCalc); writeStr(xbee, " RT = "); writeDec(xbee, ticksRight); writeChar(xbee, '\r'); } volatile int abd_speedL; volatile int abd_speedR; void drive_getSpeedCalc(int *left, int *right) { *left = abd_speedL; *right = abd_speedR; }The calculated speed is the used as setpoint for the PID controller right?
With the following code i am trying to calculate the the time that has passed between two spokes, but for some reasons it fluctuates a lot.
void watch_for_ticks(void *par) { lastTimeL = CNT; deltaTimeL = 0; lastTimeR = CNT; deltaTimeR = 0; lastStateL = 0; lastStateR = 0; while(1) { if( input(14) != lastStateL ) //Left change { deltaTimeL = CNT - lastTimeL; lastTimeL = CNT; //print("deltaTimeL=%d lastStateL=%d\n", deltaTimeL, lastStateL); } if( input(15) != lastStateR ) //Right change { deltaTimeR = CNT - lastTimeR; lastTimeR = CNT; //print("deltaTimeR=%d, lastStateR=%d\n", deltaTimeR, lastStateR); } lastStateL = input(14); lastStateR = input(15); } }Then i print the result every second with the following line:
print("lastTimeL= %f ms, lastTimeR= %f ms\n", deltaTimeL / (CLKFREQ/1000.0), deltaTimeR / (CLKFREQ/1000.0)); //Time difference in msI think that updating lastStateL and lastStateR outside of the if statements is going to lead to incorrect measurements. After that gets fixed, the measured speed may still be somewhat error prone because the time that it takes a spoke to pass by vs a space to pass by will vary with the distance between the wheel and the sensor. If the wheel has any wobble in it, the spoke time might be 60% of a cycle, and the space time might only be 40% when the wheel is closer to the sensor. When it's further away, the times might be the other way around. So, it would probably also help to take a full cycle (time of spoke + space) or even the average of several cycles.
Check the speedMonitor function in this example. It keeps track of speed based on a cycle (spoke + space).
/* XBee displays serial updates of calculated and traveled ticks with error info. XBee DO -> P9 IR receiver -> P10 Use XBee + USB adapter or XStick for PC connection. Set PC terminal to 9600 bps. Use 2 on TV remote to go 256 ticks forward at top speed Use 8 on TV remote to go 256 ticks backward at top speed CH+ -> forward, tap for slow, hold to go to full speed. CH- -> backward, tap for slow, hold to go to full speed. VOL- -> left rotate, tap for slow, hold to go to full speed. VOL+ -> right rotate, tap for slow, hold to go to full speed. Variables displayed by XBee: - i index - eL error Left (difference between calculated and measured ticks) - eL error Right - LSC Left Speed Calculated - LTC Left Ticks Calculated - LT Left Ticks (Measured) - RSC Right Speed Calculated - RTC Right Ticks Calcualted - RT Right Ticks (measured) - tCycleL Left time for spoke and space to pass. - tCycleR Right time for spoke and space to pass. - vL Velocity left based on spoke + space time. - vR Velocity right based on spoke + space time. */ #include "simpletools.h" #include "wavplayer.h" #include "sirc.h" #include "ping.h" #include "abdrive.h" #include "fdserial.h" int key; // Global var for remote key int ticksLeft, ticksLeftCalc, ticksRight, ticksRightCalc; int ticksLeftOld, ticksLeftCalcOld, ticksRightOld, ticksRightCalcOld; int t, dt, i = 0; volatile int running = 0; static int speedLeft, speedRight; fdserial *xbee; void gofwd(void *par); static int fstack[128]; void gobkwd(void *par); static int bstack[128]; void speedMonitor(void *par); static int speedstack[128]; static volatile int tCycleL, tCycleR, vL, vR; void getTicks(); void displayTicks(); void drive_getSpeedCalc(int *left, int *right); int main() // Main - execution begins! { xbee = fdserial_open(9, 8, 0, 9600); drive_speed(0,0); // Start servos/encoders cog drive_setRampStep(10); // Set ramping at 10 ticks/sec per 20 ms sirc_setTimeout(50); // Remote timeout = 50 ms cogstart(&speedMonitor, NULL, speedstack, sizeof speedstack); dt = CLKFREQ/10; t = CNT; while(1) // Outer loop { int button = sirc_button(10); // check for remote key press // Motion responses - if key pressed, set wheel speeds if(button == 2) { if(!running) cogstart(&gofwd, NULL, fstack, sizeof fstack); } if(button == 8) { if(!running) cogstart(&gobkwd, NULL, bstack, sizeof bstack); } if(button == CH_UP)drive_rampStep(128, 128); // Left turn if(button == CH_DN)drive_rampStep(-128, -128); // Right turn if(button == VOL_DN)drive_rampStep(-128, 128); // Left turn if(button == VOL_UP)drive_rampStep(128, -128); // Right turn if(button == MUTE)drive_rampStep(0, 0); // Stop if(button == ENTER) { getTicks(); displayTicks(); } if(CNT - t > dt) { t+=dt; i++; getTicks(); if ( ticksLeftCalc != ticksLeftCalcOld || ticksRightCalc != ticksRightCalcOld || ticksLeft != ticksLeftOld || ticksRight != ticksRightOld ) { displayTicks(); } } } } void gofwd(void *par) { running = 1; drive_goto(256, 256); running = 0; cogstop(cogid()); } void gobkwd(void *par) { running = 1; drive_goto(-256, -256); running = 0; cogstop(cogid()); } void getTicks(void) { ticksLeftOld = ticksLeft; ticksRightOld = ticksRight; ticksLeftCalcOld = ticksLeftCalc; ticksRightCalcOld = ticksRightCalc; drive_getTicks(&ticksLeft, &ticksRight); drive_getTicksCalc(&ticksLeftCalc, &ticksRightCalc); drive_getSpeedCalc(&speedLeft, &speedRight); } void displayTicks(void) { writeStr(xbee, "i = "); writeDec(xbee, i); writeStr(xbee, " eL = "); writeDec(xbee, ticksLeftCalc - ticksLeft); writeStr(xbee, " eR = "); writeDec(xbee, ticksRightCalc - ticksRight); writeStr(xbee, " LSC = "); writeDec(xbee, speedLeft); writeStr(xbee, " LTC = "); writeDec(xbee, ticksLeftCalc); writeStr(xbee, " LT = "); writeDec(xbee, ticksLeft); writeStr(xbee, " RSC = "); writeDec(xbee, speedRight); writeStr(xbee, " RTC = "); writeDec(xbee, ticksRightCalc); writeStr(xbee, " RT = "); writeDec(xbee, ticksRight); writeChar(xbee, '\r'); writeStr(xbee, "tCycleL = "); writeDec(xbee, tCycleL); writeStr(xbee, " tCycleR = "); writeDec(xbee, tCycleR); writeStr(xbee, " vL = "); writeDec(xbee, vL); writeStr(xbee, " vR = "); writeDec(xbee, vR); writeStr(xbee, "\n\n"); } volatile int abd_speedL; volatile int abd_speedR; void drive_getSpeedCalc(int *left, int *right) { *left = abd_speedL; *right = abd_speedR; } void speedMonitor(void *par) { int xL, xR; drive_getTicks(&xL, &xR); int xLprev = xL; int xRprev = xR; int cntL = CNT; int cntR = CNT; int cntLprev = cntL; int cntRprev = cntR; while(1) { drive_getTicks(&xL, &xR); if(xL == xLprev + 2 || xL == xLprev - 2) { cntL = CNT; tCycleL = cntL - cntLprev; if(xLprev > xL) tCycleL = -tCycleL; cntLprev = cntL; vL = (CLKFREQ/tCycleL) * 2; xLprev = xL; } if(xR == xRprev + 2 || xR == xRprev - 2) { cntR = CNT; tCycleR = cntR - cntRprev; if(xRprev > xR) tCycleR = -tCycleR; cntRprev = cntR; vR = (CLKFREQ/tCycleR) * 2; xRprev = xR; } } }When i do drive_speed(50,50), it will vary from 48 to 52 (when printing every 100 ms). This accuracy will probably good enough for my application.