My Project 1.0
Holistic Fusion: A Generic Framework for Multimodal Sensor Fusion
Loading...
Searching...
No Matches
OptimizerIsam2FixedLag.hpp
Go to the documentation of this file.
1/*
2Copyright 2024 by Julian Nubert, Robotic Systems Lab, ETH Zurich.
3All rights reserved.
4This file is released under the "BSD-3-Clause License".
5Please see the LICENSE file that has been included as part of this package.
6 */
7
8#ifndef OPTIMIZER_ISAM2_FIXED_LAG_HPP
9#define OPTIMIZER_ISAM2_FIXED_LAG_HPP
10
11// GTSAM
12#include <gtsam/linear/linearExceptions.h>
13#include <gtsam/nonlinear/IncrementalFixedLagSmoother.h>
14
15// Workspace
17
18namespace holistic_fusion {
19
21 public:
22 explicit OptimizerIsam2FixedLag(const std::shared_ptr<GraphConfig> graphConfigPtr) : OptimizerIsam2(graphConfigPtr) {
23 // Fixed Lag --> real-time: Define whether to use Cholesky factorization
24 if (graphConfigPtr_->realTimeSmootherUseCholeskyFactorizationFlag_) {
25 isam2Params_.factorization = gtsam::ISAM2Params::CHOLESKY;
26 std::cout << "Using Cholesky factorization for real-time graph (ISAM2)." << std::endl;
27 } else {
28 isam2Params_.factorization = gtsam::ISAM2Params::QR;
29 std::cout << "Using QR factorization for real-time graph (ISAM2)." << std::endl;
30 }
31
32 // Initialize Real-time Smoother -----------------------------------------------
34 std::make_shared<gtsam::IncrementalFixedLagSmoother>(graphConfigPtr_->realTimeSmootherLag_,
35 isam2Params_); // std::make_shared<gtsam::ISAM2>(isamParams_);
36 fixedLagSmootherPtr_->params().print("HolisticFusion: Factor Graph Parameters of real-time graph.");
37 }
39
40 bool update() override {
41 fixedLagSmootherPtr_->update();
42 return true;
43 }
44
45 bool update(const gtsam::NonlinearFactorGraph& newGraphFactors, const gtsam::Values& newGraphValues,
46 const std::map<gtsam::Key, double>& newGraphKeysTimeStampMap, const int depth = 0) {
47 // Limit recursion depth to prevent infinite loops
48 if (depth > 5) {
49 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
50 << " Maximum recursion depth reached (" << depth << "). Aborting optimization."
51 << COLOR_END << std::endl;
52 return false;
53 }
54
55 // Make copy of the fixedLagSmootherPtr_ to avoid changing the original graph
56 gtsam::IncrementalFixedLagSmoother fixedLagSmootherCopy = *fixedLagSmootherPtr_;
57
58 // Try to update
59 try {
60 fixedLagSmootherPtr_->update(newGraphFactors, newGraphValues, newGraphKeysTimeStampMap);
61 }
62 // Case 1: Catching the failure of marginalization (as some states which should be marginalized have not been optimized before)
63 catch (const std::out_of_range& outOfRangeException) {
64 std::cerr << YELLOW_START << "GMsf-ISAM2" << RED_START << " OutOfRange-Exception while optimizing graph: '"
65 << outOfRangeException.what() << "'." << COLOR_END << std::endl;
66 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
67 << " This happens if the FixedLagSmoother tries to marginalize out states that have never been optimized before. This e.g. "
68 "happens if the graph hasn't been optimized for the duration of the smoother lag. Increase the lag or optimization "
69 "frequency in this case."
70 << COLOR_END << std::endl;
71
72 // Detect all factors that contain a timestamp older than the smoother lag
73 double latestTimeStamp = 0.0;
74 for (auto factor : newGraphFactors) {
75 for (auto key : factor->keys()) {
76 // Check whether key is in the keyTimestampMap
77 if (newGraphKeysTimeStampMap.find(key) != newGraphKeysTimeStampMap.end()) {
78 if (newGraphKeysTimeStampMap.at(key) > latestTimeStamp) {
79 latestTimeStamp = newGraphKeysTimeStampMap.at(key);
80 }
81 }
82 }
83 }
84 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START << " Latest timestamp in new factors: " << std::setprecision(14)
85 << latestTimeStamp << COLOR_END << std::endl;
86 // Filter out the factor that caused the error -------------------------
87 bool filteredOutAtLeastOneKey = false;
88 // Containers
89 gtsam::NonlinearFactorGraph newGraphFactorsFiltered = gtsam::NonlinearFactorGraph();
90 // Filtering the keyTimestampMap
91 std::map<gtsam::Key, double> newGraphKeysTimeStampMapFiltered = newGraphKeysTimeStampMap;
92 // For loop
93 for (auto factor : newGraphFactors) {
94 bool factorOnlyContainsExistentKeys = true;
95 for (auto key : factor->keys()) {
96 // If Exists
97 if (newGraphKeysTimeStampMap.find(key) != newGraphKeysTimeStampMap.end()) {
98 double timestampAtKey = newGraphKeysTimeStampMap.at(key);
99 // Case 1: Check if key is older than smoother lag
100 if (latestTimeStamp - timestampAtKey > graphConfigPtr_->realTimeSmootherLag_) {
101 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
102 << " Factor contains key older than smoother lag: " << gtsam::Symbol(key) << ", which is "
103 << latestTimeStamp - timestampAtKey << "s old." << COLOR_END << std::endl;
104 factorOnlyContainsExistentKeys = false;
105 filteredOutAtLeastOneKey = true;
106 }
107 // Case 2: Check if key is from the future --> hence might not be optimized
108 else if (timestampAtKey > latestTimeStamp) {
109 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START << " Factor contains key from the future: " << gtsam::Symbol(key)
110 << ", which is " << timestampAtKey << "s in the future." << COLOR_END << std::endl;
111 factorOnlyContainsExistentKeys = false;
112 filteredOutAtLeastOneKey = true;
113 }
114 }
115 }
116 // Add factor if it does not contain any key older than the smoother lag
117 if (factorOnlyContainsExistentKeys) {
118 newGraphFactorsFiltered.add(factor);
119 }
120 }
121
122 // Limit depth of recursion
123 // Try again
124 if (filteredOutAtLeastOneKey) {
125 std::cout << YELLOW_START << "GMsf-ISAM2" << GREEN_START
126 << " Filtered out factors that are either older than the smoother lag or coming from the future. Trying to optimize "
127 "again."
128 << COLOR_END << std::endl;
129 return update(newGraphFactorsFiltered, newGraphValues, newGraphKeysTimeStampMapFiltered);
130 }
131 // Nothing changed, hence abort
132 else {
133 // Show all values and corresponding timestamps that are not in timestamp map
134 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
135 << " Could not filter out any factors that are either older than the smoother lag or coming from the future. Aborting "
136 "optimization."
137 << COLOR_END << std::endl;
138 return false;
139 }
140 }
141 // Case 2: Catching the addition of a bad factor (pointing to a state that does not exist)
142 catch (const gtsam::ValuesKeyDoesNotExist& valuesKeyDoesNotExistException) {
143 std::cout << "----------------------------------------------------------" << std::endl;
144 std::cerr << YELLOW_START << "GMsf-ISAM2" << RED_START << " ValuesKeyDoesNotExist exception while optimizing graph: '" << COLOR_END
145 << valuesKeyDoesNotExistException.what() << "'" << std::endl;
146 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END
147 << " This happens if a factor is added to the graph that contains a non-existent state. The most common case is a factor "
148 "containing a graph state that was marginalized out before. To avoid this, increase the factor graph lag or reduce "
149 "the delay of your measurements."
150 << std::endl;
151
152 // Filter out the factor that caused the error -------------------------
153 gtsam::NonlinearFactorGraph newGraphFactorsFiltered;
154 gtsam::Key valuesKeyNotExistent = valuesKeyDoesNotExistException.key();
155 bool removedAtLeastOneFactorOrKey = false;
156 for (auto factor : newGraphFactors) {
157 bool factorContainsExistentKeys = true;
158 for (auto key : factor->keys()) {
159 if (key == valuesKeyNotExistent) {
160 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START << " Factor contains non-existent key: " << gtsam::Symbol(key)
161 << ". -> Removed." << COLOR_END << std::endl;
162 factorContainsExistentKeys = false;
163 removedAtLeastOneFactorOrKey = true;
164 break; // factor contains dangerous key --> break inner loop
165 }
166 }
167 if (factorContainsExistentKeys) {
168 newGraphFactorsFiltered.add(factor);
169 }
170 }
171
172 // Filter out the key from the keyTimestampMap -------------------------
173 std::map<gtsam::Key, double> newGraphKeysTimeStampMapFiltered;
174 for (auto keyTimeStamp : newGraphKeysTimeStampMap) {
175 if (keyTimeStamp.first != valuesKeyNotExistent) {
176 newGraphKeysTimeStampMapFiltered.insert(keyTimeStamp);
177 } else {
178 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
179 << " GraphKeysTimeStampMap contains non-existent key: " << gtsam::Symbol(keyTimeStamp.first) << ". -> Removed."
180 << COLOR_END << std::endl;
181 removedAtLeastOneFactorOrKey = true;
182 }
183 }
184
185 // Keys in values
186 for (auto key : newGraphValues.keys()) {
187 if (key == valuesKeyNotExistent) {
188 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START << " Values contains non-existent key: " << gtsam::Symbol(key)
189 << ". -> Removed." << COLOR_END << std::endl;
190 removedAtLeastOneFactorOrKey = true;
191 }
192 }
193
194 // Potentially try to optimize again
195 if (removedAtLeastOneFactorOrKey) {
196 std::cout << YELLOW_START << "GMsf-ISAM2" << GREEN_START
197 << " Filtered out the factor or key that caused the error (i.e. containing the key "
198 << gtsam::Symbol(valuesKeyNotExistent) << "). Trying to optimize again." << COLOR_END << std::endl;
199 std::cout << "----------------------------------------------------------" << std::endl;
200 // Copy back
201 *fixedLagSmootherPtr_ = fixedLagSmootherCopy;
202 // Try again
203 return update(newGraphFactorsFiltered, newGraphValues, newGraphKeysTimeStampMapFiltered);
204 } else {
205 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
206 << " Could not filter out any factors that caused the error (i.e. containing the key "
207 << gtsam::Symbol(valuesKeyNotExistent) << "). Aborting optimization." << COLOR_END << std::endl;
208 std::cout << "----------------------------------------------------------" << std::endl;
209 return false;
210 }
211 }
212 // Case 3: ValuesKeyAlreadyExists
213 catch (const gtsam::ValuesKeyAlreadyExists& valuesKeyAlreadyExistsException) {
214 std::cerr << YELLOW_START << "GMsf-ISAM2" << RED_START << " ValuesKeyAlreadyExists exception while optimizing graph: '"
215 << valuesKeyAlreadyExistsException.what() << "'" << COLOR_END << std::endl;
216 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END
217 << " This happens if a factor is added to the graph that contains a state that already exists. This is usually a sign of "
218 "a bug in the code. Please check the factor graph construction."
219 << std::endl;
220 throw std::runtime_error(valuesKeyAlreadyExistsException.what());
221 }
222 // Case 4: Runtime error --> can't handle explicitly
223 catch (const std::runtime_error& runtimeError) {
224 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START << " Runtime error while optimizing graph: " << runtimeError.what()
225 << COLOR_END << std::endl;
226 throw std::runtime_error(runtimeError.what());
227 }
228 // Case 5: Typical indeterminant linear system
229 catch (const gtsam::IndeterminateSystemException& indeterminantLinearSystemException) {
230 std::cerr << YELLOW_START << "GMsf-ISAM2" << RED_START << " IndeterminantLinearSystem exception while optimizing graph: '"
231 << indeterminantLinearSystemException.what() << "'" << COLOR_END << std::endl;
232 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END
233 << " This happens if the linear system is indeterminant, which usually indicates that there are not enough constraints to "
234 "solve for all variables."
235 << std::endl;
236
237 // Get key
238 gtsam::Key key = indeterminantLinearSystemException.nearbyVariable();
239 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END << " The key causing the issue is: " << gtsam::Symbol(key) << std::endl;
240
241 // If bias --> fix graph
242 const gtsam::Symbol symbol(key);
243 const char stateCategory = symbol.chr();
244 if (stateCategory == 'b') {
245 const gtsam::Key& biasKey = key; // Alias
246 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END << " Latest estimated bias for key " << symbol << ": "
247 << this->latestImuBias_ << std::endl;
248 // Get number component of symbol
249 const int symbolNumber = symbol.index();
250 gtsam::Key poseKey = gtsam::symbol_shorthand::X(symbolNumber);
251 std::cout << YELLOW_START << "GMsf-ISAM2" << COLOR_END << " Latest estimated pose for key " << gtsam::Symbol(poseKey) << ": "
252 << this->latestPose_ << std::endl;
253 // Uncertainty
254 const auto priorBiasNoise =
255 gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << graphConfigPtr_->initialAccBiasStdDev_, // m/s^2
256 graphConfigPtr_->initialAccBiasStdDev_, // m/s^2
257 graphConfigPtr_->initialAccBiasStdDev_, // m/s^2
258 graphConfigPtr_->initialGyroBiasStdDev_, // rad/s
259 graphConfigPtr_->initialGyroBiasStdDev_, // rad/s
260 graphConfigPtr_->initialGyroBiasStdDev_) // rad/s
261 .finished()); // acc, acc, acc, gyro, gyro, gyro
262
263 const auto priorPoseNoise =
264 gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << graphConfigPtr_->initialOrientationStdDev_, // rad
265 graphConfigPtr_->initialOrientationStdDev_, // rad
266 graphConfigPtr_->initialOrientationStdDev_, // rad
267 graphConfigPtr_->initialPositionStdDev_, // m
268 graphConfigPtr_->initialPositionStdDev_, // m
269 graphConfigPtr_->initialPositionStdDev_) // m
270 .finished());
271 // Add additional factor for bias
272 gtsam::NonlinearFactorGraph newGraphFactorsExtended = newGraphFactors;
273 newGraphFactorsExtended.add(gtsam::PriorFactor<gtsam::imuBias::ConstantBias>(biasKey, this->latestImuBias_, priorBiasNoise));
274 newGraphFactorsExtended.add(gtsam::PriorFactor<gtsam::Pose3>(poseKey, this->latestPose_, priorPoseNoise));
275 // Copy back
276 *fixedLagSmootherPtr_ = fixedLagSmootherCopy;
277 // Recursion
278 return update(newGraphFactorsExtended, newGraphValues, newGraphKeysTimeStampMap, depth + 1);
279 } else {
280 std::cout << YELLOW_START << "GMsf-ISAM2" << RED_START
281 << " The key causing the issue is not a bias key. Currently, only bias keys can be fixed automatically. Aborting "
282 "optimization."
283 << COLOR_END << std::endl;
284 return false;
285 }
286 throw std::runtime_error(indeterminantLinearSystemException.what());
287 }
288
291 }
292 return true;
293 }
294
295 // Optimize
296 void optimize(int maxIterations) override {
297 // Optimize
299 // Flag
301 }
302
303 // Get Result
304 const gtsam::Values& getAllOptimizedStates() override {
305 // Check
307 throw std::runtime_error("HolisticFusion: Optimizer has not been optimized yet.");
308 }
309
310 // Return
312 }
313
314 // Get all keys of optimized states
315 gtsam::KeyVector getAllOptimizedKeys() override { return fixedLagSmootherOptimizedResult_.keys(); }
316
317 // Get nonlinear factor graph
318 const gtsam::NonlinearFactorGraph& getNonlinearFactorGraph() const { return fixedLagSmootherPtr_->getFactors(); }
319
320 // Get keyTimestampMap
321 const std::map<gtsam::Key, double>& getFullKeyTimestampMap() override { return fixedLagSmootherPtr_->timestamps(); }
322
323 // Calculate State / Covariance --------------------------------------------------------------------------------------------
324 // Pose3
325 gtsam::Pose3 calculateEstimatedPose3(const gtsam::Key& key) override {
326 return fixedLagSmootherPtr_->calculateEstimate<gtsam::Pose3>(key);
327 }
328 // Velocity3
329 gtsam::Vector3 calculateEstimatedVelocity3(const gtsam::Key& key) override {
330 return fixedLagSmootherPtr_->calculateEstimate<gtsam::Vector3>(key);
331 }
332 // Bias
333 gtsam::imuBias::ConstantBias calculateEstimatedBias(const gtsam::Key& key) override {
334 return fixedLagSmootherPtr_->calculateEstimate<gtsam::imuBias::ConstantBias>(key);
335 }
336 // Point3
337 gtsam::Point3 calculateEstimatedPoint3(const gtsam::Key& key) override {
338 return fixedLagSmootherPtr_->calculateEstimate<gtsam::Point3>(key);
339 }
340 // Vector
341 gtsam::Vector calculateEstimatedVector(const gtsam::Key& key) override {
342 return fixedLagSmootherPtr_->calculateEstimate<gtsam::Vector>(key);
343 }
344
345 // Marginal Covariance
346 gtsam::Matrix calculateMarginalCovarianceMatrixAtKey(const gtsam::Key& key) override {
347 // Check
349 REGULAR_COUT << RED_START << "HolisticFusion: OptimizerIsam2FixedLag: No optimization has been performed yet." << COLOR_END << std::endl;
350 }
351 return fixedLagSmootherPtr_->marginalCovariance(key);
352 }
353
354 private:
355 // Optimizer itself
356 std::shared_ptr<gtsam::IncrementalFixedLagSmoother> fixedLagSmootherPtr_;
357 // Result
359};
360
361} // namespace holistic_fusion
362
363#endif // OPTIMIZER_ISAM2_FIXED_LAG_HPP
#define REGULAR_COUT
#define COLOR_END
#define GREEN_START
#define RED_START
#define YELLOW_START
std::shared_ptr< GraphConfig > graphConfigPtr_
gtsam::imuBias::ConstantBias latestImuBias_
const gtsam::NonlinearFactorGraph & getNonlinearFactorGraph() const
OptimizerIsam2FixedLag(const std::shared_ptr< GraphConfig > graphConfigPtr)
const gtsam::Values & getAllOptimizedStates() override
gtsam::imuBias::ConstantBias calculateEstimatedBias(const gtsam::Key &key) override
gtsam::Point3 calculateEstimatedPoint3(const gtsam::Key &key) override
gtsam::Vector3 calculateEstimatedVelocity3(const gtsam::Key &key) override
gtsam::Vector calculateEstimatedVector(const gtsam::Key &key) override
std::shared_ptr< gtsam::IncrementalFixedLagSmoother > fixedLagSmootherPtr_
bool update(const gtsam::NonlinearFactorGraph &newGraphFactors, const gtsam::Values &newGraphValues, const std::map< gtsam::Key, double > &newGraphKeysTimeStampMap, const int depth=0)
gtsam::Matrix calculateMarginalCovarianceMatrixAtKey(const gtsam::Key &key) override
gtsam::Pose3 calculateEstimatedPose3(const gtsam::Key &key) override
const std::map< gtsam::Key, double > & getFullKeyTimestampMap() override
OptimizerIsam2(const std::shared_ptr< GraphConfig > graphConfigPtr)