Skip to content

Commit 59e8022

Browse files
test ains
1 parent 00e02be commit 59e8022

1 file changed

Lines changed: 252 additions & 84 deletions

File tree

tests/test_smoothing.py

Lines changed: 252 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -30,110 +30,278 @@ def test__init__(self, ains):
3030
assert smoother.bias_acc().size == 0
3131
assert smoother.bias_gyro().size == 0
3232

33-
def test_benchmark(self):
34-
fs_imu = 10.0
35-
fs_aiding = 1.0
36-
fs_ratio = np.ceil(fs_imu / fs_aiding)
37-
warmup = int(fs_imu * 600.0) # truncate 600 seconds from the beginning
38-
compass_noise_std = 0.5
39-
gps_noise_std = 0.1
40-
vel_noise_std = 0.1
41-
42-
# Reference signals (without noise)
43-
t, pos_ref, vel_ref, euler_ref, acc_ref, gyro_ref = benchmark_full_pva_beat_202311A(fs_imu)
44-
euler_ref = np.degrees(euler_ref)
45-
gyro_ref = np.degrees(gyro_ref)
46-
47-
rng = np.random.default_rng(seed=1)
48-
49-
# IMU measurements (with noise)
50-
err_acc = sf.constants.ERR_ACC_MOTION2
51-
err_gyro = sf.constants.ERR_GYRO_MOTION2
52-
noise_model = sf.noise.IMUNoise(err_acc, err_gyro, seed=0)
53-
imu_noise = noise_model(fs_imu, len(t))
54-
acc_meas = acc_ref + imu_noise[:, :3]
55-
gyro_meas = gyro_ref + np.degrees(imu_noise[:, 3:])
56-
57-
# Compass / heading (aiding) measurements
58-
head_noise = compass_noise_std * rng.standard_normal(len(t))
59-
head_meas = euler_ref[:, 2] + head_noise
60-
61-
# GPS / position (aiding) measurements
62-
pos_noise = gps_noise_std * rng.standard_normal((len(t), 3))
63-
pos_meas = pos_ref + pos_noise
64-
65-
# Velocity (aiding) measurements
66-
vel_noise = vel_noise_std * rng.standard_normal((len(t), 3))
67-
vel_meas = vel_ref + vel_noise
68-
69-
# MEKF
70-
p0 = pos_ref[0] # position [m]
71-
v0 = vel_ref[0] # velocity [m/s]
72-
q0 = sf.quaternion_from_euler(euler_ref[0]) # attitude as unit quaternion
33+
# def test_benchmark_ains(self):
34+
# fs_imu = 10.0
35+
# fs_aiding = 1.0
36+
# fs_ratio = np.ceil(fs_imu / fs_aiding)
37+
# warmup = int(fs_imu * 600.0) # truncate 600 seconds from the beginning
38+
# compass_noise_std = 0.5
39+
# gps_noise_std = 0.1
40+
# vel_noise_std = 0.1
41+
42+
# # Reference signals (without noise)
43+
# t, pos_ref, vel_ref, euler_ref, acc_ref, gyro_ref = benchmark_full_pva_beat_202311A(fs_imu)
44+
# euler_ref = np.degrees(euler_ref)
45+
# gyro_ref = np.degrees(gyro_ref)
46+
47+
# rng = np.random.default_rng(seed=1)
48+
49+
# # IMU measurements (with noise)
50+
# err_acc = sf.constants.ERR_ACC_MOTION2
51+
# err_gyro = sf.constants.ERR_GYRO_MOTION2
52+
# noise_model = sf.noise.IMUNoise(err_acc, err_gyro, seed=0)
53+
# imu_noise = noise_model(fs_imu, len(t))
54+
# acc_meas = acc_ref + imu_noise[:, :3]
55+
# gyro_meas = gyro_ref + np.degrees(imu_noise[:, 3:])
56+
57+
# # Compass / heading (aiding) measurements
58+
# head_noise = compass_noise_std * rng.standard_normal(len(t))
59+
# head_meas = euler_ref[:, 2] + head_noise
60+
61+
# # GPS / position (aiding) measurements
62+
# pos_noise = gps_noise_std * rng.standard_normal((len(t), 3))
63+
# pos_meas = pos_ref + pos_noise
64+
65+
# # Velocity (aiding) measurements
66+
# vel_noise = vel_noise_std * rng.standard_normal((len(t), 3))
67+
# vel_meas = vel_ref + vel_noise
68+
69+
# # MEKF
70+
# p0 = pos_ref[0] # position [m]
71+
# v0 = vel_ref[0] # velocity [m/s]
72+
# q0 = sf.quaternion_from_euler(euler_ref[0], degrees=True) # attitude as unit quaternion
73+
# ba0 = np.zeros(3) # accelerometer bias [m/s^2]
74+
# bg0 = np.zeros(3) # gyroscope bias [rad/s]
75+
# x0 = np.concatenate((p0, v0, q0, ba0, bg0))
76+
# P0 = sf.constants.P0
77+
# ains = sf.AidedINS(fs_imu, x0, P0, err_acc, err_gyro)
78+
79+
# smoother = FixedIntervalSmoother(ains, cov_smoothing=True)
80+
81+
# pos_ains, vel_ains, euler_ains, bias_acc_ains, bias_gyro_ains = [], [], [], [], []
82+
# for i, (acc_i, gyro_i, pos_i, vel_i, head_i) in enumerate(
83+
# zip(acc_meas, gyro_meas, pos_meas, vel_meas, head_meas)
84+
# ):
85+
# if not (i % fs_ratio): # with aiding
86+
# smoother.update(
87+
# acc_i,
88+
# gyro_i,
89+
# degrees=True,
90+
# pos=pos_i,
91+
# pos_var=gps_noise_std**2 * np.ones(3),
92+
# vel=vel_i,
93+
# vel_var=vel_noise_std**2 * np.ones(3),
94+
# head=head_i,
95+
# head_var=compass_noise_std**2,
96+
# head_degrees=True,
97+
# g_ref=True,
98+
# g_var=0.1**2 * np.ones(3),
99+
# )
100+
# else: # without aiding
101+
# smoother.update(acc_i, gyro_i, degrees=True)
102+
103+
# pos_ains.append(smoother.ains.position())
104+
# vel_ains.append(smoother.ains.velocity())
105+
# euler_ains.append(smoother.ains.euler(degrees=True))
106+
# bias_acc_ains.append(smoother.ains.bias_acc())
107+
# bias_gyro_ains.append(smoother.ains.bias_gyro(degrees=True))
108+
109+
# pos_ains = np.array(pos_ains)
110+
# vel_ains = np.array(vel_ains)
111+
# euler_ains = np.array(euler_ains)
112+
# bias_acc_ains = np.array(bias_acc_ains)
113+
# bias_gyro_ains = np.array(bias_gyro_ains)
114+
115+
# pos_smth = smoother.position()
116+
# vel_smth = smoother.velocity()
117+
# euler_smth = smoother.euler(degrees=True)
118+
# bias_acc_smth = smoother.bias_acc()
119+
# bias_gyro_smth = smoother.bias_gyro(degrees=True)
120+
121+
# pos_smth = np.array(pos_smth)
122+
# vel_smth = np.array(vel_smth)
123+
# euler_smth = np.array(euler_smth)
124+
# bias_acc_smth = np.array(bias_acc_smth)
125+
# bias_gyro_smth = np.array(bias_gyro_smth)
126+
127+
# # Half-sample shift (compensates for the delay introduced by Euler integration)
128+
# pos_ains = resample_poly(pos_ains, 2, 1)[1:-1:2]
129+
# pos_smth = resample_poly(pos_smth, 2, 1)[1:-1:2]
130+
# pos_ref = pos_ref[:-1, :]
131+
# vel_ains = resample_poly(vel_ains, 2, 1)[1:-1:2]
132+
# vel_smth = resample_poly(vel_smth, 2, 1)[1:-1:2]
133+
# vel_ref = vel_ref[:-1, :]
134+
# euler_ains = resample_poly(euler_ains, 2, 1)[1:-1:2]
135+
# euler_smth = resample_poly(euler_smth, 2, 1)[1:-1:2]
136+
# euler_ref = euler_ref[:-1, :]
137+
138+
# pos_err_smth = np.std((pos_smth - pos_ref)[warmup:], axis=0)
139+
# pos_err_ains = np.std((pos_ains - pos_ref)[warmup:], axis=0)
140+
# np.testing.assert_array_less(pos_err_smth, pos_err_ains)
141+
142+
# vel_err_smth = np.std((vel_smth - vel_ref)[warmup:], axis=0)
143+
# vel_err_ains = np.std((vel_ains - vel_ref)[warmup:], axis=0)
144+
# np.testing.assert_array_less(vel_err_smth, vel_err_ains)
145+
146+
# euler_err_smth = np.std((euler_smth - euler_ref)[warmup:], axis=0)
147+
# euler_err_ains = np.std((euler_ains - euler_ref)[warmup:], axis=0)
148+
# np.testing.assert_array_less(euler_err_smth, euler_err_ains)
149+
150+
# def test_benchmark_ahrs(self):
151+
# fs_imu = 10.0
152+
# fs_aiding = 1.0
153+
# fs_ratio = np.ceil(fs_imu / fs_aiding)
154+
# warmup = int(fs_imu * 600.0) # truncate 600 seconds from the beginning
155+
# compass_noise_std = 0.5
156+
# gps_noise_std = 0.1
157+
# vel_noise_std = 0.1
158+
159+
# # Reference signals (without noise)
160+
# t, pos_ref, vel_ref, euler_ref, acc_ref, gyro_ref = benchmark_full_pva_beat_202311A(fs_imu)
161+
# euler_ref = np.degrees(euler_ref)
162+
# gyro_ref = np.degrees(gyro_ref)
163+
164+
# rng = np.random.default_rng(seed=1)
165+
166+
# # IMU measurements (with noise)
167+
# err_acc = sf.constants.ERR_ACC_MOTION2
168+
# err_gyro = sf.constants.ERR_GYRO_MOTION2
169+
# noise_model = sf.noise.IMUNoise(err_acc, err_gyro, seed=0)
170+
# imu_noise = noise_model(fs_imu, len(t))
171+
# acc_meas = acc_ref + imu_noise[:, :3]
172+
# gyro_meas = gyro_ref + np.degrees(imu_noise[:, 3:])
173+
174+
# # Compass / heading (aiding) measurements
175+
# head_noise = compass_noise_std * rng.standard_normal(len(t))
176+
# head_meas = euler_ref[:, 2] + head_noise
177+
178+
# # GPS / position (aiding) measurements
179+
# pos_noise = gps_noise_std * rng.standard_normal((len(t), 3))
180+
# pos_meas = pos_ref + pos_noise
181+
182+
# # Velocity (aiding) measurements
183+
# vel_noise = vel_noise_std * rng.standard_normal((len(t), 3))
184+
# vel_meas = vel_ref + vel_noise
185+
186+
# # MEKF
187+
# p0 = pos_ref[0] # position [m]
188+
# v0 = vel_ref[0] # velocity [m/s]
189+
# q0 = sf.quaternion_from_euler(euler_ref[0], degrees=True) # attitude as unit quaternion
190+
# ba0 = np.zeros(3) # accelerometer bias [m/s^2]
191+
# bg0 = np.zeros(3) # gyroscope bias [rad/s]
192+
# x0 = np.concatenate((p0, v0, q0, ba0, bg0))
193+
# P0 = sf.constants.P0
194+
# ains = sf.AHRS(fs_imu, x0, P0, err_acc, err_gyro) # AHRS
195+
196+
# smoother = FixedIntervalSmoother(ains, cov_smoothing=True)
197+
198+
# pos_ains, vel_ains, euler_ains, bias_acc_ains, bias_gyro_ains = [], [], [], [], []
199+
# for i, (acc_i, gyro_i, pos_i, vel_i, head_i) in enumerate(
200+
# zip(acc_meas, gyro_meas, pos_meas, vel_meas, head_meas)
201+
# ):
202+
# if not (i % fs_ratio): # with aiding
203+
# smoother.update(
204+
# acc_i,
205+
# gyro_i,
206+
# degrees=True,
207+
# head=head_i,
208+
# head_var=compass_noise_std**2,
209+
# head_degrees=True,
210+
# )
211+
# else: # without aiding
212+
# smoother.update(acc_i, gyro_i, degrees=True)
213+
214+
# euler_ains.append(smoother.ains.euler(degrees=True))
215+
216+
# euler_ains = np.array(euler_ains)
217+
218+
# euler_smth = smoother.euler(degrees=True)
219+
220+
# euler_smth = np.array(euler_smth)
221+
222+
# # Half-sample shift (compensates for the delay introduced by Euler integration)
223+
# euler_ains = resample_poly(euler_ains, 2, 1)[1:-1:2]
224+
# euler_smth = resample_poly(euler_smth, 2, 1)[1:-1:2]
225+
# euler_ref = euler_ref[:-1, :]
226+
227+
# euler_err_smth = np.std((euler_smth - euler_ref)[warmup:], axis=0)
228+
# euler_err_ains = np.std((euler_ains - euler_ref)[warmup:], axis=0)
229+
# np.testing.assert_array_less(euler_err_smth, euler_err_ains)
230+
231+
def test_benchmark_ains(self):
232+
# Reference signal
233+
fs = 10.24 # sampling rate in Hz
234+
_, pos, vel, euler, acc, gyro = benchmark_full_pva_beat_202311A(fs)
235+
head = euler[:, 2]
236+
237+
# IMU measurements
238+
err_acc = sf.constants.ERR_ACC_MOTION2 # m/s^2
239+
err_gyro = sf.constants.ERR_GYRO_MOTION2 # rad/s
240+
imu_noise = sf.noise.IMUNoise(err_acc, err_gyro)(fs, len(acc))
241+
acc_imu = acc + imu_noise[:, :3]
242+
gyro_imu = gyro + imu_noise[:, 3:]
243+
244+
# Aiding measurements
245+
pos_noise_std = 0.1 # m
246+
head_noise_std = 0.01 # rad
247+
rng = np.random.default_rng(0)
248+
pos_aid = pos + pos_noise_std * rng.standard_normal(pos.shape)
249+
head_aid = head + head_noise_std * rng.standard_normal(head.shape)
250+
251+
# AINS
252+
p0 = pos[0] # position [m]
253+
v0 = vel[0] # velocity [m/s]
254+
q0 = sf.quaternion_from_euler(euler[0], degrees=False) # unit quaternion
73255
ba0 = np.zeros(3) # accelerometer bias [m/s^2]
74256
bg0 = np.zeros(3) # gyroscope bias [rad/s]
75257
x0 = np.concatenate((p0, v0, q0, ba0, bg0))
76-
P0 = sf.constants.P0
77-
ains = sf.AidedINS(fs_imu, x0, P0, err_acc, err_gyro)
78-
79-
smoother = FixedIntervalSmoother(ains, cov_smoothing=True)
80-
81-
pos_ains, vel_ains, euler_ains, bias_acc_ains, bias_gyro_ains = [], [], [], [], []
82-
for i, (acc_i, gyro_i, pos_i, vel_i, head_i) in enumerate(
83-
zip(acc_meas, gyro_meas, pos_meas, vel_meas, head_meas)
84-
):
85-
if not (i % fs_ratio): # with aiding
86-
smoother.update(
87-
acc_i,
88-
gyro_i,
89-
degrees=True,
90-
pos=pos_i,
91-
pos_var=gps_noise_std**2 * np.ones(3),
92-
vel=vel_i,
93-
vel_var=vel_noise_std**2 * np.ones(3),
94-
head=head_i,
95-
head_var=compass_noise_std**2,
96-
head_degrees=True,
97-
g_ref=True,
98-
g_var=0.1**2 * np.ones(3),
99-
)
100-
else: # without aiding
101-
smoother.update(acc_i, gyro_i, degrees=True)
258+
P0 = np.eye(12) * 1e-3
259+
err_acc = sf.constants.ERR_ACC_MOTION2 # m/s^2
260+
err_gyro = sf.constants.ERR_GYRO_MOTION2 # rad/s
261+
ains = sf.AidedINS(fs, x0, P0, err_acc, err_gyro)
262+
263+
smoother = sf.FixedIntervalSmoother(ains, cov_smoothing=True)
264+
265+
pos_ains, vel_ains, euler_ains = [], [], []
266+
for f_i, w_i, p_i, h_i in zip(acc_imu, gyro_imu, pos_aid, head_aid):
267+
smoother.update(
268+
f_i,
269+
w_i,
270+
degrees=False,
271+
pos=p_i,
272+
pos_var=pos_noise_std**2 * np.ones(3),
273+
head=h_i,
274+
head_var=head_noise_std**2,
275+
head_degrees=False,
276+
)
102277

