With library release 1.0.5.4b, Sparki still does not drive the distance I specify or turn the angle I want: distances and angles both fall short, although angles are closer than distance.
When I unleash Sparki on a group of middle school students, I want to start by having them draw some geometric shapes (e.g. a square or a five-pointed star). Once that’s accomplished, I may want them to navigate a maze by hard-coding the required distances and angles. (Solving the maze with sensors will come later.) Obviously, none of this will work if Sparki does not drive distances or turn angles as advertised.
I’m disappointed that this basic function is not working correctly, especially after the 1.0.5.4 release, which includes the release note “turning by degrees is now accurate”. How was this tested? Just have Sparki draw a 10cm-per-side square, and it will be obvious that it’s not working.
All right… Now that I’ve complained, here’s the solution:
The magic numbers in moveForward(), moveBackward(), moveRight() and moveLeft() within the Sparki.cpp library need to be changed.
The 222.222222 in this code (and moveBackward):
void SparkiClass::moveForward(float cm)
{
float run = 222.222222*cm;
if( (cm == -1) || (cm == 0) ){
moveForward();
}
else{
moveForward();
delay(run);
moveStop();
}
}
needs to be 306.775834.
Similarly the 21.38888 here (and in moveLeft):
void SparkiClass::moveRight(float deg)
{
float turn = 21.388888*deg;
if( (deg == -1) || (deg == 0) ){
moveRight();
}
else{
moveRight();
delay(long(turn));
moveStop();
}
}
needs to be 22.755556.
With these numbers, my 10cm by 10cm square start to look pretty good. It’s still off a little bit, in part, I think, due to the error I wrote about in this post: [url]Stepper Motor Spec: Steps per Revolution?]
If you’re wondering where the numbers are coming from:
[code]wheel_diameter = 51 mm (my measured value)
wheel_circumference = 51 mm * pi = 160.2212 mm
distance_per_wheel_revolution = wheel_circumference = 160.2212 mm / rev
target_distance = 10 mm
(10mm or 1 cm is the baseline distance of the move functions)
target_revolutions = target_distance / distance_per_wheel_revolution
= 10 mm / ( 160.2212 mm / rev )
= 0.06241 rev
motor_steps_per_revolution = 4096 steps / rev
(again, see this post: Stepper Motor Spec: Steps per Revolution?)
target_steps = motor_steps_per_revolution * target_revolutions
= 4096 steps / rev * 0.06241 rev
= 255.6465 steps
base_time_per_step = 200 us = 0.2 ms
(see this post: Interrupt rate is not as advertised)
speed_scalar = 6
(another magic number; see below)
target_run_time = target_steps * base_time_per_step * speed_scalar
= 255.6465 steps * 0.2 ms * 6
= 306.7758 ms[/code]
Or, by carrying a few more digits with the help of Microsoft Excel, I get 306.775834 ms per centimer.
After I did the above calculation with my measured wheel diameter of 51mm, I saw in the Sparki.h file that the wheel diameter is defined there as 51.5mm. If I plug that in, the new answer is 303.7974277 ms per centimeter (and 22.534628 ms per degree).
The scale factor for angles is a continuation of this math, given that the wheel-to-wheel spacing is 85mm. That 85mm seems to be a good number, but your mileage may vary. I’ve measured two Sparkis, and the spacing actually varies around the 85mm. For folks who really need accurate angles, maybe the library could implement a function that takes the actual wheel separation (as measured by the Sparki owner) and then applies an appropriate scale factor?
With regard to the magic speed_scalar of 6, this number comes from
in motorRotate() and from this code
if( speedCounter[motor] == 0) { //
step_index[motor] += step_dir[motor];
remainingSteps[motor]--;
speedCounter[motor] = speedCount[motor];
}
else{
speedCounter[motor] = speedCounter[motor]-1;
}
in the interrupt service routine. I suspect that the speed_scalar was actually supposed to be 5, but it works out to 6. I find this code unnecessarily confusing.
Overall, I have to ask: Why use this time or delay-based approach to moving specific distances or angles?
The distance-to-delay and angle-to-delay scale factors for the moveForward/Backward and moveLeft/Right functions are dependent on the interrupt rate and the speed setting. If either of those changes, then the scale factors need to be fixed again. Why not convert the target distance into required motor steps and then just drive that number of steps??? In other words, I think the moveForward/Backward and moveLeft/Right functions should be leveraging motorsRotateSteps() instead of doing this delay-based stuff.