David's dead reckoning code for the LVBots competition on March 6th. Uses the mbed LPC1768, DRV8835, QTR-3RC, and two DC motors with encoders.

Dependencies:   PololuEncoder Pacer mbed GeneralDebouncer

Files at this revision

API Documentation at this revision

Comitter:
DavidEGrayson
Date:
Sun Feb 23 23:49:58 2014 +0000
Parent:
12:835a4d24ae3b
Child:
14:c8cca3687e64
Commit message:
testReckoner routine works great! I can roll my robot around and it always knows what quadrant it is pointing to and knows what quadrant it is in!

Changed in this revision

main.h Show annotated file Show diff for this revision Revisions of this file
reckoner.cpp Show annotated file Show diff for this revision Revisions of this file
reckoner.h Show annotated file Show diff for this revision Revisions of this file
test.cpp Show annotated file Show diff for this revision Revisions of this file
test.h Show annotated file Show diff for this revision Revisions of this file
--- a/reckoner.cpp	Sun Feb 23 22:23:34 2014 +0000
+++ b/reckoner.cpp	Sun Feb 23 23:49:58 2014 +0000
@@ -2,21 +2,88 @@
 #include "reckoner.h"
 
 /**
-W: Wheel-to-wheel distance:              150 mm  // TODO: correct this
+First, we define two int32_t variables cos and sin that make a unit vector.
+These variables hold our current estimation of the direction the robot is pointed.
+We need to choose units for them that allow for a lot of accuracy without overflowing.
+We choose the units to be 1/(1 << 30) so that they will typically be about half of the
+way between 0 and overflow.
+The starting value ofvalue of the [cos, sin] will be [1 << 30, 0].
+To record this choice, we define LOG_UNIT_MAGNITUDE:
+**/
+#define LOG_UNIT_MAGNITUDE 30
+
+/**
+We define dA to be the effect of a single encoder tick on the angle the robot is
+facing, in radians.  This can be calculated from physical parameters of the robot.
+The value of dA will usually be less than 0.01.
+What we want to do to update cos and sin for a left-turning encoder tick is:
+
+  cos -= sin * dA    (floating point arithmetic)
+  sin += cos * dA    (floating point arithmetic)
+
+Instead of doing that we can do the equivalent using integer arithmetic.
+We define DA to be dA times (1 << LOG_UNIT_MAGNITUDE).
+Then we do:
+
+  cos -= ((int64_t)sin * DA) >> LOG_UNIT_MAGNITUDE;
+  sin += ((int64_t)cos * DA) >> LOG_UNIT_MAGNITUDE;
+**/
+
+/**
+We define Df to be the distance the robot moves forward per encoder tick.
+This is half of the amount a single wheel turns per encoder tick.
+Df is NOT an integer so we cannot store it in our program; it is a distance.
+We do not need to even know Df, because all of our distances can be
+measured and recorded in terms of Df.
+**/
+
+/**
+We define two int32_t variables x and y to hold the current position of the robot.
+Together, x and y are a vector that points from the starting point to the robot's
+current position.
+
+When choosing the units of x and y, we have several considerations:
+ * If the units are too big, precision is lost.
+ * If the units are too small, the integers will overflow.
+ * For convenience, the units should be a power of 2 off from Df (Distance robot moves
+   forward per encoder tick), so we can just write lines like:
+     x += cos >> some_constant
+
+Taking this into account, we choose the units of x and y to be Df / (1 << 14).
+
+Let's make sure this won't result in overflow:
+ * To ensure no overflow happens, we need:
+     (Maximum distance representable by x or y) > (Maximum roam of robot)
+                     (1 << 31) * Df / (1 << 14) > (Maximum roam of robot)
+                  (Maximum roam of robot) / Df  < (1 << 17) = 131072
+ * Assume Df is 0.1 mm (this is smaller than it will usually be for the robots we work with).
+ * That means the robot can roam at most 13.1 m (0.0001 m * 131072) from its starting point,
+   which should be plenty.
+
+So how do we update x and y?
+ * When we update x and y, the position they represent changes by Df.
+ * x and y represent a unit vector, but their units are 1 / (1 << LOG_UNIT_MAGNITUDE).
+ * If we wrote x += cos it would mean the units of x are Df / (1 << LOG_UNIT_MAGNITUDE).
+   (and x would overflow after 2 or 3 ticks).
+ * Instead, we write x += cos >> 16 so the units are correct.
+**/
+
+
+
+/**
+W: Wheel-to-wheel distance:              139 mm  // Measured with calipers.
 G: Gear ratio factor:                    30
 T: Encoder ticks per backshaft rotation: 12       (three-toothed encoder wheel)
-R: Wheel radius:                         70 mm
+R: Wheel radius:                         35 mm    (Pololu 70mm diameter wheel)
 
 Dw: Distance wheel turns per encoder tick = 2*pi*R/(G*T)
 Df: Distance robot moves forward per encoder tick = Dw/2
-dA: Change in angle per encoder tick = Dw/W = 2*pi*70/(30*12) / 150 = 0.00814486984
+dA: Change in angle per encoder tick = Dw/W = 2*pi*35/(30*12) / 150 = 0.00439471394
 **/
 