103278
pos_ains.append(smoother.ains.position())
104279
vel_ains.append(smoother.ains.velocity())
105-
euler_ains.append(smoother.ains.euler(degrees=True))
106-
bias_acc_ains.append(smoother.ains.bias_acc())
107-
bias_gyro_ains.append(smoother.ains.bias_gyro(degrees=True))
280+
euler_ains.append(smoother.ains.euler(degrees=False))
108281

282+
# Forward filter state estimates
109283
pos_ains = np.array(pos_ains)
110284
vel_ains = np.array(vel_ains)
111285
euler_ains = np.array(euler_ains)
112-
bias_acc_ains = np.array(bias_acc_ains)
113-
bias_gyro_ains = np.array(bias_gyro_ains)
114286

287+
# Smoothed state estimates
115288
pos_smth = smoother.position()
116289
vel_smth = smoother.velocity()
117-
euler_smth = smoother.euler(degrees=True)
118-
bias_acc_smth = smoother.bias_acc()
119-
bias_gyro_smth = smoother.bias_gyro(degrees=True)
290+
euler_smth = smoother.euler(degrees=False)
120291

121-
pos_smth = np.array(pos_smth)
122-
vel_smth = np.array(vel_smth)
123-
euler_smth = np.array(euler_smth)
124-
bias_acc_smth = np.array(bias_acc_smth)
125-
bias_gyro_smth = np.array(bias_gyro_smth)
126-
127-
# Half-sample shift (compensates for the delay introduced by Euler integration)
292+
# Half-sample shift
293+
# # (compensates for the delay introduced by Euler integration)
128294
pos_ains = resample_poly(pos_ains, 2, 1)[1:-1:2]
129295
pos_smth = resample_poly(pos_smth, 2, 1)[1:-1:2]
130-
pos_ref = pos_ref[:-1, :]
296+
pos_ref = pos[:-1, :]
131297
vel_ains = resample_poly(vel_ains, 2, 1)[1:-1:2]
132298
vel_smth = resample_poly(vel_smth, 2, 1)[1:-1:2]
133-
vel_ref = vel_ref[:-1, :]
299+
vel_ref = vel[:-1, :]
134300
euler_ains = resample_poly(euler_ains, 2, 1)[1:-1:2]
135301
euler_smth = resample_poly(euler_smth, 2, 1)[1:-1:2]
136-
euler_ref = euler_ref[:-1, :]
302+
euler_ref = euler[:-1, :]
303+
304+
warmup = int(fs * 600.0) # truncate 600 seconds from the beginning
137305

138306
pos_err_smth = np.std((pos_smth - pos_ref)[warmup:], axis=0)
139307
pos_err_ains = np.std((pos_ains - pos_ref)[warmup:], axis=0)

0 commit comments

Comments
 (0)