Skyward boardcore
Loading...
Searching...
No Matches
NAS.cpp
Go to the documentation of this file.
1/* Copyright (c) 2020-2022 Skyward Experimental Rocketry
2 * Authors: Alessandro Del Duca, Luca Conterio, Marco Cella
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "NAS.h"
24
26#include <math.h>
28
29#include <iostream>
30
31using namespace Boardcore;
32using namespace Eigen;
33
34namespace Boardcore
35{
36
37NAS::NAS(NASConfig config) : config(config)
38{
39 // Covariance setup
40 {
41 // clang-format off
42 Matrix3f P_pos{
43 {config.P_POS, 0, 0},
44 {0, config.P_POS, 0},
45 {0, 0, config.P_POS_VERTICAL}
46 };
47 Matrix3f P_vel{
48 {config.P_VEL, 0, 0},
49 {0, config.P_VEL, 0},
50 {0, 0, config.P_VEL_VERTICAL}
51 };
52 Matrix3f P_att = Matrix3f::Identity() * config.P_ATT;
53 Matrix3f P_bias = Matrix3f::Identity() * config.P_BIAS;
54 P << P_pos, MatrixXf::Zero(3, 9),
55 MatrixXf::Zero(3, 3), P_vel, MatrixXf::Zero(3, 6),
56 MatrixXf::Zero(3, 6), P_att, MatrixXf::Zero(3, 3),
57 MatrixXf::Zero(3, 9), P_bias; // cppcheck-suppress constStatement
58 // clang-format on
59 }
60
61 // Utility matrixes
62 R_acc << config.SIGMA_ACC * config.SIGMA_ACC * Matrix3f::Identity();
63 R_mag << config.SIGMA_MAG * config.SIGMA_MAG * Matrix3f::Identity();
64 {
65 // clang-format off
66 Q_quat << (config.SIGMA_W * config.SIGMA_W * config.T + (1.0f / 3.0f) * config.SIGMA_BETA * config.SIGMA_BETA * config.T * config.T * config.T) * Matrix3f::Identity(),
67 (0.5f * config.SIGMA_BETA * config.SIGMA_BETA * config.T * config.T) * Matrix3f::Identity(),
68 (0.5f * config.SIGMA_BETA * config.SIGMA_BETA * config.T * config.T) * Matrix3f::Identity(), // cppcheck-suppress constStatement
69 (config.SIGMA_BETA * config.SIGMA_BETA * config.T) * Matrix3f::Identity();
70 // clang-format on
71
72 Q_lin << config.SIGMA_POS * config.SIGMA_POS * Matrix3f::Identity(),
73 MatrixXf::Zero(3, 3),
74 // cppcheck-suppress constStatement
75 MatrixXf::Zero(3, 3),
76 config.SIGMA_VEL * config.SIGMA_VEL * Matrix3f::Identity();
77
78 F_lin = Matrix<float, 6, 6>::Identity();
79 F_lin.block<3, 3>(0, 3) = config.T * Matrix3f::Identity();
80 F_lin_tr = F_lin.transpose();
81
82 F_quat << -Matrix3f::Identity(), -Matrix3f::Identity() * config.T,
83 Matrix3f::Zero(3, 3), Matrix3f::Identity();
84 F_quat_tr = F_quat.transpose();
85
86 G_quat << -Matrix3f::Identity(), Matrix3f::Zero(3, 3),
87 Matrix3f::Zero(3, 3), Matrix3f::Identity();
88 G_quat_tr = G_quat.transpose();
89 }
90}
91
92void NAS::predictAcc(const Vector3f& acceleration)
93{
94 Matrix3f A = SkyQuaternion::quat2rotationMatrix(x.block<4, 1>(IDX_QUAT, 0));
95 Vector3f pos = x.block<3, 1>(IDX_POS, 0);
96 Vector3f vel = x.block<3, 1>(IDX_VEL, 0);
97
98 // Update position by integrating the velocity
99 pos = pos + vel * config.T;
100
101 // Measured acceleration in NED frame without gravity
102 Vector3f a = A * acceleration - gravityNed;
103
104 // Update velocity by integrating the acceleration
105 vel = vel + a * config.T;
106
107 // Save the updated state
108 x.block<3, 1>(IDX_POS, 0) = pos;
109 x.block<3, 1>(IDX_VEL, 0) = vel;
110
111 // Variance propagation
112 Matrix<float, 6, 6> Pl = P.block<6, 6>(0, 0);
113 P.block<6, 6>(0, 0) = F_lin * Pl * F_lin_tr + Q_lin;
114}
115
116void NAS::predictAcc(const AccelerometerData& acceleration)
117{
118 predictAcc(Vector3f{acceleration.accelerationX, acceleration.accelerationY,
119 acceleration.accelerationZ});
120}
121
122void NAS::predictGyro(const Vector3f& angularSpeed)
123{
124 Vector3f bias = x.block<3, 1>(IDX_BIAS, 0);
125 Vector4f q = x.block<4, 1>(IDX_QUAT, 0);
126
127 Vector3f omega = angularSpeed - bias;
128 // clang-format off
129 Matrix4f omega_mat{
130 { 0.0f, omega(2), -omega(1), omega(0)},
131 {-omega(2), 0.0f, omega(0), omega(1)},
132 { omega(1), -omega(0), 0.0f, omega(2)},
133 {-omega(0), -omega(1), -omega(2), 0.0f}
134 };
135 // clang-format on
136
137 // Update orientation by integrating the angular velocity
138 q = (Matrix4f::Identity() + 0.5F * omega_mat * config.T) * q;
139 q.normalize();
140
141 // Save the updated state
142 x.block<4, 1>(IDX_QUAT, 0) = q;
143
144 // Variance propagation
145 // TODO: Optimize last G_quat * Q_quat * G_att_tr
146 Matrix<float, 6, 6> Pq = P.block<6, 6>(IDX_QUAT, IDX_QUAT);
147 Pq = F_quat * Pq * F_quat_tr + G_quat * Q_quat * G_quat_tr;
148 P.block<6, 6>(IDX_QUAT, IDX_QUAT) = Pq;
149}
150
151void NAS::predictGyro(const GyroscopeData& angularSpeed)
152{
153 predictGyro(Vector3f{angularSpeed.angularSpeedX, angularSpeed.angularSpeedY,
154 angularSpeed.angularSpeedZ});
155}
156
157void NAS::correctBaro(const float pressure)
158{
159 float d = x(2);
160 float altitude = -d;
161
162 float temp = Aeroutils::relTemperature(altitude, reference.refTemperature);
163
164 Matrix<float, 1, 6> H = Matrix<float, 1, 6>::Zero();
165 float R = config.SIGMA_BAR * config.SIGMA_BAR;
166
167 // Compute gradient of the altitude-pressure function
168 H[2] = Constants::a * Constants::n * reference.refPressure *
169 powf(1 + Constants::a * d / temp, Constants::n - 1) / temp;
170
171 Matrix<float, 6, 6> Pl = P.block<6, 6>(0, 0);
172 float S = H * Pl * H.transpose() + R;
173
174 // If not invertible, don't do the correction and return
175 if (S < 1e-3)
176 return;
177
178 float y_hat =
179 Aeroutils::relPressure(reference.refAltitude - x(2),
180 reference.mslPressure, reference.mslTemperature);
181 Matrix<float, 1, 1> e = Matrix<float, 1, 1>(pressure - y_hat);
182 Matrix<float, 6, 1> K = Pl * H.transpose() / S;
183 P.block<6, 6>(0, 0) = (Matrix<float, 6, 6>::Identity() - K * H) * Pl;
184
185 // Update the state
186 x.head<6>() = x.head<6>() + K * e;
187}
188
189void NAS::correctGPS(const Vector4f& gps)
190{
191 // GPS
192 H_gps = Matrix<float, 4, 6>::Zero();
193
194 //
195 float angle_rad = (reference.refLatitude + x(0) / Constants::gpsLatConst) *
196 Constants::DEGREES_TO_RADIANS;
197
198 float H11 = 1 / Constants::gpsLatConst;
199 float H12 = 0;
200 float H21 = (x(1) * sin(angle_rad)) /
201 (Constants::gpsLatConst * Constants::gpsLonConst *
202 pow(cos(angle_rad), 2));
203 float H22 = 1 / (Constants::gpsLonConst * cos(angle_rad));
204
205 // Convert to millideg
206 H_gps.coeffRef(0, 0) = H11 * 1000;
207 H_gps.coeffRef(0, 1) = H12 * 1000;
208 H_gps.coeffRef(1, 0) = H21 * 1000;
209 H_gps.coeffRef(1, 1) = H22 * 1000;
210
211 H_gps.coeffRef(2, 3) = 1;
212 H_gps.coeffRef(3, 4) = 1;
213
214 H_gps_tr = H_gps.transpose();
215
216 Matrix<float, 1, 4> R_molt = {1, 1, std::max(std::abs(gps(2)), 30.0f),
217 std::max(std::abs(gps(3)), 30.0f)};
218 Matrix<float, 4, 4> R_gps = config.SIGMA_GPS.asDiagonal().toDenseMatrix() *
219 config.SIGMA_GPS.asDiagonal().toDenseMatrix() *
220 R_molt.asDiagonal().toDenseMatrix();
221
222 // Current state [n e vn ve]
223 float lat = x(0) / Constants::gpsLatConst + reference.refLatitude;
224 float lon = x(1) / (Constants::gpsLonConst *
225 cos(lat * Constants::DEGREES_TO_RADIANS)) +
226 reference.refLongitude;
227 Matrix<float, 4, 1> z{lat, lon, x(3), x(4)};
228 Vector4f e{(gps(0) - z(0)) * 1000.0f, (gps(1) - z(1)) * 1000.0f,
229 gps(2) - z(2), gps(3) - z(3)};
230
231 Matrix<float, 6, 6> Pl = P.block<6, 6>(0, 0);
232
233 Matrix<float, 4, 4> S = H_gps * Pl * H_gps_tr + R_gps;
234
235 // If not invertible, don't do the correction and return
236 if (S.determinant() < 1e-3)
237 return;
238
239 Matrix<float, 6, 4> K = Pl * H_gps_tr * S.inverse();
240
241 // Update the state
242 x.head<6>() = x.head<6>() + K * e;
243
244 P.block<6, 6>(0, 0) = (Matrix<float, 6, 6>::Identity() - K * H_gps) * Pl;
245}
246
247void NAS::correctGPS(const GPSData& gps)
248{
249 if (gps.fix != 3)
250 return;
251
252 correctGPS(Vector4f{gps.latitude, gps.longitude, gps.velocityNorth,
253 gps.velocityEast});
254}
255
256void NAS::correctMag(const Vector3f& mag)
257{
258 Vector4f q = x.block<4, 1>(IDX_QUAT, 0);
259 Matrix3f A = SkyQuaternion::quat2rotationMatrix(q).transpose();
260
261 // Rotate the NED magnetic field in the relative reference frame
262 Vector3f mEst = A * config.NED_MAG;
263
264 // clang-format off
265 Matrix3f M{
266 { 0, -mEst(2), mEst(1)},
267 { mEst(2), 0, -mEst(0)},
268 {-mEst(1), mEst(0), 0}
269 };
270 // clang-format on
271
272 Matrix<float, 3, 6> H;
273 H << M, Matrix3f::Zero(3, 3);
274 Matrix<float, 6, 6> Pq = P.block<6, 6>(IDX_QUAT, IDX_QUAT);
275 Matrix<float, 3, 3> S = H * Pq * H.transpose() + R_mag;
276
277 Matrix<float, 6, 3> K = Pq * H.transpose() * S.inverse();
278 Matrix<float, 6, 1> dx = K * (mag - mEst);
279 Vector4f r{0.5f * dx(0), 0.5f * dx(1), 0.5f * dx(2),
280 sqrtf(1.0f - 0.25f * dx.head<3>().squaredNorm())};
281
282 x.block<4, 1>(IDX_QUAT, 0) = SkyQuaternion::quatProd(r, q);
283 x.block<3, 1>(IDX_BIAS, 0) += dx.tail<3>();
284 Matrix<float, 6, 6> tmp = Matrix<float, 6, 6>::Identity() - K * H;
285 Pq = tmp * Pq * tmp.transpose() + K * R_mag * K.transpose();
286 P.block<6, 6>(IDX_QUAT, IDX_QUAT) = Pq;
287}
288
290{
291 Vector3f magV = {mag.magneticFieldX, mag.magneticFieldY,
292 mag.magneticFieldZ};
293 correctMag(magV.normalized());
294}
295
296void NAS::correctAcc(const Vector3f& acc)
297{
298 Vector4f q = x.block<4, 1>(IDX_QUAT, 0);
299 Matrix3f A = SkyQuaternion::quat2rotationMatrix(q).transpose();
300
301 // Rotate the NED magnetic field in the relative reference frame
302 Vector3f aEst = A * gravityNed;
303 aEst.normalize();
304
305 // Gradient matrix
306 // clang-format off
307 Matrix3f M{
308 { 0, -aEst(2), aEst(1)},
309 { aEst(2), 0, -aEst(0)},
310 {-aEst(1), aEst(0), 0}
311 };
312 // clang-format on
313
314 Matrix<float, 3, 6> H;
315 H << M, Matrix3f::Zero(3, 3);
316 Matrix<float, 6, 6> Pq = P.block<6, 6>(IDX_QUAT, IDX_QUAT);
317 Matrix<float, 3, 3> S = H * Pq * H.transpose() + R_acc;
318
319 Matrix<float, 6, 3> K = Pq * H.transpose() * S.inverse();
320 Matrix<float, 6, 1> dx = K * (acc - aEst);
321 Vector4f r{0.5f * dx(0), 0.5f * dx(1), 0.5f * dx(2),
322 sqrtf(1.0f - 0.25f * dx.head<3>().squaredNorm())};
323
324 x.block<4, 1>(IDX_QUAT, 0) = SkyQuaternion::quatProd(r, q);
325 x.block<3, 1>(IDX_BIAS, 0) += dx.tail<3>();
326 Matrix<float, 6, 6> tmp = Matrix<float, 6, 6>::Identity() - K * H;
327 Pq = tmp * Pq * tmp.transpose() + K * R_mag * K.transpose();
328 P.block<6, 6>(IDX_QUAT, IDX_QUAT) = Pq;
329}
330
332{
333 Vector3f accV = {acc.accelerationX, acc.accelerationY, acc.accelerationZ};
334 correctAcc(accV.normalized());
335}
336
337void NAS::correctPitot(const float staticPressure, const float dynamicPressure)
338{
339 using namespace Constants;
340
341 const float d = x(2);
342 const float vd = x(5);
343 const float totalPressure = staticPressure + dynamicPressure;
344 const float relTemperature =
346
347 // Temporary value reused in two different formulas
348 float temp =
349 1 + (GAMMA_AIR - 1) / 2 * (vd * vd / (GAMMA_AIR * R * relTemperature));
350
351 Matrix<float, 1, 6> H = Matrix<float, 1, 6>::Zero();
352 H(0, 5) = -GAMMA_AIR * temp * vd;
353
354 // If a nan is generated, don't do the correction
355 if (isnan(H(0, 5)))
356 return;
357
358 float R = config.SIGMA_PITOT * config.SIGMA_PITOT;
359
360 Matrix<float, 6, 6> Pl = P.block<6, 6>(0, 0);
361 float S = H * Pl * H.transpose() + R;
362
363 // If not invertible, don't do the correction and return
364 if (S < 1e-3)
365 return;
366
367 float y_hat =
368 std::pow(temp, (GAMMA_AIR / (GAMMA_AIR - 1))) - 1; // pRatio_model
369 float y_measure = dynamicPressure / totalPressure; // pRatio_measure
370
371 float e = y_measure - y_hat;
372 Matrix<float, 6, 1> K = Pl * H.transpose() / S;
373 P.block<6, 6>(0, 0) = (Matrix<float, 6, 6>::Identity() - K * H) * Pl;
374
375 // Update the state
376 x.head<6>() = x.head<6>() + K * e;
377}
378
383
384Matrix<float, 13, 1> NAS::getX() const { return x; }
385
386void NAS::setState(const NASState& state) { this->x = state.getX(); }
387
388void NAS::setX(const Matrix<float, 13, 1>& x) { this->x = x; }
389
391{
392 this->reference = reference;
393}
394
396
397} // namespace Boardcore
void predictGyro(const Eigen::Vector3f &angularSpeed)
Prediction with gyroscope data.
void correctPitot(const float staticPressure, const float dynamicPressure)
Correction with pitot pressures.
Definition NAS.cpp:337
Eigen::Matrix< float, 13, 1 > getX() const
Definition NAS.cpp:384
void predictAcc(const Eigen::Vector3f &acceleration)
Prediction with accelerometer data.
static constexpr uint16_t IDX_BIAS
Definition NAS.h:51
NASState getState() const
Definition NAS.cpp:379
void correctAcc(const Eigen::Vector3f &acc)
Correction with accelerometer data.
void correctMag(const Eigen::Vector3f &mag)
Correction with magnetometer data.
void setState(const NASState &state)
Definition NAS.cpp:386
static constexpr uint16_t IDX_POS
< Index of position elements in the state.
Definition NAS.h:42
void setReferenceValues(const ReferenceValues reference)
Changes the reference values.
Definition NAS.cpp:390
void correctGPS(const Eigen::Vector4f &gps)
Correction with gps data.
NAS(NASConfig config)
Definition NAS.cpp:37
void correctBaro(const float pressure)
Correction with barometer data.
Definition NAS.cpp:157
static constexpr uint16_t IDX_VEL
Index of quaternions elements in the state.
Definition NAS.h:45
ReferenceValues getReferenceValues()
Returns the current reference values.
Definition NAS.cpp:395
static constexpr uint16_t IDX_QUAT
Index of bias elements in the state.
Definition NAS.h:48
void setX(const Eigen::Matrix< float, 13, 1 > &x)
Definition NAS.cpp:388
float relPressure(float altitude, float pressureRef, float temperatureRef)
Returns the pressure given the altitude with respect to a reference pressure and temperature,...
Definition AeroUtils.cpp:41
float relTemperature(float altitude, float temperatureRef)
Returns the temperature at the given altitude with respect to the reference temperature.
Definition AeroUtils.cpp:46
Matrix3f quat2rotationMatrix(const Vector4f &q)
Vector4f quatProd(const Vector4f &q1, const Vector4f &q2)
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
Structure to handle accelerometer data.
Definition SensorData.h:121
Structure to handle GPS data.
Definition SensorData.h:291
Structure to handle gyroscope data.
Definition SensorData.h:207
Structure to handle magnetometer data.
Definition SensorData.h:249
float T
[s] Sample period
Definition NASConfig.h:32
float SIGMA_ACC
[m/s^2] Estimated accelerometer variance
Definition NASConfig.h:35
Eigen::Vector4f SIGMA_GPS
Definition NASConfig.h:37
float SIGMA_PITOT
[Pa] Estimated variance of the pitot velocity
Definition NASConfig.h:43
float SIGMA_POS
[m] Estimated variance of the position noise
Definition NASConfig.h:41
float P_ATT
Attitude prediction covariance.
Definition NASConfig.h:51
float P_POS
Position prediction covariance horizontal.
Definition NASConfig.h:45
float SIGMA_BETA
[rad/s^2] Estimated gyroscope bias variance
Definition NASConfig.h:33
float P_BIAS
Bias prediction covariance.
Definition NASConfig.h:52
Eigen::Vector3f NED_MAG
Normalized magnetic field vector in NED frame.
Definition NASConfig.h:56
float SIGMA_W
[rad] Estimated gyroscope variance
Definition NASConfig.h:34
float P_POS_VERTICAL
Position prediction covariance vertical.
Definition NASConfig.h:46
float SIGMA_VEL
[m/s] Estimated variance of the velocity noise
Definition NASConfig.h:42
float SIGMA_MAG
[uT] Estimated magnetometer variance
Definition NASConfig.h:36
float P_VEL
Velocity prediction covariance horizontal.
Definition NASConfig.h:48
float SIGMA_BAR
[Pa] Estimated altitude variance
Definition NASConfig.h:40
float P_VEL_VERTICAL
Velocity prediction covariance vertical.
Definition NASConfig.h:49
Eigen::Matrix< float, 13, 1 > getX() const
Definition NASState.h:66
Reference values for the Apogee Detection Algorithm.