Trouble removing gravity from calibrated IMU acceleration (dual ICM-20948 + quaternions)
Hi everyone,
I’m working on a project where I collect motion data using two ICM-20948 IMUs connected to an ESP32. I apply a 6-pose accelerometer calibration on the microcontroller and later process the data in MATLAB to obtain linear acceleration in the global frame (gravity-free). However, even after rotating the accelerometer data using the quaternions and subtracting gravity, the results still don’t look correct.
Before assuming my processing is wrong, I want to double-check with the community whether my MATLAB approach for gravity removal is correct — and whether I’m handling the quaternion convention properly.📍 Data Acquisition Pipeline (short summary)
Hardware: ESP32 + 2× ICM-20948
I use the ICM20948_WE library
On the ESP32 I:✅ Read accel & gyro✅ Apply bias + gain from 6-pose calibration✅ Log calibrated accel (in g), calibrated gyro, and quaternions
No magnetometer used (so yaw may drift, but recordings are 30–120 s only)
Data is saved as:
q0 q1 q2 q3 ax ay az gx gy gz
🧠 MATLAB Processing (core part of code)
Below is the section where I align the IMU with the global frame and remove gravity. I use the first ~5 s (static) to estimate the gravity direction, align the IMU Z-axis to global Z, rotate accel using the quaternions, and subtract gravity:
%% CORREÇÃO DA ORIENTAÇÃO INICIAL (PERÍODO PARADO)
fprintf(‘n— Correção da Orientação Inicial (5 s parado) —n’);
G = 9.80665; % m/s²
Fs_my = 1 / mean(diff(T.t_s));
Nrest = round(5 * Fs_my);
% IMU1
a1_rest = [T.a1x(1:Nrest), T.a1y(1:Nrest), T.a1z(1:Nrest)];
g_est1 = mean(a1_rest, 1);
g_est1 = g_est1 / norm(g_est1);
% direção da gravidade medida
% Quaternion inicial
Q1 = [T.q10, T.q11, T.q12, T.q13];
if mean(abs(Q1(:,1))) < mean(abs(Q1(:,4)))
Q1 = [Q1(:,4), Q1(:,1:3)]; % tentar garantir [w x y z]
end
Q1 = Q1 ./ vecnorm(Q1,2,2);
% Vetor Z global ideal
z_ref = [0 0 1];
% Calcular rotação para alinhar g_est1 -> z_ref
v = cross(g_est1, z_ref);
s = norm(v);
if s ~= 0
c = dot(g_est1, z_ref);
v_skew = [0 -v(3) v(2); v(3) 0 -v(1); -v(2) v(1) 0];
R_align = eye(3) + v_skew + v_skew^2 * ((1 – c)/(s^2));
else
R_align = eye(3);
end
% Converter aceleração e alinhar
a1 = [T.a1x, T.a1y, T.a1z];
a1_gbl = quatrotate(Q1, a1);
a1_gbl_corr = (R_align * a1_gbl’)’;
% Remover gravidade
a1_free_gbl = a1_gbl_corr – [0 0 G];
(I apply the same procedure for IMU2 if present.)📎 Example of the issue
When visualizing the acceleration before and after gravity removal, the result still does not look physically correct, despite the 6-pose calibration and quaternion rotation.
Image 1: Before gravity removal
Image 2: After gravity removal — still incorrect)
❓ My Main Questions
Is my approach to rotate acceleration into the global frame and remove gravity mathematically correct?(Or am I applying the alignment/rotation in the wrong order?)
Should gravity be removed before or after applying R_align?Some posts suggest subtracting gravity in the sensor frame, others in global frame.
Quaternion convention:I’m assuming MATLAB’s quatrotate expects [w x y z] (scalar-first).Am I handling the quaternion conversion correctly here, or could this be a mismatch with the ICM-20948 library?
Given that I’m not using the magnetometer:For recordings of 30–120 s, is yaw drift likely to significantly affect gravity removal, or should it still work reasonably well?
Any help or guidance would be greatly appreciated — especially if someone spots a conceptual mistake or if there’s a more robust approach to handle gravity removal for this sensor.
Thanks in advance!Hi everyone,
I’m working on a project where I collect motion data using two ICM-20948 IMUs connected to an ESP32. I apply a 6-pose accelerometer calibration on the microcontroller and later process the data in MATLAB to obtain linear acceleration in the global frame (gravity-free). However, even after rotating the accelerometer data using the quaternions and subtracting gravity, the results still don’t look correct.
Before assuming my processing is wrong, I want to double-check with the community whether my MATLAB approach for gravity removal is correct — and whether I’m handling the quaternion convention properly.📍 Data Acquisition Pipeline (short summary)
Hardware: ESP32 + 2× ICM-20948
I use the ICM20948_WE library
On the ESP32 I:✅ Read accel & gyro✅ Apply bias + gain from 6-pose calibration✅ Log calibrated accel (in g), calibrated gyro, and quaternions
No magnetometer used (so yaw may drift, but recordings are 30–120 s only)
Data is saved as:
q0 q1 q2 q3 ax ay az gx gy gz
🧠 MATLAB Processing (core part of code)
Below is the section where I align the IMU with the global frame and remove gravity. I use the first ~5 s (static) to estimate the gravity direction, align the IMU Z-axis to global Z, rotate accel using the quaternions, and subtract gravity:
%% CORREÇÃO DA ORIENTAÇÃO INICIAL (PERÍODO PARADO)
fprintf(‘n— Correção da Orientação Inicial (5 s parado) —n’);
G = 9.80665; % m/s²
Fs_my = 1 / mean(diff(T.t_s));
Nrest = round(5 * Fs_my);
% IMU1
a1_rest = [T.a1x(1:Nrest), T.a1y(1:Nrest), T.a1z(1:Nrest)];
g_est1 = mean(a1_rest, 1);
g_est1 = g_est1 / norm(g_est1);
% direção da gravidade medida
% Quaternion inicial
Q1 = [T.q10, T.q11, T.q12, T.q13];
if mean(abs(Q1(:,1))) < mean(abs(Q1(:,4)))
Q1 = [Q1(:,4), Q1(:,1:3)]; % tentar garantir [w x y z]
end
Q1 = Q1 ./ vecnorm(Q1,2,2);
% Vetor Z global ideal
z_ref = [0 0 1];
% Calcular rotação para alinhar g_est1 -> z_ref
v = cross(g_est1, z_ref);
s = norm(v);
if s ~= 0
c = dot(g_est1, z_ref);
v_skew = [0 -v(3) v(2); v(3) 0 -v(1); -v(2) v(1) 0];
R_align = eye(3) + v_skew + v_skew^2 * ((1 – c)/(s^2));
else
R_align = eye(3);
end
% Converter aceleração e alinhar
a1 = [T.a1x, T.a1y, T.a1z];
a1_gbl = quatrotate(Q1, a1);
a1_gbl_corr = (R_align * a1_gbl’)’;
% Remover gravidade
a1_free_gbl = a1_gbl_corr – [0 0 G];
(I apply the same procedure for IMU2 if present.)📎 Example of the issue
When visualizing the acceleration before and after gravity removal, the result still does not look physically correct, despite the 6-pose calibration and quaternion rotation.
Image 1: Before gravity removal
Image 2: After gravity removal — still incorrect)
❓ My Main Questions
Is my approach to rotate acceleration into the global frame and remove gravity mathematically correct?(Or am I applying the alignment/rotation in the wrong order?)
Should gravity be removed before or after applying R_align?Some posts suggest subtracting gravity in the sensor frame, others in global frame.
Quaternion convention:I’m assuming MATLAB’s quatrotate expects [w x y z] (scalar-first).Am I handling the quaternion conversion correctly here, or could this be a mismatch with the ICM-20948 library?
Given that I’m not using the magnetometer:For recordings of 30–120 s, is yaw drift likely to significantly affect gravity removal, or should it still work reasonably well?
Any help or guidance would be greatly appreciated — especially if someone spots a conceptual mistake or if there’s a more robust approach to handle gravity removal for this sensor.
Thanks in advance! Hi everyone,
I’m working on a project where I collect motion data using two ICM-20948 IMUs connected to an ESP32. I apply a 6-pose accelerometer calibration on the microcontroller and later process the data in MATLAB to obtain linear acceleration in the global frame (gravity-free). However, even after rotating the accelerometer data using the quaternions and subtracting gravity, the results still don’t look correct.
Before assuming my processing is wrong, I want to double-check with the community whether my MATLAB approach for gravity removal is correct — and whether I’m handling the quaternion convention properly.📍 Data Acquisition Pipeline (short summary)
Hardware: ESP32 + 2× ICM-20948
I use the ICM20948_WE library
On the ESP32 I:✅ Read accel & gyro✅ Apply bias + gain from 6-pose calibration✅ Log calibrated accel (in g), calibrated gyro, and quaternions
No magnetometer used (so yaw may drift, but recordings are 30–120 s only)
Data is saved as:
q0 q1 q2 q3 ax ay az gx gy gz
🧠 MATLAB Processing (core part of code)
Below is the section where I align the IMU with the global frame and remove gravity. I use the first ~5 s (static) to estimate the gravity direction, align the IMU Z-axis to global Z, rotate accel using the quaternions, and subtract gravity:
%% CORREÇÃO DA ORIENTAÇÃO INICIAL (PERÍODO PARADO)
fprintf(‘n— Correção da Orientação Inicial (5 s parado) —n’);
G = 9.80665; % m/s²
Fs_my = 1 / mean(diff(T.t_s));
Nrest = round(5 * Fs_my);
% IMU1
a1_rest = [T.a1x(1:Nrest), T.a1y(1:Nrest), T.a1z(1:Nrest)];
g_est1 = mean(a1_rest, 1);
g_est1 = g_est1 / norm(g_est1);
% direção da gravidade medida
% Quaternion inicial
Q1 = [T.q10, T.q11, T.q12, T.q13];
if mean(abs(Q1(:,1))) < mean(abs(Q1(:,4)))
Q1 = [Q1(:,4), Q1(:,1:3)]; % tentar garantir [w x y z]
end
Q1 = Q1 ./ vecnorm(Q1,2,2);
% Vetor Z global ideal
z_ref = [0 0 1];
% Calcular rotação para alinhar g_est1 -> z_ref
v = cross(g_est1, z_ref);
s = norm(v);
if s ~= 0
c = dot(g_est1, z_ref);
v_skew = [0 -v(3) v(2); v(3) 0 -v(1); -v(2) v(1) 0];
R_align = eye(3) + v_skew + v_skew^2 * ((1 – c)/(s^2));
else
R_align = eye(3);
end
% Converter aceleração e alinhar
a1 = [T.a1x, T.a1y, T.a1z];
a1_gbl = quatrotate(Q1, a1);
a1_gbl_corr = (R_align * a1_gbl’)’;
% Remover gravidade
a1_free_gbl = a1_gbl_corr – [0 0 G];
(I apply the same procedure for IMU2 if present.)📎 Example of the issue
When visualizing the acceleration before and after gravity removal, the result still does not look physically correct, despite the 6-pose calibration and quaternion rotation.
Image 1: Before gravity removal
Image 2: After gravity removal — still incorrect)
❓ My Main Questions
Is my approach to rotate acceleration into the global frame and remove gravity mathematically correct?(Or am I applying the alignment/rotation in the wrong order?)
Should gravity be removed before or after applying R_align?Some posts suggest subtracting gravity in the sensor frame, others in global frame.
Quaternion convention:I’m assuming MATLAB’s quatrotate expects [w x y z] (scalar-first).Am I handling the quaternion conversion correctly here, or could this be a mismatch with the ICM-20948 library?
Given that I’m not using the magnetometer:For recordings of 30–120 s, is yaw drift likely to significantly affect gravity removal, or should it still work reasonably well?
Any help or guidance would be greatly appreciated — especially if someone spots a conceptual mistake or if there’s a more robust approach to handle gravity removal for this sensor.
Thanks in advance! imu, quaternions, preprocessing, acceleration, icm20948, esp32 MATLAB Answers — New Questions









