# remove gravity from acceleration on Myo armband IMU measurements

I'm working with the Myo armband through the myo_ros package. The device is able to provide IMU measurements comprising orientation, linear acceleration and angular velocity. The orientation is expressed wrt an (unknown) reference frame, chosen when the device is turned on. We can refer to this frame as myo_ref. The linear acceleration is expressed wrt the myo_raw frame, which is a north-west-up moving frame attached to the device. I want to manually remove the gravity component from the accelerometer data. These are the steps I'm doing:

• calibration: I record the orientation when the accelerometer measure +9.81 over the z-axis (so I'm sure the device is aligned with the Earth's z-axis pointing upward). This orientation, let's call it q_ref2aligned is used to publish a static transformation, describing the new frame myo_aligned wrt the frame myo_ref;
• each IMU measurement has an orientation, let's call it q_ref2raw which expresses the current pose of the armband wrt the frame myo_ref
• To the best of my knowledge, the inverse quaternion of q_ref2aligned, that is q_aligned2ref, describes the transformation from the frame myo_aligned to the frame myo_ref
• q_aligned2ref * q_ref2raw = q_aligned2raw should represent the current orientation of the armband wrt the frame aligned with the Earth's z-axis, right?
• if lin_acc is the acceleration recorded in the current IMU measurement (so wrt the myo_raw frame) and G = [0, 0, 9.81] is the gravity vector, if I multiply lin_acc by q_aligned2raw and then substract G I should be able to remove the gravity component, correct?

To accomplish this, I first turn q_aligned2raw into a rotation matrix M with tf.transformations.quaternion_matrix, then I use the matrix-vector multiplication with lin_acc and finally just substract G.

Am I missing something? This approach fails. Here are some experiments:

# 1.

• IMU lin_acc reading [x, y, z]: [-0.32561143, -0.80924016, 9.88805286]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [-1.76936953, -4.4546028 , 8.69254434]

# 2.

• IMU lin_acc reading [x, y, z]: [-0.19153613, -0.01915361, -9.62947908]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [ 1.58807182, 9.41955642, -1.23040848]

# 3.

• IMU lin_acc reading [x, y, z]: [-0.09576807, -9.61990227, 2.36068284]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [-8.92865455, -4.05394425, 1.40327425]

# 4.

• IMU lin_acc reading [x, y, z]: [-0.36391865, 9.62947908, 0.70389529]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [-8.56518971, 3.71455092, -2.48885704]

# 5.

• IMU lin_acc reading [x, y, z]: [9.60553706e+00, 4.78840332e-03, 9.57680664e-03]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [ 1.43719352, 7.26609646, -6.11594423]

# 6.

• IMU lin_acc reading [x, y, z]: [-10.07480059, -0.16280571, 0.09576807]
• expected lin_acc after rotation: [~0, ~0, ~9.81 ]
• observed lin_acc after rotation: [ 1.86515326, 7.72080671, -6.20061538]
• The linear acceleration is expressed wrt the myo_raw frame Probably not. The acceleration seems to be described wrt its own moving frame and the rotation from it to the myo_raw frame is not given. That may be the missing point.
– dcfg
Jul 22, 2022 at 10:27

I see this kind of question a lot, and my answer is always the same - use the Madgwick filter. Here's the original site but if it's not working here's another and the Github page. It's PhD-level work that's available, for free, already written, in C, C#, and Matlab.

You don't need to implement your own calibration routine, and:

1. The odds of your accelerometer reading exactly 9.8100000 on the z axis are very small, and even if they did
2. You're not checking accelerations on the other axes, so you aren't discriminating between gravity and motion, and even if you were
3. You don't appear to be taking any angular velocities into account, so it's not clear how you're developing q_ref2raw, which in turn is critical to your conversion.

For reference, I took the magnitude (norm) of all of your example cases and I got the following: 9.9265, 9.6314, 9.9058, 9.5520, 9.6055, 10.077. None of these are 9.81, and so your conversion will never get any of those readings to [0, 0, (+/-)9.81]. Maybe there are readings on the other axes? I do get what you mean, though, in that the 9.81-ish readings aren't in the z position, but your algorithm isn't provided in detail here and I don't think it's suitable anyways.

Have you tried troubleshooting to determine when/if you're hitting the 9.81 on the z-axis? You may be looking at stale/uninitialized rotations.

Whatever the case, again, Madgwick has you covered. Use the free, open-source algorithm that works.

• q_ref2raw is the quaternion orientation read from the IMU sensor. I'm actually testing the Madgwick filter and indeed it seems to do a very good job. The problem is that it requires some time to converge to an acceleration value which is nearly 0 in steady state each time I change to orientation of the armband. Therefore I found it pretty poor for a real time application. There's one thing I don't get from the Madgwick filter: the provided orientation is absolute wrt to a fixed Earth's frame?
– dcfg
Jul 22, 2022 at 15:37
• @dc_Bita98 have you seen the demonstration video? I've used it and it works fine for realtime applications. Are you coding it yourself or using the implementations provided by Madgwick himself?
– Chuck
Jul 22, 2022 at 18:00
• @dc_Bita98 can you show a video of the behavior you're describing? I'd also be interested to know what a rostopic hz ~myo_imu gives you, because I'm wondering if maybe it's taking a long time to converge because the update rate is really low.
– Chuck
Jul 23, 2022 at 11:54
• @dc_Bita98 are you using constant_dt in your filter setup? Variable sample rates if it's expecting constant would definitely skew your readings. Otherwise you may just be dropping samples. Gut feeling is that 60 Hz should still get you quality outputs (assuming you're using the correct sample intervals!) but I'd definitely try to investigate the rate fluctuation.
– Chuck
Jul 23, 2022 at 15:19
• constant_dt is set to the default value of 0, so it should be automatically computed from message headers. Of course I need to understand what's behind those fluctuations
– dcfg
Jul 23, 2022 at 15:23