Created
November 19, 2018 09:41
-
-
Save phausamann/721fa3df0f8ef6f4f6f24b86fdde53c0 to your computer and use it in GitHub Desktop.
IMU orientation estimation with complementary filter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from scipy.signal import filtfilt, butter | |
from quaternion import quaternion, from_rotation_vector, rotate_vectors | |
def estimate_orientation(a, w, t, alpha=0.9, g_ref=(0., 0., 1.), | |
theta_min=1e-6, highpass=.01, lowpass=.05): | |
""" Estimate orientation with a complementary filter. | |
Fuse linear acceleration and angular velocity measurements to obtain an | |
estimate of orientation using a complementary filter as described in | |
`Wetzstein 2017: 3-DOF Orientation Tracking with IMUs`_ | |
.. _Wetzstein 2017: 3-DOF Orientation Tracking with IMUs: | |
https://pdfs.semanticscholar.org/5568/e2100cab0b573599accd2c77debd05ccf3b1.pdf | |
Parameters | |
---------- | |
a : array-like, shape (N, 3) | |
Acceleration measurements (in arbitrary units). | |
w : array-like, shape (N, 3) | |
Angular velocity measurements (in rad/s). | |
t : array-like, shape (N,) | |
Timestamps of the measurements (in s). | |
alpha : float, default 0.9 | |
Weight of the angular velocity measurements in the estimate. | |
g_ref : tuple, len 3, default (0., 0., 1.) | |
Unit vector denoting direction of gravity. | |
theta_min : float, default 1e-6 | |
Minimal angular velocity after filtering. Values smaller than this | |
will be considered noise and are not used for the estimate. | |
highpass : float, default .01 | |
Cutoff frequency of the high-pass filter for the angular velocity as | |
fraction of Nyquist frequency. | |
lowpass : float, default .05 | |
Cutoff frequency of the low-pass filter for the linear acceleration as | |
fraction of Nyquist frequency. | |
Returns | |
------- | |
q : array of quaternions, shape (N,) | |
The estimated orientation for each measurement. | |
""" | |
# initialize some things | |
N = len(t) | |
dt = np.diff(t) | |
g_ref = np.array(g_ref) | |
q = np.ones(N, dtype=quaternion) | |
# get high-passed angular velocity | |
w = filtfilt(*butter(5, highpass, btype='high'), w, axis=0) | |
w[np.linalg.norm(w, axis=1) < theta_min] = 0 | |
q_delta = from_rotation_vector(w[1:] * dt[:, None]) | |
# get low-passed linear acceleration | |
a = filtfilt(*butter(5, lowpass, btype='low'), a, axis=0) | |
for i in range(1, N): | |
# get rotation estimate from gyroscope | |
q_w = q[i - 1] * q_delta[i - 1] | |
# get rotation estimate from accelerometer | |
v_world = rotate_vectors(q_w, a[i]) | |
n = np.cross(v_world, g_ref) | |
phi = np.arccos(np.dot(v_world / np.linalg.norm(v_world), g_ref)) | |
q_a = from_rotation_vector( | |
(1 - alpha) * phi * n[None, :] / np.linalg.norm(n))[0] | |
# fuse both estimates | |
q[i] = q_a * q_w | |
return q |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment