Saturday, March 3, 2012

Using the MMA7361 Accelerometer with the Arduino UNO 3 (Part I)

Now that I have coded up a basic LED circuit, I thought I would move on to working with sensors that will measure robot location. Remember the goal of the first experiment is to build a small robot that can use dead reckoning to move through an environment. This is the precursor to developing a SLAM system as you need an initial estimate of object location to put the sensor measurements into a consistent coordinate reference frame. Subsequent processing is used to align the measurements in front of the vehicle over time to correct for errors in inertial sensor measurements.

The obvious first sensor to use for localization is an accelerometer. Accelerometers measure weight per unit of (test) mass along several different axis. This can be converted into a heading direction by integrating the observed values over time (acceleration is the derivative of velocity). Since accelerometers do not measure changes in position directly (but rather the derivative) they tend to produce noisy measurements which must be filtered or smoothed. There seems to be a lot of discussion out there on how best to do this on the Arduino (and whether a simple FIR filter or a Kalman filter is needed). I have decided to side step this issue for now. My first step is to get some measurements and look at just how noisy is the data that is coming back from the sensor. I will do this for several other sensors (gyroscope and magnetometer) and figure out how to combine measurements once all the sensors are characterized.

There lots of good resources out there for accelerometers and the Arduino. Here are a few that I have found useful:
Some of these provide topics that will come up later once the basic accelerometer measurements are made. I decided to make a separate class that will be responsible for interacting with the accelerometer and providing out the basic measurements. This will allow a re-usable component for later on when the magnetometer and gyroscope are introduced. First up, we need the parts list. Not too many things are needed here for this project:
  1. Protoype shield for the Arduino
  2. Arduino Uno R3
  3. Jumper wires, colored LED, push button, and several resistors
  4. MMA7361 Accelerometer
I picked the MMA7361 just based on its low cost (14 dollars) and its availability on amazon prime for free shipping. It has a nice advantage that it takes 5v directly and does not need any resistors/capacitors to be hooked up. When I got the board, it does not seem to fit my breadboard very well. It needs a breadboard that has 12 holes across. These do not seem to exist, so I am going to just wire it with jumpers for now and figure out how to mount it better later. Clearly this will not be an issue when I move to rigidly mounting this on a PCB, but for now, it is just kind of a pain. Here is how I wired this up:

The dataset says the sleep pin must be high for the part to work. Next I downloaded what code was available from the virtuabotix web site and fired up Eclipse. The code for this will again be going on my github site. This site is located at this site. For simplicity, you can grab the code by cloning the entire repository using the command:
      git clone
The code for this example is in sensors/accelerometer. I will dive into the code in more detail but first, here is a picture of the actual wiring:

Given that there are going to be lots of pins in use, I figured I would write an abstract class that would help make sure that the pin parameters are a little more explicit (as opposed to just being a bunch of ints that do not imply the pin mapping very well). I therefore created a class in common/Pin.h that has a constructor to set the pin number and a cast operator to get it back again:
inline Pin(int iId);operator int () const;
 Now the accelerometer class uses typedefs to give a better indication of the parameter ordering:
typedef common::Pin X;typedef common::Pin Y;typedef common::Pin Z;typedef common::Pin S;void setup(const X&x, const Y&y, const Z&z, const S&s);void update();float x() const;float y() const;float z() const;
So you initialize the class in the setup() method and call update() in loop() to read out and store the values. The big question is how to read out the values and convert them to something meaningful. I started with the discussion at On that blog, the equations for mapping from the analog to digital are given as:
Rx = (AdcRx * Vref / 1023 – VzeroG) / Sensitivity
Ry = (AdcRy * Vref / 1023 – VzeroG) / Sensitivity
Rz = (AdcRz * Vref / 1023 – VzeroG) / Sensitivity
For the moment, just ignore what the variables mean other than the AdcR vector is the analog to digital that is read using analogRead and the R vector is the direction the accelerometer is pointing. After this conversion, the vector R is converted to a unit vector. Converting to a unit vector causes the multiplicative scale values to be irrelevant (they are normalized away when all vector elements are multiplied by the same value). We can therefore re-write this equation as:

Rx = (AdcRx - T) * S
Ry = (AdcRy - T) * S
Rz = (AdcRz 
- T) * S

S = Vref / 1023 / Sensitivity
T = VzeroG * 1023 / Vref
and since we are normalizing R, the S can be discarded (meaning sensitivity only affects the magnitude of the vector which we are discarding). The translation offset can be computed by looking at the data sheet for the device. When I plugged in the data sheet values I did not get the exact values I was expecting. I am guessing this is due to error sources and slight differences in location on the earth (affecting gravity) and manufacturing. Therefore, I set the accelerometer on several axis and observed the readings. I then used these to estimate the T values. This is poor way of doing an initial calibration. I might write a calibration routine later once I have a moving robot (i.e., drive in circles to estimate the bias).

I am now embarking on a way to plot these values and visualize the orientation so I can validate the measurements in a qualitative way (and maybe even some quantitate analysis as well).

1 comment:

  1. i was also bothered by the fact that the accelerometer board has such a wide spacing until i realized that you could use jumper wires from under the board to another location. no need to use an external solderless breadboard.