My Project 1.0
Holistic Fusion: A Generic Framework for Multimodal Sensor Fusion
Loading...
Searching...
No Matches
GmsfUnaryExpressionAbsolut.h
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 GMSF_UNARY_EXPRESSION_ABSOLUT_H
9#define GMSF_UNARY_EXPRESSION_ABSOLUT_H
10
11// C++
12#include <mutex>
13#include <stdexcept>
14#include <string>
15
16// GTSAM
17#include <gtsam/base/types.h>
18#include <gtsam/navigation/NavState.h>
19
20// Workspace
23
24namespace holistic_fusion {
25
26template <class GTSAM_MEASUREMENT_TYPE, char CALIBRATION_CHAR>
27class GmsfUnaryExpressionAbsolut : public GmsfUnaryExpression<GTSAM_MEASUREMENT_TYPE, UnaryExpressionType::Absolute, CALIBRATION_CHAR> {
28 public:
29 // Constructor
30 GmsfUnaryExpressionAbsolut(const std::shared_ptr<UnaryMeasurementAbsolute>& gmsfUnaryAbsoluteMeasurementPtr,
31 const std::string& imuFrameName, const Eigen::Isometry3d& T_I_sensorFrame,
32 const double createReferenceAlignmentKeyframeEveryNSeconds)
33 : GmsfUnaryExpression<GTSAM_MEASUREMENT_TYPE, UnaryExpressionType::Absolute, CALIBRATION_CHAR>(gmsfUnaryAbsoluteMeasurementPtr,
34 imuFrameName, T_I_sensorFrame),
35 gmsfUnaryAbsoluteMeasurementPtr_(gmsfUnaryAbsoluteMeasurementPtr),
36 createReferenceAlignmentKeyframeEveryNSeconds_(createReferenceAlignmentKeyframeEveryNSeconds) {}
37
38 // Destructor
40
41 protected:
42 // Virtual Methods --------------------------------------------------------------
43 // ii).A Holistically Optimize over Fixed Frames
45 const gtsam::NavState& W_currentPropagatedState,
46 const bool centerMeasurementsAtKeyframePositionBeforeAlignmentFlag) final {
47 if (gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName() == gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName()) {
48 // Do nothing as this is a world frame measurement
49 return;
50 }
51
52 // Mutex because we are changing the dynamically allocated graphKeys
53 std::lock_guard<std::mutex> modifyGraphKeysLock(gtsamDynamicExpressionKeys.get<gtsam::Pose3>().mutex());
54
55 // Run through steps needed for absolute measurements
56 // If it should be centered --> create keyframe for measurement
57 Eigen::Vector3d measurementOriginPosition = Eigen::Vector3d::Zero();
58 if (centerMeasurementsAtKeyframePositionBeforeAlignmentFlag && this->measuresPosition()) {
59 measurementOriginPosition = this->getMeasurementPosition();
60 }
61
62 // Initial Guess
63 gtsam::Pose3 T_W_fixedFrame_initial = this->computeT_W_fixedFrame_initial(W_currentPropagatedState);
64
65 // If initial guess is externally set
66 bool initialGuessSetExternally = false;
67 if (gtsamDynamicExpressionKeys.get<gtsam::Pose3>().getInitialGuessForFramePair(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(),
68 gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName(),
69 T_W_fixedFrame_initial)) {
70 initialGuessSetExternally = true;
71 REGULAR_COUT << GREEN_START << " Initial guess for transform " << gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName() << " to "
72 << gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName() << " was set externally." << COLOR_END << std::endl;
73 // Remove from map
74 gtsamDynamicExpressionKeys.get<gtsam::Pose3>().removeInitialGuessForFramePair(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(),
75 gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName());
76 }
77
78 // A. Search for the new graph key of T_fixedFrame_W -----------------------------------------------------
79 if (!this->measuresPosition() &&
80 !gtsamDynamicExpressionKeys.get<gtsam::Pose3>().isFramePairInDictionary(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(),
81 gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName())) {
82 throw std::logic_error("GmsfUnaryExpressionAbsolut: a measurement without a position cannot create the keyframe of " +
83 gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName() +
84 ". Add a measurement with a position of the same frame (e.g. a position measurement) before this one.");
85 }
86 bool newGraphKeyAddedFlag = false;
87 const DynamicVariableType dynamicVariableType =
88 DynamicVariableType::RefFrame(measurementOriginPosition, gmsfUnaryAbsoluteMeasurementPtr_->timeK());
89 // Search for new graph key, if not found, add it
90 DynamicFactorGraphStateKey<gtsam::Pose3> graphKey = gtsamDynamicExpressionKeys.get<gtsam::Pose3>().getTransformationKey<'r'>(
91 newGraphKeyAddedFlag, gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName(),
92 gmsfUnaryAbsoluteMeasurementPtr_->timeK(), T_W_fixedFrame_initial, dynamicVariableType);
93 // Get age of keyframe
94 const double keyframeAge = graphKey.computeKeyframeAge(gmsfUnaryAbsoluteMeasurementPtr_->timeK());
95
96 // B: Take care of the old keyframe -----------------------------------------------------
97 // If deactivated or too old, we should remove the old keyframe and create a new one (with or without random walk)
98 // In any case, we have to add relation between old and new keyframe
99 // Offline graph: we can always add the delta as all states are part of the optimization (random walk or deterministic keyframe
100 // displacement).
101 // Online graph: we can only add the delta if the old keyframe is still active (random walk or deterministic keyframe displacement. If
102 // it is not active, add prior from old keyframe to new keyframe (including the displacement).
103 bool introducedNewKeyframeDisplacement = false;
104 // Old GTSAM key --> needed for adding constraints between previous and new keyframe
105 const gtsam::Key oldGtsamKey = graphKey.key();
106 const Eigen::Vector3d oldKeyframePosition = graphKey.getReferenceFrameKeyframePosition();
107 // Latest belief of the old keyframe, which a new displacement keyframe starts from
108 const gtsam::Pose3 T_W_fixedFrameOld_belief = graphKey.getNumberStepsOptimized() > 0
111 bool oldVariableWasActive = graphKey.isVariableActive();
112 // If needed, have a copy of the old key to add back a prior to the online graph
113 std::unique_ptr<DynamicFactorGraphStateKey<gtsam::Pose3>> oldKeyPtr = nullptr;
114 if (!oldVariableWasActive) {
115 REGULAR_COUT << " Old keyframe is not active anymore. Hence keeping the belief and uncertainty of previous key." << std::endl;
116 oldKeyPtr = std::make_unique<DynamicFactorGraphStateKey<gtsam::Pose3>>(graphKey);
117 }
118
119 // C: Check if we need to create a new keyframe -----------------------------------------------------
120 // Case 1: Old keyframe not active --> just reactivate the keyframe (independent of whether new keyframe was added or not)
121 if (!oldVariableWasActive) {
122 // Check whether no new keyframe was added
123 if (newGraphKeyAddedFlag) {
124 throw std::logic_error("GmsfUnaryExpressionAbsolut: Old keyframe was not active, but new keyframe was added.");
125 }
126 // Reactivate the keyframe (have to use reference instead of copy "graphKey" above)
127 gtsamDynamicExpressionKeys.get<gtsam::Pose3>()
128 .lv_T_frame1_frame2(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName())
129 .activateVariable();
130 // Set number of steps optimized to zero
131 gtsamDynamicExpressionKeys.get<gtsam::Pose3>()
132 .lv_T_frame1_frame2(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName())
133 .resetNumberStepsOptimized();
134 // Print out
135 REGULAR_COUT << GREEN_START << " Reactivated old keyframe " << gtsam::Symbol(graphKey.key())
136 << " as it was deactivated, but is needed again. Also set number of optimization steps to 0." << COLOR_END << std::endl;
137
138 // If the old keyframe was not active, then it is not part of the optimization anymore (or has never been) --> prior to online graph
139 // Make sure that old key ptr is not null
140 if (oldKeyPtr == nullptr) {
141 throw std::logic_error("GmsfUnaryExpressionAbsolut: Old keyframe was not active, but old key ptr is null.");
142 }
143 // Container for belief of old keyframe
144 gtsam::Pose3 T_W_fixedFrameOld;
145 // Noise model container
146 gtsam::SharedNoiseModel noiseModelPtr;
147 // Case 1: Has been optimized before
148 if (oldKeyPtr->getNumberStepsOptimized() > 0) {
149 T_W_fixedFrameOld = oldKeyPtr->getTransformationAfterOptimization();
150 Eigen::Matrix<double, 6, 6> oldKeyframeCovariance = oldKeyPtr->getCovarianceAfterOptimization();
151 // Noise model from 6x6 covariance
152 noiseModelPtr = gtsam::noiseModel::Gaussian::Covariance(oldKeyframeCovariance);
153 // Print
154 REGULAR_COUT << " Old keyframe has been optimized before. Adding previous optimization outcome again to online graph." << std::endl;
155 }
156 // Case 2: Has not been optimized before
157 else {
158 T_W_fixedFrameOld = oldKeyPtr->getApproximateTransformationBeforeOptimization();
159 Eigen::Matrix<double, 6, 1> oldKeyframeCovariance = gmsfUnaryAbsoluteMeasurementPtr_->initialSe3AlignmentNoise();
160 // Noise model for prior
161 noiseModelPtr = gtsam::noiseModel::Diagonal::Sigmas(gmsfUnaryAbsoluteMeasurementPtr_->initialSe3AlignmentNoise());
162 // Print
163 REGULAR_COUT << " Old keyframe has not been optimized before. Adding very initial guess that should have been added last time."
164 << std::endl;
165 }
166 // Add information to online graph only (as has been marginalized out), offline still has it
167 // Prior belief
168 this->newOnlinePosePriorFactors_.emplace_back(oldGtsamKey, T_W_fixedFrameOld, noiseModelPtr);
169 // State value
170 this->newOnlineStateValues_.insert(oldGtsamKey, T_W_fixedFrameOld);
171 // Print
172 REGULAR_COUT << GREEN_START << " Added prior to old keyframe " << gtsam::Symbol(oldGtsamKey)
173 << " to the online graph, as it was not active anymore." << COLOR_END << std::endl;
174 }
175
176 // Case 2: Keyframe is too old --> create a new keyframe to model the displacement
177 // Transform from the old to the new keyframe, used for both the initial guess and the constraint between them
178 gtsam::Pose3 T_fixedFrameOld_fixedFrame;
179 // Only a measurement with a position can place the new keyframe
181 // Remove the old keyframe from memory
182 gtsamDynamicExpressionKeys.get<gtsam::Pose3>().removeTransform(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(),
183 gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName(), graphKey);
184 // Create a new keyframe
185 graphKey = gtsamDynamicExpressionKeys.get<gtsam::Pose3>().getTransformationKey<'r'>(
186 newGraphKeyAddedFlag, gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName(),
187 gmsfUnaryAbsoluteMeasurementPtr_->timeK(), T_W_fixedFrame_initial, dynamicVariableType);
188 // Assert that new keyframe was added and that it is active
189 assert(newGraphKeyAddedFlag && graphKey.isVariableActive());
190 // Set flag that we introduced a new keyframe
191 introducedNewKeyframeDisplacement = true;
192 T_fixedFrameOld_fixedFrame =
193 gtsam::Pose3(gtsam::Rot3::Identity(), gtsam::Point3(graphKey.getReferenceFrameKeyframePosition() - oldKeyframePosition));
194 }
195
196 // D: Shift the measurement to the robot position and recompute initial guess if we create keyframes -----------------------------------
197 // Has to be done here, as we did not know the keyframe position before
198 if (centerMeasurementsAtKeyframePositionBeforeAlignmentFlag && this->measuresPosition() && !initialGuessSetExternally) {
199 // Shift the measurement to the robot position
201 // Recompute initial guess
202 T_W_fixedFrame_initial = this->computeT_W_fixedFrame_initial(W_currentPropagatedState);
203 // Update the initial guess
204 gtsamDynamicExpressionKeys.get<gtsam::Pose3>()
205 .lv_T_frame1_frame2(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName())
206 .setApproximateTransformationBeforeOptimization(T_W_fixedFrame_initial);
207 }
208
209 // A displacement keyframe starts from the old keyframe, as the constraint between them states. The guess from a single measurement
210 // can be far off, e.g. a position measurement carries no orientation.
211 if (introducedNewKeyframeDisplacement && !initialGuessSetExternally) {
212 T_W_fixedFrame_initial = T_W_fixedFrameOld_belief * T_fixedFrameOld_fixedFrame;
213 gtsamDynamicExpressionKeys.get<gtsam::Pose3>()
214 .lv_T_frame1_frame2(gmsfUnaryAbsoluteMeasurementPtr_->worldFrameName(), gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName())
215 .setApproximateTransformationBeforeOptimization(T_W_fixedFrame_initial);
216 }
217
218 // E: Define expression for T_fixedFrame_W -----------------------------------------------------
219 gtsam::Pose3_ exp_T_W_fixedFrame(graphKey.key()); // T_fixedFrame_W
220 // Call main child function
222
223 // F: Initial values and priors if needed -----------------------------------------------------
224 if (newGraphKeyAddedFlag) {
225 REGULAR_COUT << " Initial Guess for T_W_" << gmsfUnaryAbsoluteMeasurementPtr_->fixedFrameName()
226 << ", RPY (deg): " << T_W_fixedFrame_initial.rotation().rpy().transpose() * (180.0 / M_PI)
227 << ", t (x, y, z): " << T_W_fixedFrame_initial.translation().transpose() << std::endl;
228 // Insert Values
229 this->newOnlineStateValues_.insert(graphKey.key(), T_W_fixedFrame_initial);
230 this->newOfflineStateValues_.insert(graphKey.key(), T_W_fixedFrame_initial);
231
232 // Case 1: Entirely new keyframe has been added (not just a displacement) --> add prior to online graph
233 if (!introducedNewKeyframeDisplacement) {
234 // Insert Prior ONLY for online graph (offline is observable regardless)
235 this->newOnlinePosePriorFactors_.emplace_back(
236 graphKey.key(), T_W_fixedFrame_initial,
237 gtsam::noiseModel::Diagonal::Sigmas(gmsfUnaryAbsoluteMeasurementPtr_->initialSe3AlignmentNoise()));
238 REGULAR_COUT << GREEN_START << " Adding prior to new keyframe " << gtsam::Symbol(graphKey.key()) << " with noise "
239 << gmsfUnaryAbsoluteMeasurementPtr_->initialSe3AlignmentNoise().transpose()
240 << " to the online graph, as it is an entirely new keyframe." << COLOR_END << std::endl;
241 }
242 // Case 2: New keyframe has been added, but one existed before already --> add relative constraint from old to new keyframe
243 else {
244 // Add relative constraint from old keyframe to new keyframe
245 // Define noise model --> either random walk or deterministic displacement
246
247 // a): Random Walk between the two
248 if (gmsfUnaryAbsoluteMeasurementPtr_->modelAsRandomWalkFlag()) {
249 gtsam::SharedDiagonal noiseModelPtr =
250 gtsam::noiseModel::Diagonal::Sigmas(gmsfUnaryAbsoluteMeasurementPtr_->se3AlignmentRandomWalk());
251 this->newOnlineAndOfflinePoseBetweenFactors_.emplace_back(oldGtsamKey, graphKey.key(), T_fixedFrameOld_fixedFrame, noiseModelPtr);
252
253 // Add prior from old keyframe to new keyframe
254 REGULAR_COUT << GREEN_START << " Adding relative RANDOM WALK constraint from key " << gtsam::Symbol(oldGtsamKey) << " to key "
255 << gtsam::Symbol(graphKey.key()) << ": " << T_fixedFrameOld_fixedFrame << COLOR_END << std::endl;
256
257 }
258 // b): Deterministic displacement modelled as equality constraint
259 else {
260 gtsam::SharedConstrained noiseModelPtr =
261 gtsam::noiseModel::Constrained::MixedSigmas(gmsfUnaryAbsoluteMeasurementPtr_->se3AlignmentRandomWalk());
262 this->newOnlineAndOfflinePoseBetweenFactors_.emplace_back(oldGtsamKey, graphKey.key(), T_fixedFrameOld_fixedFrame, noiseModelPtr);
263
264 // Add prior from old keyframe to new keyframe
265 REGULAR_COUT << GREEN_START << " Adding relative DETERMINISTIC constraint from key " << gtsam::Symbol(oldGtsamKey) << " to key "
266 << gtsam::Symbol(graphKey.key()) << ": " << T_fixedFrameOld_fixedFrame << COLOR_END << std::endl;
267 }
268 }
269 }
270 }
271
272 // ii).B Adding Landmark State in Dynamic Memory
274 const gtsam::NavState& W_currentPropagatedState) final {
275 // Raise logic error, as it is not a landmark measurement
276 throw std::logic_error("GmsfUnaryExpressionAbsolut: convertRobotAndLandmarkStatesToMeasurement() is not implemented.");
277 }
278
279 // Sub Functions that have to be implemented in derived classes ----------------
280 virtual gtsam::Pose3 computeT_W_fixedFrame_initial(const gtsam::NavState& W_currentPropagatedState) = 0;
281 // A measurement without a position reuses the current keyframe of its fixed frame and is not centered
282 virtual bool measuresPosition() const = 0;
283
284 virtual const Eigen::Vector3d getMeasurementPosition() = 0;
285
286 virtual void setMeasurementPosition(const Eigen::Vector3d& position) = 0;
287
288 virtual void transformStateToReferenceFrameMeasurement(const gtsam::Pose3_& exp_T_W_fixedFrame) = 0;
289
290 private:
291 // Pointer to the GMSF Unary Absolute Measurement
292 std::shared_ptr<UnaryMeasurementAbsolute> gmsfUnaryAbsoluteMeasurementPtr_;
293 // Create Reference Alignment Keyframe Every N Seconds
295};
296
297} // namespace holistic_fusion
298
299#endif // GMSF_UNARY_EXPRESSION_ABSOLUT_H
#define REGULAR_COUT
#define COLOR_END
#define GREEN_START
GTSAM_TRANSFORM_TYPE getApproximateTransformationBeforeOptimization() const
double computeKeyframeAge(const double currentTime) const
const GTSAM_TRANSFORM_TYPE & getTransformationAfterOptimization() const
void transformImuStateFromWorldToReferenceFrame(DynamicDictionaryContainer &gtsamDynamicExpressionKeys, const gtsam::NavState &W_currentPropagatedState, const bool centerMeasurementsAtKeyframePositionBeforeAlignmentFlag) final
std::shared_ptr< UnaryMeasurementAbsolute > gmsfUnaryAbsoluteMeasurementPtr_
void transformLandmarkInWorldToImuFrame(DynamicDictionaryContainer &gtsamDynamicExpressionKeys, const gtsam::NavState &W_currentPropagatedState) final
virtual const Eigen::Vector3d getMeasurementPosition()=0
virtual void transformStateToReferenceFrameMeasurement(const gtsam::Pose3_ &exp_T_W_fixedFrame)=0
GmsfUnaryExpressionAbsolut(const std::shared_ptr< UnaryMeasurementAbsolute > &gmsfUnaryAbsoluteMeasurementPtr, const std::string &imuFrameName, const Eigen::Isometry3d &T_I_sensorFrame, const double createReferenceAlignmentKeyframeEveryNSeconds)
virtual void setMeasurementPosition(const Eigen::Vector3d &position)=0
virtual gtsam::Pose3 computeT_W_fixedFrame_initial(const gtsam::NavState &W_currentPropagatedState)=0
GmsfUnaryExpression(const std::shared_ptr< UnaryMeasurement > &gmsfBaseUnaryMeasurementPtr, const std::string &imuFrameName, const Eigen::Isometry3d &T_I_sensorFrame)
static DynamicVariableType RefFrame(const Eigen::Vector3d &referenceFrameKeyframePosition, const double variableCreationTime)