Greetings,
I am working to design a robot that maps out relative heights on a circle approximately 4 feet in diameter. I'm using the FXOS8700CQ mounted on a small arm. The arm rotates ~360 degrees, and a small ball caster on the end of the arm allows it to pivot up and down with the changing height on the surface. We want to record the pitch of the arm from the FXOS8700CQ approximately every 3 degrees of rotation, to get roughly 120 readings.
We are using the sensor fusion library to filter the results from the FXOS8700CQ. This is returning more stable results that previous filters we've tried, but we're not quite getting the results we need yet. The filtered results are smooth, but they are moving (falling) without the robot moving (mostly the compass).
I believe this is due to the sensor not being properly calibrated. Previously we'd been using MotionCal software to calibrate the sensor (hard- and soft-iron matrices), but those values don't appear to work correctly with the fusion library. We want to use the Sensor Fusion Toolbox for calibration.
The issue here is that we're not using an NXP MCU, we're using an ESP32. We're also not writing the calibration matrices to the EEPROM, I'm simply writing them into the cal[] array in NXPMotionSense.cpp, like so:
cal[0] = 0; //accelerometer offsets
cal[1] = 0;
cal[2] = 0;
cal[3] = 0; //zero-drift for gyro
cal[4] = 0;
cal[5] = 0;
cal[6] = 30.32; //hard-iron offsets
cal[7] = 7.19;
cal[8] = 72.75;
cal[9] = 45.74; //magnetic field
cal[10] = 0.988; //soft-iron map
cal[11] = 0.951;
cal[12] = 1.068;
cal[13] = 0.023;
cal[14] = 0.010;
cal[15] = -0.050;
Here is my question. I saw something about using Serial UART to communicate with the Sensor Fusion Toolbox if we were using something other than a NXP MCU. However, I was unable to figure out how to select a serial COM port, or what values the Sensor Fusion Toolbox expected from the sensor in order to perform a calibration. Could anyone point me in the right direction?
已解决! 转到解答。
We're using the FXOS8700 + FXAS21002 9-DOF module. I like the proposal of using the gyro for greater pitch accuracy. Is there a way to tell SensorFusion to ignore the compass? It looks like FQWINITDD_9DOF_GBY_KALMAN modifies the impact the compass has, but I'm not sure this is what I'm looking for to modify.
Evan,
There are a number of issues to discuss here:
Here is an extract from a previous blog posting I wrote on the topic of magnetic interference:
Consider impacts on magnetic field resulting from wires and traces on your PCB. We can use the Biot-Savart law to estimate the effect of wire and trace currents on sensor readings. For long wires and traces, Biot-Savart can be simplified to:
where:
This can be re-arranged to:
where:
If you know the field magnitude you can afford to ignore and the distance from your sensor to trace/wire, then you can use Equation 2 to calculate the maximum wire/trace current in mA.
Hopefully all of the above will give you some ideas of where to start.
Regards,
Mike
Mike,
Firstly, thank you for such a prompt and well-detailed reply.
The fact that the NXP MCUs do the calibration makes sense--I actually don't feel like this is our biggest issue. I've used MotionCal to obtain what seem like appropriate soft- and hard-iron matrix coefficients.
The robot was indeed built to address the issue of producing adverse fields that could affect the magnetometer. The sensor is mounted on an all-aluminum arm about half a meter away from the body of the robot. The main body is ABS plastic with a single servo, battery pack, and ESP32. The arm with the sensor does rotate continuously, however, the base remains stationary and the sensor, robot body (including servo and batteries) and arm all rotate as a single unit. This way, any field produced from the robot remains constant to the sensor; the sensor's position does not change in relation to this field.
By now, I have gotten the compass (getHeading()) to function very nicely. The issue is with measuring the pitch. For our orientation, the pitch of the arm is measured from (-1) * getRoll(). When the arm is stationary, the values are quite good. However, once it begins rotating, the pitch tends to drift up or down, even while the actual pitch isn't changing.
I've read as many docs as I can and have a general understanding of how SensorFusion.cpp works, and the difficulty in decoupling these values due to how they are calculated from accelerometer and gyro inputs. I realize we might not be able to fully decouple the heading/roll, but do you have any tips for anything that could help me? We are assuming a constant rotational velocity of the arm (in the x-y plane), with gentle fluctuations in the pitch (z-direction).
EDIT: One clarifying detail, the pitch will never exceed +/- 5 degrees.
Evan,
It sounds like a fun project and that you've done the right things in your robot design. Since you're happy now with the compass heading, let's focus on pitch. I can't tell from the thread above if your system includes a gyro or not. Or are you using the six-axis eCompass algorithm? The latter includes a low-pass filter that will throw off dynamic measurements. Also, errors in magnetic calibration can easily swamp out the accuracy you are looking for in tilt.
If you have a gyro in your system, I would suggest also running the 6-axis Kalman filter for tilt. You'll get more accurate results by excluding the magnetometer. We commonly run all six algorithms in parallel in our standard demos, and there's no reason you couldn't run both in order to optimize your precision.
If you don't have a gyro, you might want to experiment with the low pass filter timing. See parameter FLPFSECS_3DOF_B_BASIC in filename fusion.h.
Regards,
Mike
We're using the FXOS8700 + FXAS21002 9-DOF module. I like the proposal of using the gyro for greater pitch accuracy. Is there a way to tell SensorFusion to ignore the compass? It looks like FQWINITDD_9DOF_GBY_KALMAN modifies the impact the compass has, but I'm not sure this is what I'm looking for to modify.
Evan,
Take a look at function fFuseSensors() in fusion.c. You'll see we have 6 separate algorithms running. You just need to read the values from the appropriate data structure. See Section 4.9 of the user guide.
Regards,
Mike