Wednesday, October 3, 2012

Triple Axis Magnetometer (e.g. Compass)

If one's 'bot is going to know where it is going to need to know in what direction it is traveling.   Enter the HMC5883L Triple Axis Magnetometer.  I am not sure why I got this particular device as all I need is one axis!   In any case it is another cool little device if only I could get it to work properly.   More on that later.

The device has four connection points, two of which are for 3.3v power.  I am using the power from the Arduino though I could have used it from the Explorer PCB.   The other two connection points are for the "I2C / TWI" interface used by the compass.  This is a serial type of interface that can have multiple devices like HMC5883L on the same circuit connected back to the Arduino.   The two connections are labeled SCL (clock) and SDA (data) on both the compass and on the Arduino.

Once the compass is connected you need to use the "Wire" interface to talk to it.   I scoured the internet for sample code and pulled the below from it for my purposes.   Note that what is shown is an excerpt from my  Python to Arduino Interface library so it will not run as shown but still gives you the idea.

There is a really good article on the Love Electronics website that discusses this device in detail.

Now a little about my current issue with this device...I can't get it to act like a sane compass.   You would think that west and east (90 and 180) would be on the same axis but this does not seem to be the case.   I can deal with the advertized accuracy of the compass (+/- 2 degrees) but I am seeing deviations from actual compass bearings of dozens of degrees.   At times it even flips by 90 or more degrees.

I am not sure that I can blame the device though.  My wife and I are living in a temporary rental while we navigate the painful English house buying process.  The place we are in is really quite nice and sits right on the Thames at Maidenhead.   Right on the Thames and next to the famous 'sounding arches' railroad bridge built by Brunel to host a major train line into London.    Major as in really busy and next to as in just about right under.    I am assuming the electromagnetic fields from the railroad are what is messing with my compass though I have not taken it afield to prove this.

    // I2C Arduino Library for a compass
    #include <Wire.h>
    // Reference the HMC5883L Compass Library
    #include <HMC5883L.h>
    // Store our compass as a variable.
    HMC5883L compass;
    int compassError = FALSE;


    // ***** Initialize Compass
    case 'I':
        outputLine = "0,";
        Wire.begin();
        compass = HMC5883L();
        compassError = compass.SetScale(1.3);
        if (compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        compassError = compass.SetMeasurementMode(Measurement_Continuous);
        if(compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;

    // ***** Compass bearing
    case 'B':
        xSum = 0;
        MagnetometerRaw raw = compass.ReadRawAxis();
        MagnetometerScaled scaled = compass.ReadScaledAxis();
        int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)
        float heading = atan2(scaled.YAxis, scaled.XAxis);
        // Declination for London should be 24.73
        float declinationAngle = 24.73/1000;
        heading += declinationAngle;
        if(heading < 0)
            heading += 2*PI;
        if(heading > 2*PI)
            heading -= 2*PI;
        float headingDegrees = heading * 180/M_PI;

        int tempHeadingDegrees = int(headingDegrees);
        int tempHeadingDegreesF = (headingDegrees - tempHeadingDegrees) * 100;
        String outHeadingDegrees = String(tempHeadingDegrees);
        outHeadingDegrees += ".";
        outHeadingDegrees += String(tempHeadingDegreesF);

        outputLine = "0,";
        outputLine += outHeadingDegrees;
        if (debugOn == TRUE) {
            outputLine += ",Compass reading";
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;


More on the compass story here.

No comments:

Post a Comment