@@ -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