My Version of a HMC5883L Object
Duane Degn
Posts: 10,588
I've been meaning to post my version of a HMC5883L object for a while.
Every so often I see posts asking about how to use the raw reading from the sensor to compute heading. The only way I know how to do this is to use the trig arctangent function. Fortunately F32 has both an Atan method and an Atan2 method. The Atan2 method takes two parameters (y and x) and returns the angle (in radians) these two points make with the x axis.
The demo code attached to post #2 provides out such as this:
Both numbers have units of degrees. The "course" is based on the compass rose and not the normal Cartesian coordinate system.
I'm going to reserve a few posts to make it easier to add future updates to this object. Please hold any replies until I've made four posts.
Every so often I see posts asking about how to use the raw reading from the sensor to compute heading. The only way I know how to do this is to use the trig arctangent function. Fortunately F32 has both an Atan method and an Atan2 method. The Atan2 method takes two parameters (y and x) and returns the angle (in radians) these two points make with the x axis.
The demo code attached to post #2 provides out such as this:
course = -146.093 field angle from horizontal = 46.994
Both numbers have units of degrees. The "course" is based on the compass rose and not the normal Cartesian coordinate system.
I'm going to reserve a few posts to make it easier to add future updates to this object. Please hold any replies until I've made four posts.
Comments
Update May 23, 2014: Uploaded newest version "Hmc5883lDemo140523e". This is the version to try!
All features of the HMC5883L are accessible. The rate the sensor takes readings, the number of readings to internally average, the gain and the reading mode can all be set from the Parallax Serial Terminal.
See post #19 below for additional information and sample output.
16,19, 9, 9, 0 As of 140523.
I'm attempting to use a HMC5883L sensor with an aluminum wallet.
I built a four servo gadget with gimbals in a hope if could keep the sensor stationary while the servo assembly was rotated in various directions. Very limited success.
First version "CompassDemo140512b" uploaded May 12, 2014. (removed May 23, 2014 with 16 views)
Basic version without averaging "CompassBasicDemo140512b" uploaded May 122014.(removed May 23, 2014 with 19 views)
Versions with calibration corrections.
"CompassDemo140513a" uploaded May 13, 2014.(removed May 23, 2014 with 9 views)
"CompasBasicDemo140513c" uploaded May 13, 2014. (removed May 23, 2014 with 9 views)
I'm not convinced the calibration corrections do much good. These newer version may not be better than the previously uploaded version.
See discussion about calibration corrections below in post #10.
Edit: In the comments on both "140512b" versions, I state the I2C lines are actively driven. This is not correct. The I2C lines are controlled by setting the direction of the pins only. There must be pull-up resistors to pull the lines high when the pins are set as inputs. I'll fix these comments with the next revision.
This looks like it will allow me to do so fairly easily?
You should be able to figure out how far from vertical the rocket is is based on the magnetic field's vector. You'd need to know the orientation of the sensor inside the rocket and how far from vertical the field normally is in your area.
I was surprised to see the magnetic field lines are about 67 degrees from horizontal where I live. Here in Idaho, magnetic north is mostly down.
I can't think of an equation to let you figure out the rocket's angle off the top of my head, but I don't think it would be too hard to come up with one. Maybe a fellow forum members knows how to do this and will let us know what equation to use.
I was just playing around with the sensor to see if I could get each axis aligned with the field and I could but they magnitude of the field wasn't measured consistently across all the axes. On my sensor the x axis would read up to about 570 and the y axis would only go up to 460. The z axis seemed to be the most sensitive and would read about 580 when aligned with earth's magnetic field.
I'm betting to make best use of the sensor, it should be calibrated. Each axis should probably be scaled prior to using the values in the trig equations.
I'll probably try to figure out some sort of calibration procedure. I know one digital compass I purchased years ago, had a calibration mode. While in the calibration mode you were supposed to slowly rotate the compass about its z axis. I'm thinking I'll just have the user point each axes at the magnetic north one after the other and have them record the magnitude of the field. These magnitude readings could then be entered into the program as a calibration constant.
Haven't thought about this much, but couldn't one just determine the magnetic field angle on the pad before launch and use that as a starting point? (Again, not very well thought through)
I'm doing something similar with the accelerometer to determine which way up the altimeter is mounted in the rocket.
Do you know, I might just have a use for it... :-)
The way I determined these constants was to point an axis at magnetic north and try to get the other axes value as close to zero as possible. I then read the "raw field strength" from the terminal and used it as the max expected constant.
Anyone using the program will like need to change these values to match their sensors.
Here's the output from the two new versions.
The "raw" values are the same as previous versions but the "cooked" values have been scaled so the maximum value should be 1000.
The "normalized field strength" should be 1000. One thing I've noticed is these sensors are very sensitive to magnetic fields generated by electronics. After seeing how hard it is to get a clean reading in a room filled with electronics, I'm not sure this sort of calibration is really worth the effort.
I'm interested to hear what others think when comparing a "calibrated" reading vs an unmodified reading.
What I was trying to do was to have two magnetometers back-to-back and use the values from each to get to a 'nulled' value. It was an experiment as much as anything (rather than aiming at a specific need*). Your code made it so easy to do this.
These plots were the result of waving a small pair of side-cutting pliers, about 6" from the board.
The raw (x, y, z) data from each sensor:
The difference between x, y, z:
With a rolling-average of the difference subtracted
The reason I used two sensors was to try and increase the sensitivity. It still outputs a stream of values to the PST but it could easily go to an SD card, etc.,
* The alleged existence of a WWII tank under the field across my house starting the train of thought...
Many thanks Duane - That must be a candidate for the Obex!
I've thought about this a bit more and I think you're right. You just need to measure the initial field and subtract the initial angle from the current angle to find how far the rocket is tilted.
I added this method to find the initial field. (I added this coded to the code I'm using, it's not part of the attached code above.)
In hopes of getting a good initial value, I averaged multiple readings together. I was going to explain each parameter but I think the names are pretty self explanatory.
I added these constants which I use when calling the method.
You might want to change the value of "ALLOWED_MOVEMENT" from two degrees to a fraction of a degree. You can make fractional degrees by dividing the constant "Compass#PSEUDOREAL_MULTIPLIER" by the desired fractional value (for example use "Compass#PSEUDOREAL_MULTIPLIER / 2" for half a degree).
You probably should make sure the method returned a valid reading by checking it against "Compass#PSEUDO_CIRCLE" which is what the method returns if it is unable to get a good initial reading.
This assumes your vertical axis is the z axis. If you're using a different vertical axis, the "tilt" portion of the child object would need to be modified.
I wonder at what altitude the field lines start changing angle as the loop around to the other pole. Hmmm
But that's a bit specific for this object I suspect so I won't hijack
There's a bug in version "CompassDemo140513a" of the code above. The speed data isn't being accessed correctly from the parent object. The section of code effected by the bug is commented out in the posted version. I'll post a bug fix once I add a few additional features.
I've been having a lot of trouble trying to incorporate the HMC5883L sensor into my aluminum wallet project. In the past, whenever I've used the HMC5883L in a project, I keep the sensor at a distance from the other electronics. With this wallet project, the compass sensor is right up against a bunch of other electronic devices. The magnetic fields from the other electronics swamp the sensor and cause the values output to overflow the 12-bit limits.
From reading the datasheet, I've learned the gain of the sensor can be adjusted. I've hoping I can still get useful data from the sensor by changing the gain settings.
I seem to get multiple problems when reading the sensor. Sometimes I can overcome the problem by adjusting the gain but other times the raw values returned are all "-1".
The problem with all negative ones just seems to happen on the small Propeller board I'm using in the aluminum wallet. When I connect the sensor to a QuickStart board, I don't get the problem. I'm pretty sure this problem is some sort of connection problem since I get the same readings returned when the sensor is not connected to the Propeller.
I noticed the compass includes three ID registers which contain the characters "H43". I've added a method to read these characters (not yet posted) but I still want to add some code to compare the character read from the sensor with the characters expected so the program can use these registers to confirm the correct sensor is being used.
I've been learning there's a lot more to these sensors than just reading the raw data. Apparently there's some sort of calibration feature in the sensor. Hopefully I'll learn enough about all this to take advantage of all the sensor's capabilities.
Edit: BTW, the electronic device causing the most trouble for the sensor appears to be the piezo speaker I added to the project. I would really like to figure out a way of keeping the piezo speaker since it will warn me when the battery is getting low.
I haven't yet assembled my board, but am likely to run into similar problems (even though I removed the copper and followed supplier recommendations). I also have a piezo, as well as bluetooth and various other bits and pieces.
I'll see about writing a test program to cycle through each device and see what effect they have on the readings if it helps.
I'm using a small breakout board I purchased from ebay for a few dollars. I just checked and they didn't remove the copper on the bottom layer of the board.
The board I have from SparkFun has the copper removed. The SparkFun board doesn't bring the ready pin out. I was originally writing this object with the SparkFun board in mind but I think the driver could have better performance if I take advantage of the ready pin. As I think about this a bit, I suppose I don't really need the ready pin since the Propeller can just read the status register to see if the data is ready. I think the ready pin makes more sense for a microcontroller relying on interrupts.
The most recent version "Hmc5883lDemo140523e" lets one access all the features of the HMC58853L sensor.
Here's some sample output:
(The above output was active after selecting menu item "D".)
Theoretically the "normalized field strength should always be 1000. This value gets adjusted to compensate for the gain setting used.
The program will record the initial heading and tilt when the program starts up. To reset this "initial" state, use the "S" option.
All the menu choices can be selected with either upper or lower case letters.
Make sure and check out the "P" and "N" options. I believe these "biased" configurations can be used to calibrate the compass. For now I'm calibrating the compass by pointing each axis towards magnetic north and recording the magnitude of the field. I enter these readings into the constants "MAX_EXPECTED_RAW_X" through "MAX_EXPECTED_RAW_Z".
Now that the gain can be adjusted, you want to make sure you using the same gain setting when recording the "MAX_EXPECTED" values as you assign to the constant "GAIN_USED_FOR_EXPECTED_RAW".
I used the default gain index of 1 but the sensor is more sensitive with a gain index of zero. If any of you use a gain index of zero, make sure you don't get any overflow errors.
When the program starts up, it uses the default gain index of one but if there's overflow errors, the program will keep adjusting the gain until the overflow errors go away or maximum gain index is reached. The higher the gain index, the lower the sensitivity of the sensor.
I think this latest version is worthy of the OBEX. I'll likely submit it to the OBEX in the next few days unless someone finds a problem.
If any of you have a Propeller and a HMC5883L sensor, I hope you give this program a try. I don't claim the user interface is intuitive but if some aspect of the menu is unclear, I hope you let me know.
For those without a HMC5883L sensor, what are you waiting for? You can buy them for about $3 shipped from ebay. Get a couple.
Edit: Be warned. Setting the mode to 1 (single read) will pretty much stop the readings from the sensor. Mode 0 (continuous read) is presently the only useful mode setting used by the program.
It uses Kye's integer math object. While there's things about the demo code I don't like, I do like it doesn't require a cog to run the math co-processor (F32).
The demo code neglects to use the method "polarAngle" (aka ATAN2 or arctangent2) which will return an angle from 0 to 360. IMO, the demo code goes about computing this angle the hard way.
I'm hoping to combine the best of my object and Parallax's object to come up with an improvement on both.
One concern I have about using the integer math object is the heading is limited in precision to a single degree. I find with my current version I can get sub-degree precision on the heading and tilt values. I'm pretty sure I'll end up making a version using both types of math objects so the one best matching the needs of a particular application can be used.