Line | Branch | Exec | Source |
---|---|---|---|
1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
3 | // the University of Stuttgart, Germany. | ||
4 | // | ||
5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
8 | |||
9 | /// @file InertialIntegrator.cpp | ||
10 | /// @brief Inertial Measurement Integrator | ||
11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
12 | /// @date 2023-12-09 | ||
13 | |||
14 | #include "InertialIntegrator.hpp" | ||
15 | |||
16 | #include "internal/gui/NodeEditorApplication.hpp" | ||
17 | #include "internal/gui/widgets/HelpMarker.hpp" | ||
18 | |||
19 | #include "Navigation/Ellipsoid/Ellipsoid.hpp" | ||
20 | #include "Navigation/INS/Functions.hpp" | ||
21 | #include "Navigation/Math/Math.hpp" | ||
22 | |||
23 | namespace NAV | ||
24 | { | ||
25 | |||
26 | ✗ | InertialIntegrator::InertialIntegrator(IntegrationFrame integrationFrame) | |
27 | ✗ | : _integrationFrame(integrationFrame), _lockIntegrationFrame(true) {} | |
28 | |||
29 | 51 | void InertialIntegrator::reset() | |
30 | { | ||
31 | 51 | _states.clear(); | |
32 | 51 | _p_lastBiasAcceleration.setZero(); | |
33 | 51 | _p_lastBiasAngularRate.setZero(); | |
34 | 51 | setBufferSizes(); | |
35 | 51 | } | |
36 | |||
37 | 126826 | bool InertialIntegrator::hasInitialPosition() const | |
38 | { | ||
39 | 126826 | return !_states.empty(); | |
40 | } | ||
41 | |||
42 | 17 | void InertialIntegrator::setInitialState(const PosVelAtt& state, const char* nameId) | |
43 | { | ||
44 | 17 | _states.clear(); | |
45 | 17 | addState(state, nameId); | |
46 | 17 | } | |
47 | |||
48 | 17 | void InertialIntegrator::addState(const PosVelAtt& state, [[maybe_unused]] const char* nameId) | |
49 | { | ||
50 |
16/32✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 17 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 17 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 17 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 17 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 17 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 17 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 17 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 17 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 17 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 17 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 17 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 17 times.
✗ Branch 47 not taken.
|
85 | _states.push_back(State{ |
51 | .epoch = state.insTime, | ||
52 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .position = _integrationFrame == IntegrationFrame::ECEF ? state.e_position() : state.lla_position(), |
53 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .velocity = _integrationFrame == IntegrationFrame::ECEF ? state.e_velocity() : state.n_velocity(), |
54 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | .attitude = _integrationFrame == IntegrationFrame::ECEF ? state.e_Quat_b() : state.n_Quat_b(), |
55 | .m = Measurement{ | ||
56 | .averagedMeasurement = false, | ||
57 | .dt = 0.0, | ||
58 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | .p_acceleration = Eigen::Vector3d::Zero(), |
59 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | .p_angularRate = Eigen::Vector3d::Zero(), |
60 | } }); | ||
61 | LOG_DATA("{}: Adding state for [{}]. Now has {} states", nameId, state.insTime.toYMDHMS(GPST), _states.size()); | ||
62 | 17 | } | |
63 | |||
64 | 17 | void InertialIntegrator::setTotalSensorBiases(const Eigen::Vector3d& p_biasAcceleration, const Eigen::Vector3d& p_biasAngularRate) | |
65 | { | ||
66 | 17 | _p_lastBiasAcceleration = p_biasAcceleration; | |
67 | 17 | _p_lastBiasAngularRate = p_biasAngularRate; | |
68 | 17 | } | |
69 | |||
70 | 26813 | void InertialIntegrator::applySensorBiasesIncrements(const Eigen::Vector3d& p_deltaBiasAcceleration, const Eigen::Vector3d& p_deltaBiasAngularRate) | |
71 | { | ||
72 | 26813 | _p_lastBiasAcceleration += p_deltaBiasAcceleration; | |
73 | 26813 | _p_lastBiasAngularRate += p_deltaBiasAngularRate; | |
74 | 26813 | } | |
75 | |||
76 | 25517 | void InertialIntegrator::applyStateErrors_n(const Eigen::Vector3d& lla_positionError, const Eigen::Vector3d& n_velocityError, const Eigen::Vector3d& n_attitudeError_b) | |
77 | { | ||
78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25517 times.
|
25517 | INS_ASSERT_USER_ERROR(_integrationFrame == IntegrationFrame::NED, "You can only apply errors to the selected integration frame"); |
79 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 25517 times.
✗ Branch 4 not taken.
|
25517 | INS_ASSERT_USER_ERROR(!_states.empty(), "You can only apply errors if the states vector is not empty"); |
80 | |||
81 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
|
25517 | _states.back().position -= lla_positionError; |
82 |
2/4✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
|
25517 | _states.back().velocity -= n_velocityError; |
83 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.15 | ||
84 |
7/14✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25517 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 25517 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 25517 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 25517 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 25517 times.
✗ Branch 20 not taken.
|
25517 | Eigen::Matrix3d n_Dcm_b = (Eigen::Matrix3d::Identity() - math::skewSymmetricMatrix(n_attitudeError_b)) * _states.back().attitude.toRotationMatrix(); |
85 |
3/6✓ Branch 1 taken 25517 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25517 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25517 times.
✗ Branch 8 not taken.
|
25517 | _states.back().attitude = Eigen::Quaterniond(n_Dcm_b).normalized(); |
86 | |||
87 | // TODO: Test this out again | ||
88 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.16 | ||
89 | // Eigen::Quaterniond n_Quat_b = posVelAtt->n_Quat_b() | ||
90 | // * (Eigen::AngleAxisd(attError(0), Eigen::Vector3d::UnitX()) | ||
91 | // * Eigen::AngleAxisd(attError(1), Eigen::Vector3d::UnitY()) | ||
92 | // * Eigen::AngleAxisd(attError(2), Eigen::Vector3d::UnitZ())) | ||
93 | // .normalized(); | ||
94 | // posVelAttCorrected->setAttitude_n_Quat_b(n_Quat_b.normalized()); | ||
95 | |||
96 | // Eigen::Vector3d attError = pvaError->n_attitudeError(); | ||
97 | // const Eigen::Quaterniond& n_Quat_b = posVelAtt->n_Quat_b(); | ||
98 | // Eigen::Quaterniond n_Quat_b_c{ n_Quat_b.w() + 0.5 * (+attError(0) * n_Quat_b.x() + attError(1) * n_Quat_b.y() + attError(2) * n_Quat_b.z()), | ||
99 | // n_Quat_b.x() + 0.5 * (-attError(0) * n_Quat_b.w() + attError(1) * n_Quat_b.z() - attError(2) * n_Quat_b.y()), | ||
100 | // n_Quat_b.y() + 0.5 * (-attError(0) * n_Quat_b.z() - attError(1) * n_Quat_b.w() + attError(2) * n_Quat_b.x()), | ||
101 | // n_Quat_b.z() + 0.5 * (+attError(0) * n_Quat_b.y() - attError(1) * n_Quat_b.x() - attError(2) * n_Quat_b.w()) }; | ||
102 | // posVelAttCorrected->setAttitude_n_Quat_b(n_Quat_b_c.normalized()); | ||
103 | 25517 | } | |
104 | |||
105 | 1296 | void InertialIntegrator::applyStateErrors_e(const Eigen::Vector3d& e_positionError, const Eigen::Vector3d& e_velocityError, const Eigen::Vector3d& e_attitudeError_b) | |
106 | { | ||
107 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1296 times.
|
1296 | INS_ASSERT_USER_ERROR(_integrationFrame == IntegrationFrame::ECEF, "You can only apply errors to the selected integration frame"); |
108 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1296 times.
✗ Branch 4 not taken.
|
1296 | INS_ASSERT_USER_ERROR(!_states.empty(), "You can only apply errors if the states vector is not empty"); |
109 | |||
110 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
|
1296 | _states.back().position -= e_positionError; |
111 |
2/4✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
|
1296 | _states.back().velocity -= e_velocityError; |
112 | // Attitude correction, see Titterton and Weston (2004), p. 407 eq. 13.15 | ||
113 |
7/14✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1296 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1296 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1296 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1296 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1296 times.
✗ Branch 20 not taken.
|
1296 | Eigen::Matrix3d e_Dcm_b = (Eigen::Matrix3d::Identity() - math::skewSymmetricMatrix(e_attitudeError_b)) * _states.back().attitude.toRotationMatrix(); |
114 |
3/6✓ Branch 1 taken 1296 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1296 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1296 times.
✗ Branch 8 not taken.
|
1296 | _states.back().attitude = Eigen::Quaterniond(e_Dcm_b).normalized(); |
115 | 1296 | } | |
116 | |||
117 | 543633 | std::optional<std::reference_wrapper<const InertialIntegrator::State>> InertialIntegrator::getLatestState() const | |
118 | { | ||
119 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 543633 times.
|
543633 | if (_states.empty()) { return {}; } |
120 |
1/2✓ Branch 1 taken 543633 times.
✗ Branch 2 not taken.
|
543633 | return _states.back(); |
121 | } | ||
122 | |||
123 | 52589 | const Eigen::Vector3d& InertialIntegrator::p_getLastAccelerationBias() const | |
124 | { | ||
125 | 52589 | return _p_lastBiasAcceleration; | |
126 | } | ||
127 | |||
128 | 52589 | const Eigen::Vector3d& InertialIntegrator::p_getLastAngularRateBias() const | |
129 | { | ||
130 | 52589 | return _p_lastBiasAngularRate; | |
131 | } | ||
132 | |||
133 | 270250 | InertialIntegrator::IntegrationFrame InertialIntegrator::getIntegrationFrame() const | |
134 | { | ||
135 | 270250 | return _integrationFrame; | |
136 | } | ||
137 | |||
138 | ✗ | const PosVelAttDerivativeConstants& InertialIntegrator::getConstants() const | |
139 | { | ||
140 | ✗ | return _posVelAttDerivativeConstants; | |
141 | } | ||
142 | |||
143 | ✗ | bool InertialIntegrator::areAccelerationsAveragedMeasurements() const | |
144 | { | ||
145 | ✗ | return _accelerationsAreAveragedMeasurements; | |
146 | } | ||
147 | |||
148 | 49997 | std::optional<Eigen::Vector3d> InertialIntegrator::p_calcCurrentAcceleration() const | |
149 | { | ||
150 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 49997 times.
|
49997 | if (_states.empty()) { return {}; } |
151 | |||
152 |
4/8✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
|
49997 | return _states.back().m.p_acceleration + _states.back().p_biasAcceleration; |
153 | } | ||
154 | |||
155 | 26813 | std::optional<Eigen::Vector3d> InertialIntegrator::p_calcCurrentAngularRate() const | |
156 | { | ||
157 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26813 times.
|
26813 | if (_states.empty()) { return {}; } |
158 | |||
159 |
4/8✓ Branch 1 taken 26813 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26813 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 26813 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 26813 times.
✗ Branch 11 not taken.
|
26813 | return _states.back().m.p_angularRate + _states.back().p_biasAngularRate; |
160 | } | ||
161 | |||
162 | 49998 | void InertialIntegrator::addMeasurement(const InsTime& epoch, double dt, const Eigen::Vector3d& p_acceleration, const Eigen::Vector3d& p_angularRate, [[maybe_unused]] const char* nameId) | |
163 | { | ||
164 | LOG_DATA("{}: Adding measurement [{}]. Last state at [{}]", nameId, epoch.toYMDHMS(GPST), _states.back().epoch.toYMDHMS(GPST)); | ||
165 |
1/2✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
|
49998 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
166 | |||
167 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 49997 times.
|
49998 | if (_states.back().epoch == epoch) |
168 | { | ||
169 | LOG_DATA("{}: Updating existing state", nameId); | ||
170 | 1 | _states.back().m.averagedMeasurement = _accelerationsAreAveragedMeasurements; | |
171 | 1 | _states.back().m.dt = dt; | |
172 | 1 | _states.back().m.p_acceleration = p_acceleration; | |
173 | 1 | _states.back().m.p_angularRate = p_angularRate; | |
174 | 1 | _states.back().p_biasAcceleration = _p_lastBiasAcceleration; | |
175 | 1 | _states.back().p_biasAngularRate = _p_lastBiasAngularRate; | |
176 | 1 | return; | |
177 | } | ||
178 | |||
179 | LOG_DATA("{}: Adding as new state", nameId); | ||
180 |
8/16✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 49997 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 49997 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 49997 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 49997 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 49997 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 49997 times.
✗ Branch 23 not taken.
|
99994 | _states.push_back(State{ |
181 | .epoch = epoch, | ||
182 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .position = _states.back().position, |
183 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .velocity = _states.back().velocity, |
184 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49997 times.
✗ Branch 5 not taken.
|
49997 | .attitude = _states.back().attitude, |
185 | .m = Measurement{ | ||
186 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .averagedMeasurement = _accelerationsAreAveragedMeasurements, |
187 | .dt = dt, | ||
188 | .p_acceleration = p_acceleration, | ||
189 | .p_angularRate = p_angularRate, | ||
190 | }, | ||
191 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .p_biasAcceleration = _p_lastBiasAcceleration, |
192 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | .p_biasAngularRate = _p_lastBiasAngularRate, |
193 | }); | ||
194 | } | ||
195 | |||
196 | ✗ | void InertialIntegrator::addDeltaMeasurement(const InsTime& epoch, double dt, double deltaTime, const Eigen::Vector3d& p_deltaVelocity, const Eigen::Vector3d& p_deltaTheta, const char* nameId) | |
197 | { | ||
198 | LOG_DATA("{}: Adding delta measurement for [{}]", nameId, epoch.toYMDHMS(GPST)); | ||
199 | ✗ | Eigen::Vector3d p_acceleration = Eigen::Vector3d::Zero(); | |
200 | ✗ | Eigen::Vector3d p_angularRate = Eigen::Vector3d::Zero(); | |
201 | |||
202 | ✗ | if (deltaTime > 0.0) // dt given by sensor (should never be 0 or negative, but check here just in case) | |
203 | { | ||
204 | ✗ | p_acceleration = p_deltaVelocity / deltaTime; | |
205 | ✗ | p_angularRate = p_deltaTheta / deltaTime; | |
206 | } | ||
207 | ✗ | else if (std::abs(dt) > 0.0) // Time difference between messages (differs from dt if message lost) | |
208 | { | ||
209 | // Negative values of dTimeLastState should not happen, but algorithm can work with it to propagate backwards | ||
210 | ✗ | p_acceleration = p_deltaVelocity / dt; | |
211 | ✗ | p_angularRate = p_deltaTheta / dt; | |
212 | } | ||
213 | |||
214 | ✗ | addMeasurement(epoch, dt, p_acceleration, p_angularRate, nameId); | |
215 | ✗ | _states.back().m.averagedMeasurement = true; | |
216 | ✗ | } | |
217 | |||
218 | 1 | std::shared_ptr<PosVelAtt> InertialIntegrator::lastStateAsPosVelAtt() const | |
219 | { | ||
220 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
221 | |||
222 | 1 | auto posVelAtt = std::make_shared<PosVelAtt>(); | |
223 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | posVelAtt->insTime = _states.back().epoch; |
224 |
1/3✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
1 | switch (_integrationFrame) |
225 | { | ||
226 | 1 | case IntegrationFrame::NED: | |
227 |
4/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
|
1 | posVelAtt->setPosVelAtt_n(_states.back().position, _states.back().velocity, _states.back().attitude); |
228 | 1 | break; | |
229 | ✗ | case IntegrationFrame::ECEF: | |
230 | ✗ | posVelAtt->setPosVelAtt_e(_states.back().position, _states.back().velocity, _states.back().attitude); | |
231 | ✗ | break; | |
232 | } | ||
233 | |||
234 | 1 | return posVelAtt; | |
235 | ✗ | } | |
236 | |||
237 | 49998 | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolution(const InsTime& obsTime, | |
238 | const Eigen::Vector3d& p_acceleration, const Eigen::Vector3d& p_angularRate, | ||
239 | const ImuPos& imuPos, const char* nameId) | ||
240 | { | ||
241 |
3/6✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 49998 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 49998 times.
|
49998 | if (!hasInitialPosition() || obsTime < _states.back().epoch) { return nullptr; } |
242 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 49998 times.
|
49998 | if (_states.back().epoch.empty()) { _states.back().epoch = obsTime; } |
243 | |||
244 |
2/4✓ Branch 1 taken 49998 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 49998 times.
✗ Branch 5 not taken.
|
49998 | auto dt = static_cast<double>((obsTime - _states.back().epoch).count()); |
245 | 49998 | addMeasurement(obsTime, dt, p_acceleration, p_angularRate, nameId); | |
246 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 49997 times.
|
49998 | if (std::abs(dt) < 1e-8) |
247 | { | ||
248 | LOG_DATA("{}: Returning state [{}], as no time difference between measurement.", nameId, _states.back().epoch.toYMDHMS(GPST)); | ||
249 | 1 | return lastStateAsPosVelAtt(); | |
250 | } | ||
251 | |||
252 | 49997 | return calcInertialSolutionFromMeasurementBuffer(imuPos, nameId); | |
253 | } | ||
254 | |||
255 | ✗ | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolutionDelta(const InsTime& obsTime, double deltaTime, | |
256 | const Eigen::Vector3d& p_deltaVelocity, const Eigen::Vector3d& p_deltaTheta, | ||
257 | const ImuPos& imuPos, const char* nameId) | ||
258 | { | ||
259 | ✗ | if (!hasInitialPosition() || obsTime < _states.back().epoch) { return nullptr; } | |
260 | ✗ | if (_states.back().epoch.empty()) { _states.back().epoch = obsTime; } | |
261 | |||
262 | ✗ | auto dt = static_cast<double>((obsTime - _states.back().epoch).count()); | |
263 | ✗ | addDeltaMeasurement(obsTime, dt, deltaTime, p_deltaVelocity, p_deltaTheta, nameId); | |
264 | ✗ | if (std::abs(dt) < 1e-8) | |
265 | { | ||
266 | LOG_DATA("{}: Returning state [{}], as no time difference between measurement.", nameId, _states.back().epoch.toYMDHMS(GPST)); | ||
267 | ✗ | return lastStateAsPosVelAtt(); | |
268 | } | ||
269 | |||
270 | ✗ | return calcInertialSolutionFromMeasurementBuffer(imuPos, nameId); | |
271 | } | ||
272 | |||
273 | 49997 | std::shared_ptr<PosVelAtt> InertialIntegrator::calcInertialSolutionFromMeasurementBuffer(const ImuPos& imuPos, const char* nameId) | |
274 | { | ||
275 |
2/4✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 49997 times.
✗ Branch 4 not taken.
|
49997 | INS_ASSERT_USER_ERROR(!_states.empty(), "You need to add an initial state first"); |
276 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | INS_ASSERT_USER_ERROR(_states.size() >= 2, "You need to add an initial state and at least one measurement first"); |
277 | |||
278 | LOG_DATA("{}: Calculating inertial solution from buffer. States t0 = [{}], t1 = [{}]", | ||
279 | nameId, _states.back().epoch.toYMDHMS(GPST), _states.at(_states.size() - 2).epoch.toYMDHMS(GPST)); | ||
280 | |||
281 |
3/6✓ Branch 2 taken 49997 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 49997 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 49997 times.
✗ Branch 9 not taken.
|
49997 | auto y = calcInertialSolution(imuPos, _states.back(), _states.at(_states.size() - 2), nameId); |
282 | |||
283 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | auto posVelAtt = std::make_shared<PosVelAtt>(); |
284 |
1/2✓ Branch 1 taken 49997 times.
✗ Branch 2 not taken.
|
49997 | posVelAtt->insTime = _states.back().epoch; |
285 |
2/3✓ Branch 0 taken 37109 times.
✓ Branch 1 taken 12888 times.
✗ Branch 2 not taken.
|
49997 | switch (_integrationFrame) |
286 | { | ||
287 | 37109 | case IntegrationFrame::NED: | |
288 |
5/10✓ Branch 2 taken 37109 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 37109 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 37109 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 37109 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 37109 times.
✗ Branch 15 not taken.
|
37109 | posVelAtt->setPosVelAtt_n(y.segment<3>(0), y.segment<3>(3), Eigen::Quaterniond(y.segment<4>(6))); |
289 | 37109 | break; | |
290 | 12888 | case IntegrationFrame::ECEF: | |
291 |
5/10✓ Branch 2 taken 12888 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12888 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12888 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 12888 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 12888 times.
✗ Branch 15 not taken.
|
12888 | posVelAtt->setPosVelAtt_e(y.segment<3>(0), y.segment<3>(3), Eigen::Quaterniond(y.segment<4>(6))); |
292 | 12888 | break; | |
293 | } | ||
294 | |||
295 |
2/3✓ Branch 0 taken 37109 times.
✓ Branch 1 taken 12888 times.
✗ Branch 2 not taken.
|
49997 | switch (_integrationFrame) |
296 | { | ||
297 | 37109 | case IntegrationFrame::NED: | |
298 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().position = posVelAtt->lla_position(); |
299 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().velocity = posVelAtt->n_velocity(); |
300 |
2/4✓ Branch 3 taken 37109 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37109 times.
✗ Branch 7 not taken.
|
37109 | _states.back().attitude = posVelAtt->n_Quat_b(); |
301 | 37109 | break; | |
302 | 12888 | case IntegrationFrame::ECEF: | |
303 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().position = posVelAtt->e_position(); |
304 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().velocity = posVelAtt->e_velocity(); |
305 |
2/4✓ Branch 3 taken 12888 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12888 times.
✗ Branch 7 not taken.
|
12888 | _states.back().attitude = posVelAtt->e_Quat_b(); |
306 | 12888 | break; | |
307 | } | ||
308 | |||
309 | 99994 | return posVelAtt; | |
310 | ✗ | } | |
311 | |||
312 | 69 | void InertialIntegrator::setBufferSizes() | |
313 | { | ||
314 |
1/4✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
69 | switch (_integrationAlgorithm) |
315 | { | ||
316 | 69 | case IntegrationAlgorithm::SingleStepRungeKutta1: | |
317 | case IntegrationAlgorithm::SingleStepRungeKutta2: | ||
318 | case IntegrationAlgorithm::SingleStepHeun2: | ||
319 | case IntegrationAlgorithm::SingleStepRungeKutta3: | ||
320 | case IntegrationAlgorithm::SingleStepHeun3: | ||
321 | case IntegrationAlgorithm::SingleStepRungeKutta4: | ||
322 | 69 | _states.resize(2); | |
323 | 69 | break; | |
324 | ✗ | case IntegrationAlgorithm::MultiStepRK3: | |
325 | case IntegrationAlgorithm::MultiStepRK4: | ||
326 | ✗ | _states.resize(3); | |
327 | ✗ | break; | |
328 | ✗ | case IntegrationAlgorithm::COUNT: | |
329 | ✗ | break; | |
330 | } | ||
331 | 69 | } | |
332 | |||
333 | ✗ | bool InertialIntegratorGui(const char* label, InertialIntegrator& integrator, bool& preferAccelerationOverDeltaMeasurements, float width) | |
334 | { | ||
335 | ✗ | bool changed = false; | |
336 | |||
337 | ✗ | if (integrator._lockIntegrationFrame) { ImGui::BeginDisabled(); } | |
338 | ✗ | ImGui::SetNextItemWidth(width * gui::NodeEditorApplication::windowFontRatio()); | |
339 | ✗ | if (auto integrationFrame = static_cast<int>(integrator._integrationFrame); | |
340 | ✗ | ImGui::Combo(fmt::format("Integration Frame##{}", label).c_str(), &integrationFrame, "ECEF\0NED\0\0")) | |
341 | { | ||
342 | ✗ | integrator._integrationFrame = static_cast<decltype(integrator._integrationFrame)>(integrationFrame); | |
343 | ✗ | LOG_DEBUG("Integration Frame changed to {}", integrator._integrationFrame == InertialIntegrator::IntegrationFrame::NED ? "NED" : "ECEF"); | |
344 | ✗ | changed = true; | |
345 | } | ||
346 | ✗ | if (integrator._lockIntegrationFrame) { ImGui::EndDisabled(); } | |
347 | |||
348 | ✗ | ImGui::SetNextItemWidth(width * gui::NodeEditorApplication::windowFontRatio()); | |
349 | ✗ | if (ImGui::BeginCombo(fmt::format("Integration Algorithm##{}", label).c_str(), to_string(integrator._integrationAlgorithm))) | |
350 | { | ||
351 | ✗ | for (size_t i = 0; i < static_cast<size_t>(InertialIntegrator::IntegrationAlgorithm::MultiStepRK3); i++) // TODO: InertialIntegrator::IntegrationAlgorithm::COUNT | |
352 | { | ||
353 | ✗ | const bool is_selected = (static_cast<size_t>(integrator._integrationAlgorithm) == i); | |
354 | ✗ | if (ImGui::Selectable(to_string(static_cast<InertialIntegrator::IntegrationAlgorithm>(i)), is_selected)) | |
355 | { | ||
356 | ✗ | integrator._integrationAlgorithm = static_cast<InertialIntegrator::IntegrationAlgorithm>(i); | |
357 | ✗ | integrator.setBufferSizes(); | |
358 | ✗ | LOG_DEBUG("Integration Algorithm Attitude changed to {}", fmt::underlying(integrator._integrationAlgorithm)); | |
359 | ✗ | changed = true; | |
360 | } | ||
361 | |||
362 | // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) | ||
363 | ✗ | if (is_selected) | |
364 | { | ||
365 | ✗ | ImGui::SetItemDefaultFocus(); | |
366 | } | ||
367 | } | ||
368 | |||
369 | ✗ | ImGui::EndCombo(); | |
370 | } | ||
371 | |||
372 | ✗ | changed |= ImGui::Checkbox(fmt::format("Prefer raw measurements over delta##{}", label).c_str(), &preferAccelerationOverDeltaMeasurements); | |
373 | |||
374 | ✗ | if (preferAccelerationOverDeltaMeasurements) | |
375 | { | ||
376 | ✗ | changed |= ImGui::Checkbox(fmt::format("Accumulated Acceleration##{}", label).c_str(), &integrator._accelerationsAreAveragedMeasurements); | |
377 | ✗ | ImGui::SameLine(); | |
378 | ✗ | gui::widgets::HelpMarker("Some IMUs operate at higher rates than their output rate. (e.g. internal rate 800Hz and output rate is 100Hz)\n" | |
379 | "- Such IMUs often output averaged accelerations between the current and last output.\n" | ||
380 | "- If the connected IMU (e.g. a VectorNavSensor node) is such as sensor, please check this.\n" | ||
381 | "- If delta measurements are used, this is automatically true"); | ||
382 | } | ||
383 | |||
384 | ✗ | ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver); | |
385 | ✗ | if (ImGui::TreeNode(fmt::format("Compensation models##{}", label).c_str())) | |
386 | { | ||
387 | ✗ | ImGui::TextUnformatted("Acceleration compensation"); | |
388 | { | ||
389 | ✗ | ImGui::Indent(); | |
390 | ✗ | ImGui::SetNextItemWidth(230 * gui::NodeEditorApplication::windowFontRatio()); | |
391 | ✗ | if (ComboGravitationModel(fmt::format("Gravitation Model##{}", label).c_str(), integrator._posVelAttDerivativeConstants.gravitationModel)) | |
392 | { | ||
393 | ✗ | LOG_DEBUG("Gravity Model changed to {}", NAV::to_string(integrator._posVelAttDerivativeConstants.gravitationModel)); | |
394 | ✗ | changed = true; | |
395 | } | ||
396 | ✗ | if (ImGui::Checkbox(fmt::format("Coriolis acceleration ##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.coriolisAccelerationCompensationEnabled)) | |
397 | { | ||
398 | ✗ | LOG_DEBUG("coriolisAccelerationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.coriolisAccelerationCompensationEnabled); | |
399 | ✗ | changed = true; | |
400 | } | ||
401 | ✗ | if (ImGui::Checkbox(fmt::format("Centrifugal acceleration##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.centrifgalAccelerationCompensationEnabled)) | |
402 | { | ||
403 | ✗ | LOG_DEBUG("centrifgalAccelerationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.centrifgalAccelerationCompensationEnabled); | |
404 | ✗ | changed = true; | |
405 | } | ||
406 | ✗ | ImGui::Unindent(); | |
407 | } | ||
408 | ✗ | ImGui::TextUnformatted("Angular rate compensation"); | |
409 | { | ||
410 | ✗ | ImGui::Indent(); | |
411 | ✗ | if (ImGui::Checkbox(fmt::format("Earth rotation rate##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.angularRateEarthRotationCompensationEnabled)) | |
412 | { | ||
413 | ✗ | LOG_DEBUG("angularRateEarthRotationCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.angularRateEarthRotationCompensationEnabled); | |
414 | ✗ | changed = true; | |
415 | } | ||
416 | ✗ | if (integrator._integrationFrame == InertialIntegrator::IntegrationFrame::NED) | |
417 | { | ||
418 | ✗ | if (ImGui::Checkbox(fmt::format("Transport rate##{}", label).c_str(), &integrator._posVelAttDerivativeConstants.angularRateTransportRateCompensationEnabled)) | |
419 | { | ||
420 | ✗ | LOG_DEBUG("angularRateTransportRateCompensationEnabled changed to {}", integrator._posVelAttDerivativeConstants.angularRateTransportRateCompensationEnabled); | |
421 | ✗ | changed = true; | |
422 | } | ||
423 | } | ||
424 | ✗ | ImGui::Unindent(); | |
425 | } | ||
426 | |||
427 | ✗ | ImGui::TreePop(); | |
428 | } | ||
429 | |||
430 | ✗ | return changed; | |
431 | } | ||
432 | |||
433 | ✗ | void to_json(json& j, const InertialIntegrator& data) | |
434 | { | ||
435 | ✗ | j = json{ | |
436 | ✗ | { "integrationFrame", data._integrationFrame }, | |
437 | ✗ | { "lockIntegrationFrame", data._lockIntegrationFrame }, | |
438 | ✗ | { "integrationAlgorithm", data._integrationAlgorithm }, | |
439 | ✗ | { "accelerationsAreAveragedMeasurements", data._accelerationsAreAveragedMeasurements }, | |
440 | ✗ | { "posVelAttDerivativeConstants", data._posVelAttDerivativeConstants }, | |
441 | ✗ | }; | |
442 | ✗ | } | |
443 | |||
444 | 18 | void from_json(const json& j, InertialIntegrator& data) | |
445 | { | ||
446 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("integrationFrame")) { j.at("integrationFrame").get_to(data._integrationFrame); } |
447 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("lockIntegrationFrame")) { j.at("lockIntegrationFrame").get_to(data._lockIntegrationFrame); } |
448 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("integrationAlgorithm")) |
449 | { | ||
450 | 18 | j.at("integrationAlgorithm").get_to(data._integrationAlgorithm); | |
451 | 18 | data.setBufferSizes(); | |
452 | } | ||
453 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("accelerationsAreAveragedMeasurements")) { j.at("accelerationsAreAveragedMeasurements").get_to(data._accelerationsAreAveragedMeasurements); } |
454 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if (j.contains("posVelAttDerivativeConstants")) { j.at("posVelAttDerivativeConstants").get_to(data._posVelAttDerivativeConstants); } |
455 | 18 | } | |
456 | |||
457 | ✗ | const char* to_string(InertialIntegrator::IntegrationAlgorithm algorithm) | |
458 | { | ||
459 | ✗ | switch (algorithm) | |
460 | { | ||
461 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta1: | |
462 | ✗ | return "Runge-Kutta 1st order (explicit) / (Forward) Euler method"; | |
463 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta2: | |
464 | ✗ | return "Runge-Kutta 2nd order (explicit)"; | |
465 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepHeun2: | |
466 | ✗ | return "Heun's method (2nd order) (explicit)"; | |
467 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta3: | |
468 | ✗ | return "Runge-Kutta 3rd order (explicit) / Simpson's rule"; | |
469 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepHeun3: | |
470 | ✗ | return "Heun's method (3nd order) (explicit)"; | |
471 | ✗ | case InertialIntegrator::IntegrationAlgorithm::SingleStepRungeKutta4: | |
472 | ✗ | return "Runge-Kutta 4th order (explicit)"; | |
473 | ✗ | case InertialIntegrator::IntegrationAlgorithm::MultiStepRK3: | |
474 | ✗ | return "Runge-Kutta 3rd order (explicit) / Simpson's rule (multistep)"; | |
475 | ✗ | case InertialIntegrator::IntegrationAlgorithm::MultiStepRK4: | |
476 | ✗ | return "Runge-Kutta 4th order (explicit) (multistep)"; | |
477 | ✗ | case InertialIntegrator::IntegrationAlgorithm::COUNT: | |
478 | ✗ | return ""; | |
479 | } | ||
480 | ✗ | return ""; | |
481 | } | ||
482 | |||
483 | ✗ | const char* to_string(InertialIntegrator::IntegrationFrame frame) | |
484 | { | ||
485 | ✗ | switch (frame) | |
486 | { | ||
487 | ✗ | case InertialIntegrator::IntegrationFrame::ECEF: | |
488 | ✗ | return "ECEF"; | |
489 | ✗ | case InertialIntegrator::IntegrationFrame::NED: | |
490 | ✗ | return "NED"; | |
491 | } | ||
492 | ✗ | return ""; | |
493 | } | ||
494 | |||
495 | } // namespace NAV | ||
496 |