-#define LOG_UNIT_MAGNITUDE 30
+#define DA 4718788  // 0.00439471394 * 0x40000000
 
-#define DA 8745487  // 0.00814486984 * 0x40000000
-
-#define LOG_COS_TO_X_CONVERSION  14    // 30 - 16
+#define LOG_COS_TO_X_CONVERSION  16    // 30 - 14
 
 Reckoner reckoner;
 
--- a/reckoner.h	Sun Feb 23 22:23:34 2014 +0000
+++ b/reckoner.h	Sun Feb 23 23:49:58 2014 +0000
@@ -1,7 +1,3 @@
-// Robot configuration:
-
-// General purpose code:
-
 class Reckoner
 {
     public:
@@ -9,28 +5,15 @@
     Reckoner();
     
     // Together, cos and sin form a vector with a magnitude close to 2^30 that indicates
-    // the current position of the robot.  By definition, when the reckoning starts,
-    // sin is 0 and cos is 2^30.
+    // the current position of the robot.
     // cos corresponds to the x component of the orientation vector.
     // sin corresponds to the y component of the orientation vector.
     int32_t cos, sin;
     
     // Together, x and y are a vector that points from the starting point to the
     // robot's current position.
-    // Units:
-    // * If the units are too big, precision is lost.
-    // * If they are too small, the integers wil overflow.
-    // * For convenience, they should be a power of 2 off from Df (Distance robot moves
-    //   forward per encoder tick), so we can just write x += cos >> some_constant.
-    // * Worst case for overflow: our encoders give us a Df of 0.25mm and our robot moves
-    //   5m away from the starting point.  Therefore, x and y might need to hold a value
-    //   20000 or -20000 without overflowing.
-    //   Overflow condition:              x = (1<<31)
-    //                20000*Df < max_dist_x = (1<<31) * U
-    //                                U > Df * (20000) / (1<<31) = Df / (1<<16)
-    // * Therefore the units we choose for x are Df / (1<<16).
-    // * If we wrote x += cos it would mean the units are Df / (1<<30).
-    //   Instead, we write x += cos >> 14 so the units are correct.
+    // The untis are Df / (1<<14), where Df is the distance the robot moves forward
+    // or backwards due to a single encoder tick.
     int32_t x, y;
 
     void handleTickLeftForward();
--- a/test.cpp	Sun Feb 23 22:23:34 2014 +0000
+++ b/test.cpp	Sun Feb 23 23:49:58 2014 +0000
@@ -15,14 +15,21 @@
 
 void testReckoner()
 {
+    Pacer reportPacer(100000);
     while(1)
     {
         updateReckonerFromEncoders();
-        led1 = (reckoner.x > 0);
-        led2 = (reckoner.y > 0);
-        led3 = (reckoner.cos > 0);
-        led4 = (reckoner.sin > 0);
+        led1 = (reckoner.cos > 0);
+        led2 = (reckoner.sin > 0);
+        led3 = (reckoner.x > 0);
+        led4 = (reckoner.y > 0);
         
+        if (reportPacer.pace())
+        {
+            pc.printf("%11d %11d %11d %11d | %8d %8d \n",
+              reckoner.cos, reckoner.sin, reckoner.x, reckoner.y,
+              encoderLeft.getCount(), encoderRight.getCount());
+        }
     }
 }
 
--- a/test.h	Sun Feb 23 22:23:34 2014 +0000
+++ b/test.h	Sun Feb 23 23:49:58 2014 +0000
@@ -6,4 +6,7 @@
 void __attribute__((noreturn)) testReckoner();
 
 
+
+// These are actually defined in main.h:
+
 void updateReckonerFromEncoders();
\ No newline at end of file