132 const gtsam::Vector3& imuAngularVelocity) {
135 auto priorPoseNoise = gtsam::noiseModel::Diagonal::Sigmas(
140 auto priorVelocityNoise = gtsam::noiseModel::Isotropic::Sigma(3,
graphConfigPtr_->initialVelocityStdDev_);
141 auto priorBiasNoise = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) <<
graphConfigPtr_->initialAccBiasStdDev_,
150 gtsam::NonlinearFactorGraph newRtGraphFactors, newBatchGraphFactors;
151 gtsam::Values newRtGraphValues, newBatchGraphValues;
152 std::shared_ptr<std::map<gtsam::Key, double>> priorKeyTimestampMapPtr = std::make_shared<std::map<gtsam::Key, double>>();
159 gtsam::Values valuesEstimate;
160 REGULAR_COUT <<
" Initial Pose of imu in world frame: " << T_W_I0 << std::endl;
162 REGULAR_COUT <<
" Initial velocity assumed to be: " << gtsam::Vector3(0, 0, 0) << std::endl;
163 valuesEstimate.insert(gtsam::symbol_shorthand::V(
propagatedStateKey_), gtsam::Vector3(0, 0, 0));
182 gtsam::Vector3(0, 0, 0),
185 gtsam::Vector3(0, 0, 0),
224 T_W_O_ = (T_W_I0.inverse() * T_O_I0).matrix();
230 std::shared_ptr<SafeNavStateWithCovarianceAndBias>& newOptimizedNavStatePtr,
231 const std::shared_ptr<ImuBuffer>& imuBufferPtr,
const double imuTimeK,
bool createNewStateFlag) {
243 imuBufferPtr->getLastTwoMeasurements(imuMeas);
254 imuBufferPtr->integrateNavStateFromTimestamp(imuMeas.begin()->first, imuMeas.rbegin()->first,
W_imuPropagatedState_,
258 imuBufferPtr->integrateNavStateFromTimestamp(imuMeas.begin()->first, imuMeas.rbegin()->first,
W_imuPropagatedState_,
265 const gtsam::Pose3 T_Ikm1_Ik = T_W_I_beforeInt.between(T_W_I_afterInt);
269 const Eigen::Matrix3d& R_W_I = T_W_I_afterInt.rotation().matrix();
270 Eigen::Matrix3d R_O_I = T_O_I.rotation().matrix();
271 R_O_I.row(2) = R_W_I.row(2);
272 R_O_I.row(1) = R_O_I.row(2).cross(R_O_I.row(0)).normalized();
273 R_O_I.row(0) = R_O_I.row(1).cross(R_O_I.row(2)).normalized();
274 T_O_I = gtsam::Pose3(gtsam::Rot3(R_O_I), T_O_I.translation());
282 newOptimizedNavStatePtr = std::make_shared<SafeNavStateWithCovarianceAndBias>(
optimizedGraphState_);
284 newOptimizedNavStatePtr =
nullptr;
293 returnPreIntegratedNavState.
update(
T_W_O_, Eigen::Isometry3d(T_O_Ik_nav.pose().matrix()), T_O_Ik_nav.bodyVelocity(),
294 optimizedGraphState_.imuBias().correctGyroscope(imuMeas.rbegin()->second.angularVelocity), imuTimeK,
300 createNewStateFlag =
false;
302 <<
" The current measurement would fall outside of the real-time smoother lag, hence skipping the creation of a new"
303 <<
"IMU measurement. Not creating new state." <<
COLOR_END << std::endl;
307 if (createNewStateFlag) {
318 gtsam::CombinedImuFactor imuFactor(gtsam::symbol_shorthand::X(oldKey), gtsam::symbol_shorthand::V(oldKey),
319 gtsam::symbol_shorthand::X(newKey), gtsam::symbol_shorthand::V(newKey),
324 gtsam::Values valuesEstimate;
419 const double timeKm1,
const double timeK,
const double rate,
420 const RobustNormEnum& robustNormEnum,
const double robustNormConstant) {
422 const double maxLidarTimestampDistance = (1.0 / rate) + (2.0 *
graphConfigPtr_->maxSearchDeviation_);
423 gtsam::Key closestKeyKm1, closestKeyK;
424 double keyTimeStampDistance{0.0};
426 if (!
findGraphKeys_(closestKeyKm1, closestKeyK, keyTimeStampDistance, maxLidarTimestampDistance, timeKm1, timeK,
"pose between")) {
428 << closestKeyKm1 <<
" and " << closestKeyK <<
COLOR_END << std::endl;
433 const double scale = keyTimeStampDistance / (timeK - timeKm1);
435 REGULAR_COUT <<
" Scaling factor in tangent space for pose between delta pose: " << scale << std::endl;
437 gtsam::Pose3 scaledDeltaPose = gtsam::Pose3::Expmap(scale * gtsam::Pose3::Logmap(deltaPose));
440 assert(poseBetweenNoiseDensity.size() == 6);
441 auto noise = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(poseBetweenNoiseDensity)));
442 gtsam::noiseModel::Robust::shared_ptr robustErrorFunction;
444 switch (robustNormEnum) {
446 robustErrorFunction = gtsam::noiseModel::Robust::Create(gtsam::noiseModel::mEstimator::Huber::Create(robustNormConstant), noise);
449 robustErrorFunction = gtsam::noiseModel::Robust::Create(gtsam::noiseModel::mEstimator::Cauchy::Create(robustNormConstant), noise);
452 robustErrorFunction = gtsam::noiseModel::Robust::Create(gtsam::noiseModel::mEstimator::Tukey::Create(robustNormConstant), noise);
455 robustErrorFunction =
456 gtsam::noiseModel::Robust::Create(gtsam::noiseModel::mEstimator::GemanMcClure::Create(robustNormConstant), noise);
461 gtsam::BetweenFactor<gtsam::Pose3> poseBetweenFactor;
463 poseBetweenFactor = gtsam::BetweenFactor<gtsam::Pose3>(gtsam::symbol_shorthand::X(closestKeyKm1),
464 gtsam::symbol_shorthand::X(closestKeyK), scaledDeltaPose, noise);
466 poseBetweenFactor = gtsam::BetweenFactor<gtsam::Pose3>(gtsam::symbol_shorthand::X(closestKeyKm1),
467 gtsam::symbol_shorthand::X(closestKeyK), scaledDeltaPose, robustErrorFunction);
476 << closestKeyKm1 <<
" and key " << closestKeyK <<
COLOR_END << std::endl;
542 gtsam::NonlinearFactorGraph newRtGraphFactors, newBatchGraphFactors;
543 gtsam::Values newRtGraphValues, newBatchGraphValues;
544 std::map<gtsam::Key, double> newRtGraphKeysTimestampsMap, newBatchGraphKeysTimestampsMap;
545 gtsam::Key currentPropagatedKey;
546 gtsam::Vector3 currentGraphStateKeyAngularVelocity;
547 double currentGraphStateKeyTime;
583 bool successfulOptimizationFlag =
true;
585 if (newRtGraphFactors.size() > 0) {
589 REGULAR_COUT <<
" No factors present in rt smoother, not optimizing." << std::endl;
592 if (
graphConfigPtr_->useAdditionalSlowBatchSmootherFlag_ && newBatchGraphFactors.size() > 0) {
593 successfulOptimizationFlag =
596 REGULAR_COUT <<
" No factors present in batch smoother, not optimizing." << std::endl;
608 if (!successfulOptimizationFlag) {
615 gtsam::NavState resultNavState =
619 gtsam::imuBias::ConstantBias resultBias =
rtOptimizerPtr_->calculateEstimatedBias(gtsam::symbol_shorthand::B(currentPropagatedKey));
624 gtsam::Matrix66 resultPoseCovarianceWorldFrame =
627 gtsam::Matrix33 resultVelocityCovariance =
628 rtOptimizerPtr_->calculateMarginalCovarianceMatrixAtKey(gtsam::symbol_shorthand::V(currentPropagatedKey));
645 const std::pair<std::string, std::string>& framePair = framePairKeyMapIterator.first;
648 const gtsam::Key& gtsamKey = framePairKeyMapIterator.second.key();
651 bool isLandmarkState =
false;
653 isLandmarkState =
true;
657 const char stateCategory = gtsam::Symbol(gtsamKey).chr();
660 if (framePairKeyMapIterator.second.isVariableActive()) {
663 gtsam::Pose3 T_frame1_frame2;
664 gtsam::Matrix66 T_frame1_frame2_covariance = gtsam::Z_6x6;
669 T_frame1_frame2_covariance =
rtOptimizerPtr_->calculateMarginalCovarianceMatrixAtKey(gtsamKey);
671 framePairKeyMapIterator.second.updateLatestEstimate(T_frame1_frame2, T_frame1_frame2_covariance);
674 gtsam::Values valuesEstimate;
675 valuesEstimate.insert(gtsamKey, T_frame1_frame2);
681 gtsam::Point3 point3Estimate =
rtOptimizerPtr_->calculateEstimatedPoint3(gtsamKey);
682 T_frame1_frame2 = gtsam::Pose3(gtsam::Rot3::Identity(), point3Estimate);
683 T_frame1_frame2_covariance.block<3, 3>(3, 3) =
rtOptimizerPtr_->calculateMarginalCovarianceMatrixAtKey(gtsamKey);
685 framePairKeyMapIterator.second.updateLatestEstimate(T_frame1_frame2, T_frame1_frame2_covariance);
688 gtsam::Values valuesEstimate;
689 valuesEstimate.insert(gtsamKey, point3Estimate);
695 throw std::runtime_error(
"Key is neither a pose nor a displacement key.");
700 gtsam::Pose3 T_frame2_frame1_corrected = T_frame1_frame2.inverse();
701 T_frame2_frame1_corrected =
702 gtsam::Pose3(T_frame2_frame1_corrected.rotation(),
703 T_frame2_frame1_corrected.translation() + framePairKeyMapIterator.second.getReferenceFrameKeyframePosition());
704 gtsam::Pose3 T_frame1_frame2_corrected = T_frame2_frame1_corrected.inverse();
707 if (isLandmarkState) {
709 resultLandmarkTransformations.
set_T_frame1_frame2(framePairKeyMapIterator.first.first, framePairKeyMapIterator.first.second,
710 Eigen::Isometry3d(T_frame1_frame2_corrected.matrix()));
711 resultLandmarkTransformationsCovariance.
set_T_frame1_frame2(framePairKeyMapIterator.first.first,
712 framePairKeyMapIterator.first.second, T_frame1_frame2_covariance);
717 resultReferenceFrameTransformations.
set_T_frame1_frame2(framePairKeyMapIterator.first.first,
718 framePairKeyMapIterator.first.second,
719 Eigen::Isometry3d(T_frame1_frame2_corrected.matrix()));
721 framePairKeyMapIterator.first.first, framePairKeyMapIterator.first.second, T_frame1_frame2_covariance);
726 REGULAR_COUT <<
" Added frame pair: " << framePair.first <<
" to " << framePair.second <<
" to real-time logging."
733 if (framePairKeyMapIterator.second.getNumberStepsOptimized() == 0) {
735 REGULAR_COUT <<
" Fixed-frame Transformation between " << framePairKeyMapIterator.first.first <<
" and "
736 << framePairKeyMapIterator.first.second <<
" optimized for the first time." <<
COLOR_END << std::endl;
739 REGULAR_COUT <<
GREEN_START <<
" Result, RPY (deg): " << T_frame1_frame2.rotation().rpy().transpose() * (180.0 / M_PI)
740 <<
", t (x, y, z): " << T_frame1_frame2.translation().transpose() <<
COLOR_END << std::endl;
744 framePairKeyMapIterator.second.incrementNumberStepsOptimized();
747 if (framePairKeyMapIterator.first.second ==
worldFrame_ &&
749 const gtsam::Pose3& T_frame1_frame2_initial =
750 framePairKeyMapIterator.second.getApproximateTransformationBeforeOptimization();
751 const double errorTangentSpace = gtsam::Pose3::Logmap(T_frame1_frame2_initial.between(T_frame1_frame2)).norm();
754 if (errorTangentSpace >
graphConfigPtr_->referenceFramePosesResetThreshold_) {
757 << framePairKeyMapIterator.first.first <<
" and " << framePairKeyMapIterator.first.second
758 <<
" diverged too much. Removing from optimization and adding again freshly at next possibility." <<
COLOR_END
762 framePairKeyMapIterator.first.second);
768 catch (
const std::out_of_range& exception) {
770 if (framePairKeyMapIterator.second.getNumberStepsOptimized() > 0) {
771 REGULAR_COUT <<
RED_START <<
" OutOfRange-Exception while querying the active transformation and/or covariance at key "
772 << gtsam::Symbol(gtsamKey) <<
", for frame pair " << framePairKeyMapIterator.first.first <<
","
773 << framePairKeyMapIterator.first.second << std::endl
774 <<
" This happens if the requested variable is outside of the smoother window (e.g. because corresponding "
775 "measurement stopped). Hence, we keep this estimate unchanged and remove/deactivate it from the state "
780 framePairKeyMapIterator.first.second);
786 double variableAge = framePairKeyMapIterator.second.computeVariableAge(currentGraphStateKeyTime);
789 << framePairKeyMapIterator.first.first <<
" and " << framePairKeyMapIterator.first.second <<
" is too old ("
791 <<
")s and was never optimized. Removing from optimization and adding again freshly at next "
796 framePairKeyMapIterator.first.second);
801 <<
YELLOW_START <<
" Tried to query the transformation and/or covariance for frame pair "
802 << framePairKeyMapIterator.first.first <<
" to " << framePairKeyMapIterator.first.second
803 <<
", at key: " << gtsam::Symbol(gtsamKey)
804 <<
". Not yet available, as it was not yet optimized. Waiting for next optimization iteration until publishing it. "
805 "Current state key: "
806 << currentPropagatedKey <<
COLOR_END << std::endl;
822 optimizedGraphState_.updateNavStateAndBias(currentPropagatedKey, currentGraphStateKeyTime, resultNavState,
823 resultBias.correctGyroscope(currentGraphStateKeyAngularVelocity), resultBias);
825 optimizedGraphState_.updateReferenceFrameTransformsCovariance(resultReferenceFrameTransformationsCovariance);
827 optimizedGraphState_.updateLandmarkTransformsCovariance(resultLandmarkTransformationsCovariance);
828 optimizedGraphState_.updateCovariances(resultPoseCovarianceWorldFrame, resultVelocityCovariance);
934 std::map<std::string, std::ofstream> fileStreams;
937 if (!std::filesystem::exists(savePath + timeString)) {
938 std::filesystem::create_directories(savePath + timeString);
942 std::string stateCategoryString =
"rt_R_6D_transform";
947 std::string frameInformation =
"_" + framePair.first +
"_to_" + framePair.second;
949 std::replace(frameInformation.begin(), frameInformation.end(),
'/',
'_');
959 const double& timeStamp = mapPair.first;
962 for (
const auto& framePairTransform : referenceFrameTransforms.
getTransformsMap()) {
964 const std::pair<std::string, std::string>& framePair = framePairTransform.first;
966 const Eigen::Isometry3d& transform = framePairTransform.second;
968 std::string frameInformation =
"_" + framePair.first +
"_to_" + framePair.second;
970 std::replace(frameInformation.begin(), frameInformation.end(),
'/',
'_');
1030 const std::string& savePath,
const bool saveCovarianceFlag) {
1038 std::map<std::string, std::ofstream> fileStreams;
1041 std::time_t now_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
1042 std::tm now_tm = *std::localtime(&now_time_t);
1044 std::ostringstream oss;
1045 oss << std::put_time(&now_tm,
"%Y_%m_%d_%H_%M_%S");
1047 std::string timeString = oss.str();
1049 timeString.erase(std::remove(timeString.begin(), timeString.end(),
'\n'), timeString.end());
1051 std::replace(timeString.begin(), timeString.end(),
' ',
'_');
1054 if (!std::filesystem::exists(savePath + timeString)) {
1055 std::filesystem::create_directories(savePath + timeString);
1073 for (
const auto& keyPosePair : optimizedValues.extract<gtsam::Pose3>()) {
1075 const gtsam::Key& graphKey = keyPosePair.first;
1076 gtsam::Pose3 pose = keyPosePair.second;
1077 const gtsam::Symbol stateSymbol(graphKey);
1078 const char stateCategory = stateSymbol.chr();
1079 const double timeStamp = keyTimestampMap.at(graphKey);
1082 std::string stateCategoryString =
"";
1083 std::string frameInformation =
"";
1086 const std::string stateCategoryCapital(1,
static_cast<char>(std::toupper(stateCategory)));
1090 if (stateCategory ==
'x') {
1091 stateCategoryString = stateCategoryCapital +
"_state_6D_pose";
1096 stateCategoryString = stateCategoryCapital +
"_6D_transform";
1098 std::pair<std::string, std::string> framePair;
1100 frameInformation =
"_" + framePair.first +
"_to_" + framePair.second;
1103 throw std::runtime_error(
"Could not find frame pair for key.");
1108 throw std::logic_error(stateCategoryString +
" is an unknown 6D state category. Check the admissible GTSAM symbols in the config.");
1111 std::string transformIdentifier = stateCategoryString + frameInformation;
1113 std::replace(transformIdentifier.begin(), transformIdentifier.end(),
'/',
'_');
1116 Eigen::Vector3d keyframePosition = Eigen::Vector3d::Zero();
1122 fileLogger_.createPose3CsvFileStream(fileStreams, savePath, transformIdentifier, timeString, saveCovarianceFlag);
1124 fileLogger_.createPose3TumFileStream(fileStreams, savePath, transformIdentifier +
"_tum", timeString);
1127 Eigen::Matrix<double, 6, 6> poseCovarianceInWorldRos = Eigen::Matrix<double, 6, 6>::Zero();
1128 if (saveCovarianceFlag) {
1135 if (keyframePosition.norm() > 1e-6) {
1136 const gtsam::Pose3& T_W_M = pose;
1137 gtsam::Pose3 T_M_W_corrected = T_W_M.inverse();
1138 T_M_W_corrected = gtsam::Pose3(T_M_W_corrected.rotation(),
1139 T_M_W_corrected.translation() + keyframePosition);
1140 gtsam::Pose3 T_W_M_corrected = T_M_W_corrected.inverse();
1141 pose = T_W_M_corrected;
1142 REGULAR_COUT <<
" Corrected frame position for " << transformIdentifier <<
" (" << gtsam::Symbol(graphKey) <<
") by "
1143 << keyframePosition.transpose() << std::endl;
1148 fileLogger_.writePose3ToCsvFile(fileStreams, pose, transformIdentifier, timeStamp, saveCovarianceFlag, poseCovarianceInWorldRos);
1150 fileLogger_.writePose3ToTumFile(fileStreams, pose, transformIdentifier +
"_tum", timeStamp);
1154 for (
const auto& keyVectorPair : optimizedValues.extract<gtsam::Point3>()) {
1156 const gtsam::Key& key = keyVectorPair.first;
1157 const gtsam::Vector& vector = keyVectorPair.second;
1158 const gtsam::Symbol symbol(key);
1159 const char stateCategory = symbol.chr();
1160 const double timeStamp = keyTimestampMap.at(key);
1163 std::string stateCategoryString =
"";
1164 std::string frameInformation =
"";
1167 const std::string stateCategoryNonCapital(1, stateCategory);
1171 if (stateCategory ==
'v') {
1172 stateCategoryString = stateCategoryNonCapital +
"_state_3D_velocity";
1177 stateCategoryString = stateCategoryNonCapital +
"_3D_vector";
1179 if (stateCategory ==
'l') {
1184 std::pair<std::string, std::string> framePair;
1186 frameInformation =
"_" + framePair.first +
"_to_" + framePair.second;
1193 throw std::logic_error(stateCategoryString +
" is an unknown 3D state category. Check the admissible GTSAM symbols in the config.");
1196 std::string transformIdentifier = stateCategoryString + frameInformation;
1200 fileLogger_.createPoint3CsvFileStream(fileStreams, savePath, transformIdentifier, timeString);
1203 fileLogger_.writePoint3ToCsvFile(fileStreams, vector, transformIdentifier, timeStamp);
1207 for (
const auto& keyBiasPair : optimizedValues.extract<gtsam::imuBias::ConstantBias>()) {
1209 const gtsam::Key& key = keyBiasPair.first;
1210 const gtsam::imuBias::ConstantBias& bias = keyBiasPair.second;
1211 const gtsam::Symbol symbol(key);
1212 const char stateCategory = symbol.chr();
1213 const double timeStamp = keyTimestampMap.at(key);
1216 assert(stateCategory ==
'b');
1219 const std::string stateCategoryCapital(1,
static_cast<char>(std::toupper(stateCategory)));
1223 std::string stateCategoryIdentifier = stateCategoryCapital +
"_imu_bias";
1227 fileLogger_.createImuBiasCsvFileStream(fileStreams, savePath, stateCategoryIdentifier, timeString);
1230 fileLogger_.writeImuBiasToCsvFile(fileStreams, bias, stateCategoryIdentifier, timeStamp);
1234 for (
auto& pair : fileStreams) {
1235 pair.second.close();