My Project 1.0
Holistic Fusion: A Generic Framework for Multimodal Sensor Fusion
Loading...
Searching...
No Matches
HolisticFusionRos.cpp
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// Implementation
10
11// ROS
12#include <tf/tf.h>
13
14// Workspace
18
19namespace holistic_fusion {
20
21HolisticFusionRos::HolisticFusionRos(const std::shared_ptr<ros::NodeHandle>& privateNodePtr) : privateNode(*privateNodePtr) {
22 REGULAR_COUT << GREEN_START << " HolisticFusionRos-Constructor called." << COLOR_END << std::endl;
23
24 // Configurations ----------------------------
25 // Graph Config
26 graphConfigPtr_ = std::make_shared<GraphConfig>();
27}
28
29void HolisticFusionRos::setup(const std::shared_ptr<StaticTransforms> staticTransformsPtr) {
30 REGULAR_COUT << GREEN_START << " HolisticFusionRos-Setup called." << COLOR_END << std::endl;
31
32 // Check
33 if (staticTransformsPtr_ == nullptr) {
34 std::runtime_error("Static transforms not set. Has to be set.");
35 }
36
37 // Read parameters ----------------------------
39
40 // Call super class Setup ----------------------------
41 HolisticFusion::setup(graphConfigPtr_, staticTransformsPtr);
42
43 // Publishers ----------------------------
45
46 // Subscribers ----------------------------
48
49 // Messages ----------------------------
51
52 // Services ----------------------------
54
55 // Time
56 startTime = std::chrono::high_resolution_clock::now();
57
58 // Wrap up
59 REGULAR_COUT << GREEN_START << " Set up successfully." << COLOR_END << std::endl;
60}
61
63 REGULAR_COUT << GREEN_START << " Initializing publishers." << COLOR_END << std::endl;
64 // Odometry
65 pubEstOdomImu_ = privateNode.advertise<nav_msgs::Odometry>("/holistic_fusion/est_odometry_odom_imu", ROS_QUEUE_SIZE);
66 pubEstWorldImu_ = privateNode.advertise<nav_msgs::Odometry>("/holistic_fusion/est_odometry_world_imu", ROS_QUEUE_SIZE);
67 pubOptWorldImu_ = privateNode.advertise<nav_msgs::Odometry>("/holistic_fusion/opt_odometry_world_imu", ROS_QUEUE_SIZE);
68 // Vector3 Variances
69 pubEstWorldPosVariance_ = privateNode.advertise<geometry_msgs::Vector3Stamped>("/holistic_fusion/est_world_pos_variance", ROS_QUEUE_SIZE);
70 pubEstWorldRotVariance_ = privateNode.advertise<geometry_msgs::Vector3Stamped>("/holistic_fusion/est_world_rot_variance", ROS_QUEUE_SIZE);
71 // Velocity Marker
72 pubVelocityMarker_ = privateNode.advertise<visualization_msgs::Marker>("/holistic_fusion/velocity_marker", ROS_QUEUE_SIZE);
73 // Paths
74 pubEstOdomImuPath_ = privateNode.advertise<nav_msgs::Path>("/holistic_fusion/est_path_odom_imu", ROS_QUEUE_SIZE);
75 pubEstWorldImuPath_ = privateNode.advertise<nav_msgs::Path>("/holistic_fusion/est_path_world_imu", ROS_QUEUE_SIZE);
76 pubOptWorldImuPath_ = privateNode.advertise<nav_msgs::Path>("/holistic_fusion/opt_path_world_imu", ROS_QUEUE_SIZE);
77 // Imu Bias
78 pubAccelBias_ = privateNode.advertise<geometry_msgs::Vector3Stamped>("/holistic_fusion/accel_bias", ROS_QUEUE_SIZE);
79 pubGyroBias_ = privateNode.advertise<geometry_msgs::Vector3Stamped>("/holistic_fusion/gyro_bias", ROS_QUEUE_SIZE);
80 // Added Imu Measurements
81 pubAddedImuMeas_ = privateNode.advertise<sensor_msgs::Imu>("/holistic_fusion/added_imu_meas", ROS_QUEUE_SIZE);
82 // Landmarks
83 pubLandmarks_ = privateNode.advertise<visualization_msgs::MarkerArray>("/holistic_fusion/landmarks", ROS_QUEUE_SIZE);
84}
85
87 REGULAR_COUT << GREEN_START << " Initializing subscribers." << COLOR_END << std::endl;
88 // Imu
89 subImu_ = privateNode.subscribe<sensor_msgs::Imu>("/imu_topic", ROS_QUEUE_SIZE, &HolisticFusionRos::imuCallback, this,
90 ros::TransportHints().tcpNoDelay());
91 std::cout << YELLOW_START << "HolisticFusionRos" << COLOR_END << " Initialized main IMU subscriber with topic: " << subImu_.getTopic()
92 << std::endl;
93}
94
96 REGULAR_COUT << GREEN_START << " Initializing messages." << COLOR_END << std::endl;
97 // Odometry
98 estOdomImuMsgPtr_ = boost::make_shared<nav_msgs::Odometry>();
99 estWorldImuMsgPtr_ = boost::make_shared<nav_msgs::Odometry>();
100 optWorldImuMsgPtr_ = boost::make_shared<nav_msgs::Odometry>();
101 // Vector3 Variances
102 estWorldPosVarianceMsgPtr_ = boost::make_shared<geometry_msgs::Vector3Stamped>();
103 estWorldRotVarianceMsgPtr_ = boost::make_shared<geometry_msgs::Vector3Stamped>();
104 // Path
105 estOdomImuPathPtr_ = boost::make_shared<nav_msgs::Path>();
106 estWorldImuPathPtr_ = boost::make_shared<nav_msgs::Path>();
107 optWorldImuPathPtr_ = boost::make_shared<nav_msgs::Path>();
108 // Imu Bias
109 accelBiasMsgPtr_ = boost::make_shared<geometry_msgs::Vector3Stamped>();
110 gyroBiasMsgPtr_ = boost::make_shared<geometry_msgs::Vector3Stamped>();
111 // Landmark Markers
112 landmarkMarkersMsgPtr_ = boost::make_shared<visualization_msgs::MarkerArray>();
113}
114
116 // Trigger offline smoother optimization
118 privateNode.advertiseService("/holistic_fusion/trigger_offline_optimization", &HolisticFusionRos::srvOfflineSmootherOptimizeCallback, this);
119 // Real-Time State Logging
121 privateNode.advertiseService("/holistic_fusion/log_real_time_nav_states", &HolisticFusionRos::srvLogRealTimeNavStatesCallback, this);
122}
123
124bool HolisticFusionRos::srvOfflineSmootherOptimizeCallback(holistic_fusion_ros_msgs::OfflineOptimizationTrigger::Request& req,
125 holistic_fusion_ros_msgs::OfflineOptimizationTrigger::Response& res) {
126 // Max Iterations from service call
127 const int maxIterations = req.max_optimization_iterations;
128 const bool saveCovarianceFlag = req.save_covariance;
129
130 // Trigger offline smoother optimization and create response
131 if (HolisticFusion::optimizeSlowBatchSmoother(maxIterations, optimizationResultLoggingPath, saveCovarianceFlag)) {
132 res.success = true;
133 res.message = "Optimization successful.";
134 } else {
135 res.success = false;
136 res.message = "Optimization failed.";
137 }
138 return true;
139}
140
141bool HolisticFusionRos::srvLogRealTimeNavStatesCallback(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res) {
142 // Log real-time states
144 res.success = true;
145 res.message = "Logging real-time states.";
146 } else {
147 res.success = false;
148 res.message = "Could not log real-time states. Did you enable the flag 'logRealTimeStateToMemory'?";
149 }
150 return true;
151}
152
153void HolisticFusionRos::addToPathMsg(const nav_msgs::PathPtr& pathPtr, const std::string& fixedFrameName, const ros::Time& stamp,
154 const Eigen::Vector3d& t, const int maxBufferLength) {
155 geometry_msgs::PoseStamped pose;
156 pose.header.frame_id = fixedFrameName;
157 pose.header.stamp = stamp;
158 pose.pose.position.x = t(0);
159 pose.pose.position.y = t(1);
160 pose.pose.position.z = t(2);
161 pathPtr->header.frame_id = fixedFrameName;
162 pathPtr->header.stamp = stamp;
163 pathPtr->poses.push_back(pose);
164 if (pathPtr->poses.size() > maxBufferLength) {
165 pathPtr->poses.erase(pathPtr->poses.begin());
166 }
167}
168
169void HolisticFusionRos::addToOdometryMsg(const nav_msgs::OdometryPtr& msgPtr, const std::string& fixedFrame, const std::string& movingFrame,
170 const ros::Time& stamp, const Eigen::Isometry3d& T, const Eigen::Vector3d& F_v_W_F,
171 const Eigen::Vector3d& F_w_W_F, const Eigen::Matrix<double, 6, 6>& poseCovariance,
172 const Eigen::Matrix<double, 6, 6>& twistCovariance) {
173 msgPtr->header.frame_id = fixedFrame;
174 msgPtr->child_frame_id = movingFrame;
175 msgPtr->header.stamp = stamp;
176 tf::poseTFToMsg(isometry3ToTf(T), msgPtr->pose.pose);
177 msgPtr->twist.twist.linear.x = F_v_W_F(0);
178 msgPtr->twist.twist.linear.y = F_v_W_F(1);
179 msgPtr->twist.twist.linear.z = F_v_W_F(2);
180 msgPtr->twist.twist.angular.x = F_w_W_F(0);
181 msgPtr->twist.twist.angular.y = F_w_W_F(1);
182 msgPtr->twist.twist.angular.z = F_w_W_F(2);
183
184 for (int i = 0; i < 6; i++) {
185 for (int j = 0; j < 6; j++) {
186 msgPtr->pose.covariance[6 * i + j] = poseCovariance(i, j);
187 msgPtr->twist.covariance[6 * i + j] = twistCovariance(i, j);
188 }
189 }
190}
191
192void HolisticFusionRos::addToPoseWithCovarianceStampedMsg(const geometry_msgs::PoseWithCovarianceStampedPtr& msgPtr, const std::string& frameName,
193 const ros::Time& stamp, const Eigen::Isometry3d& T,
194 const Eigen::Matrix<double, 6, 6>& transformCovariance) {
195 msgPtr->header.frame_id = frameName;
196 msgPtr->header.stamp = stamp;
197 tf::poseTFToMsg(isometry3ToTf(T), msgPtr->pose.pose);
198 for (int i = 0; i < 6; i++) {
199 for (int j = 0; j < 6; j++) {
200 msgPtr->pose.covariance[6 * i + j] = transformCovariance(i, j);
201 }
202 }
203}
204
205void HolisticFusionRos::addToLandmarkMarkerArrayMsg(const visualization_msgs::MarkerArrayPtr& markerArrayPtr,
206 const std::string& referenceFrameName, const ros::Time& stamp, const Eigen::Isometry3d& T_R_L,
207 const std::string& transformName) {
208 // Save last 10 markers to know whether to add or not
209 static std::deque<std::string> lastMarkerNames;
210 static int landmarkIndex = 0;
211 // Check whether marker already exists --> return if already added
212 if (std::find(lastMarkerNames.begin(), lastMarkerNames.end(), transformName) != lastMarkerNames.end()) {
213 return;
214 }
215 // Else add
216 else {
217 lastMarkerNames.push_back(transformName);
218 if (lastMarkerNames.size() > NUM_KEEP_MARKER_NAMES) {
219 lastMarkerNames.pop_front();
220 }
221 }
222
223 // Marker
224 visualization_msgs::Marker landmarkMarker;
225 HolisticFusionRos::createContactMarker(referenceFrameName, stamp, T_R_L.translation(), "landmarks", landmarkIndex++,
226 Eigen::Vector3d(1.0, 0.0, 0.0), landmarkMarker);
227
228 // Add to array
229 markerArrayPtr->markers.push_back(landmarkMarker);
230 // If too many markers, delete the first one
231 if (markerArrayPtr->markers.size() > NUM_MARKERS_IN_ARRAY) {
232 markerArrayPtr->markers.erase(markerArrayPtr->markers.begin());
233 }
234}
235
237 Eigen::Matrix<double, 6, 6>& poseCovarianceRos, Eigen::Matrix<double, 6, 6>& twistCovarianceRos,
238 const std::shared_ptr<holistic_fusion::SafeNavStateWithCovarianceAndBias>& optimizedStateWithCovarianceAndBiasPtr) {
239 // Extract covariances from optimized state
240 if (optimizedStateWithCovarianceAndBiasPtr != nullptr) {
241 poseCovarianceRos =
242 holistic_fusion::convertCovarianceGtsamConventionToRosConvention(optimizedStateWithCovarianceAndBiasPtr->getPoseCovariance());
243 twistCovarianceRos.block<3, 3>(0, 0) = optimizedStateWithCovarianceAndBiasPtr->getVelocityCovariance();
244 } else {
245 poseCovarianceRos.setZero();
246 twistCovarianceRos.setZero();
247 }
248}
249
250// Markers
251void HolisticFusionRos::createVelocityMarker(const std::string& referenceFrameName, const ros::Time& stamp, const Eigen::Vector3d& velocity,
252 const Eigen::Vector3d& colorRgb, visualization_msgs::Marker& marker) {
253 // Arrow
254 marker.header.frame_id = referenceFrameName;
255 marker.header.stamp = stamp;
256 marker.id = 0;
257 marker.type = visualization_msgs::Marker::ARROW;
258 marker.action = visualization_msgs::Marker::ADD;
259 // Scale and Color
260 const double scale = velocity.norm();
261 marker.scale.x = 0.1; // shaft diameter
262 marker.scale.y = 0.2; // head diameter
263 marker.scale.z = 0.2; // head length
264 marker.color.a = 1.0;
265 marker.color.r = colorRgb(0);
266 marker.color.g = colorRgb(1);
267 marker.color.b = colorRgb(2);
268 // Define Arrow through start and end point
269 geometry_msgs::Point startPoint, endPoint;
270 startPoint = geometry_msgs::Point();
271 startPoint.x = 0.0; // origin
272 startPoint.y = 0.0; // origin
273 startPoint.z = 0.0; // origin
274 endPoint = geometry_msgs::Point();
275 endPoint.x = startPoint.x + velocity(0);
276 endPoint.y = startPoint.y + velocity(1);
277 endPoint.z = startPoint.z + velocity(2);
278 marker.points.push_back(startPoint);
279 marker.points.push_back(endPoint);
280 // Quaternion
281 tf::Quaternion q = tf::createQuaternionFromRPY(0, 0, 0);
282 tf::quaternionTFToMsg(q, marker.pose.orientation);
283}
284
285void HolisticFusionRos::createContactMarker(const std::string& referenceFrameName, const ros::Time& stamp, const Eigen::Vector3d& position,
286 const std::string& nameSpace, const int id, const Eigen::Vector3d& colorRgb,
287 visualization_msgs::Marker& marker) {
288 // Sphere
289 marker.header.frame_id = referenceFrameName;
290 marker.header.stamp = stamp;
291 marker.ns = nameSpace;
292 marker.id = id;
293 marker.type = visualization_msgs::Marker::SPHERE;
294 marker.action = visualization_msgs::Marker::ADD;
295 // Scale and Color
296 marker.scale.x = 0.1;
297 marker.scale.y = 0.1;
298 marker.scale.z = 0.1;
299 marker.color.a = 1.0;
300 marker.color.r = colorRgb(0);
301 marker.color.g = colorRgb(1);
302 marker.color.b = colorRgb(2);
303 // Position
304 marker.pose.position.x = position(0);
305 marker.pose.position.y = position(1);
306 marker.pose.position.z = position(2);
307 // Orientation
308 marker.pose.orientation.x = 0.0;
309 marker.pose.orientation.y = 0.0;
310 marker.pose.orientation.z = 0.0;
311 marker.pose.orientation.w = 1.0;
312}
313
315 currentTime = std::chrono::high_resolution_clock::now();
316 return std::chrono::duration_cast<std::chrono::seconds>(currentTime - startTime).count();
317}
318
319// Main IMU Callback
320void HolisticFusionRos::imuCallback(const sensor_msgs::Imu::ConstPtr& imuMsgPtr) {
321 // Convert to Eigen
322 Eigen::Vector3d linearAcc(imuMsgPtr->linear_acceleration.x, imuMsgPtr->linear_acceleration.y, imuMsgPtr->linear_acceleration.z);
323 Eigen::Vector3d angularVel(imuMsgPtr->angular_velocity.x, imuMsgPtr->angular_velocity.y, imuMsgPtr->angular_velocity.z);
324 Eigen::Matrix<double, 6, 1> addedImuMeasurements; // accel, gyro
325
326 // Create pointer for carrying state
327 std::shared_ptr<SafeIntegratedNavState> preIntegratedNavStatePtr = nullptr;
328 std::shared_ptr<SafeNavStateWithCovarianceAndBias> optimizedStateWithCovarianceAndBiasPtr = nullptr;
329
330 // Add measurement and get state
331 if (HolisticFusion::addCoreImuMeasurementAndGetState(linearAcc, angularVel, imuMsgPtr->header.stamp.toSec(), preIntegratedNavStatePtr,
332 optimizedStateWithCovarianceAndBiasPtr, addedImuMeasurements)) {
333 // Encountered Delay
334 if (ros::Time::now() - ros::Time(preIntegratedNavStatePtr->getTimeK()) > ros::Duration(0.5)) {
335 REGULAR_COUT << RED_START << " Encountered delay of " << std::setprecision(14)
336 << (ros::Time::now() - ros::Time(preIntegratedNavStatePtr->getTimeK())).toSec() << " seconds." << COLOR_END << std::endl;
337 }
338
339 // Publish Odometry
340 this->publishState(preIntegratedNavStatePtr, optimizedStateWithCovarianceAndBiasPtr);
341
342 // Publish Filtered Imu Measurements
343 // this->publishAddedImuMeas_(addedImuMeasurements, imuMsgPtr->header.stamp);
344 } else if (HolisticFusion::isGraphInited()) {
345 REGULAR_COUT << RED_START << " Could not add IMU measurement." << COLOR_END << std::endl;
346 }
347}
348
349// Publish state ---------------------------------------------------------------
350// Higher Level Functions
352 const std::shared_ptr<holistic_fusion::SafeIntegratedNavState>& integratedNavStatePtr,
353 const std::shared_ptr<holistic_fusion::SafeNavStateWithCovarianceAndBias>& optimizedStateWithCovarianceAndBiasPtr) {
354 // Covariances
355 Eigen::Matrix<double, 6, 6> poseCovarianceRos, twistCovarianceRos;
356 extractCovariancesFromOptimizedState(poseCovarianceRos, twistCovarianceRos, optimizedStateWithCovarianceAndBiasPtr);
357
358 // Variances (only digonal elements
359 Eigen::Vector3d positionVarianceRos = poseCovarianceRos.block<3, 3>(0, 0).diagonal();
360 Eigen::Vector3d orientationVarianceRos = poseCovarianceRos.block<3, 3>(3, 3).diagonal();
361
362 // Publish non-time critical data in a separate thread
363 std::thread publishNonTimeCriticalDataThread(&HolisticFusionRos::publishNonTimeCriticalData, this, poseCovarianceRos, twistCovarianceRos,
364 positionVarianceRos, orientationVarianceRos, integratedNavStatePtr,
365 optimizedStateWithCovarianceAndBiasPtr);
366 publishNonTimeCriticalDataThread.detach();
367}
368
369// Copy the arguments in order to be thread safe
371 const Eigen::Matrix<double, 6, 6> poseCovarianceRos, const Eigen::Matrix<double, 6, 6> twistCovarianceRos,
372 const Eigen::Vector3d positionVarianceRos, const Eigen::Vector3d orientationVarianceRos,
373 const std::shared_ptr<const holistic_fusion::SafeIntegratedNavState> integratedNavStatePtr,
374 const std::shared_ptr<const holistic_fusion::SafeNavStateWithCovarianceAndBias> optimizedStateWithCovarianceAndBiasPtr) {
375 // Mutex for not overloading ROS
376 std::lock_guard<std::mutex> lock(rosPublisherMutex_);
377
378 // Time
379 const double& timeK = integratedNavStatePtr->getTimeK(); // Alias
380
381 // Odometry messages
382 publishImuOdoms(integratedNavStatePtr, poseCovarianceRos, twistCovarianceRos);
383
384 // Publish to TF
385 // B_O
386 Eigen::Isometry3d T_B_Ok =
387 staticTransformsPtr_->rv_T_frame1_frame2(staticTransformsPtr_->getBaseLinkFrame(), staticTransformsPtr_->getImuFrame()) *
388 integratedNavStatePtr->getT_O_Ik_gravityAligned().inverse();
389 publishTfTreeTransform(staticTransformsPtr_->getBaseLinkFrame(), staticTransformsPtr_->getOdomFrame(), timeK, T_B_Ok);
390 // O_W
391 Eigen::Isometry3d T_O_W = integratedNavStatePtr->getT_W_O().inverse();
392 publishTfTreeTransform(staticTransformsPtr_->getOdomFrame(), staticTransformsPtr_->getWorldFrame(), timeK, T_O_W);
393
394 // Publish Variances
395 publishDiagVarianceVectors(positionVarianceRos, orientationVarianceRos, timeK);
396
397 // Publish Velocity Markers
398 publishVelocityMarkers(integratedNavStatePtr);
399
400 // Publish paths
401 publishImuPaths(integratedNavStatePtr);
402
403 // Optimized estimate ----------------------
404 publishOptimizedStateAndBias(optimizedStateWithCovarianceAndBiasPtr, poseCovarianceRos, twistCovarianceRos);
405}
406
408 const std::shared_ptr<const holistic_fusion::SafeNavStateWithCovarianceAndBias> optimizedStateWithCovarianceAndBiasPtr,
409 const Eigen::Matrix<double, 6, 6>& poseCovarianceRos, const Eigen::Matrix<double, 6, 6>& twistCovarianceRos) {
410 if (optimizedStateWithCovarianceAndBiasPtr != nullptr &&
411 optimizedStateWithCovarianceAndBiasPtr->getTimeK() - lastOptimizedStateTimestamp_ > 1e-03) {
412 // Time of this optimized state
413 lastOptimizedStateTimestamp_ = optimizedStateWithCovarianceAndBiasPtr->getTimeK();
414
415 // Odometry messages
416 // world->imu
417 if (pubOptWorldImu_.getNumSubscribers() > 0) {
419 ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK()), optimizedStateWithCovarianceAndBiasPtr->getT_W_Ik(),
420 optimizedStateWithCovarianceAndBiasPtr->getI_v_W_I(), optimizedStateWithCovarianceAndBiasPtr->getI_w_W_I(),
421 poseCovarianceRos, twistCovarianceRos);
423 }
424
425 // Path
426 // world->imu
427 addToPathMsg(optWorldImuPathPtr_, staticTransformsPtr_->getWorldFrame(), ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK()),
428 optimizedStateWithCovarianceAndBiasPtr->getT_W_Ik().translation(), graphConfigPtr_->imuBufferLength_ * 20);
429 if (pubOptWorldImuPath_.getNumSubscribers() > 0) {
431 }
432
433 // Biases
434 // Publish accel bias
435 if (pubAccelBias_.getNumSubscribers() > 0) {
436 Eigen::Vector3d accelBias = optimizedStateWithCovarianceAndBiasPtr->getAccelerometerBias();
437 accelBiasMsgPtr_->header.stamp = ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK());
438 accelBiasMsgPtr_->header.frame_id = staticTransformsPtr_->getImuFrame();
439 accelBiasMsgPtr_->vector.x = accelBias(0);
440 accelBiasMsgPtr_->vector.y = accelBias(1);
441 accelBiasMsgPtr_->vector.z = accelBias(2);
443 }
444 // Publish gyro bias
445 if (pubGyroBias_.getNumSubscribers() > 0) {
446 Eigen::Vector3d gyroBias = optimizedStateWithCovarianceAndBiasPtr->getGyroscopeBias();
447 gyroBiasMsgPtr_->header.stamp = ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK());
448 gyroBiasMsgPtr_->header.frame_id = staticTransformsPtr_->getImuFrame();
449 gyroBiasMsgPtr_->vector.x = gyroBias(0);
450 gyroBiasMsgPtr_->vector.y = gyroBias(1);
451 gyroBiasMsgPtr_->vector.z = gyroBias(2);
453 }
454
455 // Reference Frame TFs in Optimized State
456 for (const auto& framePairTransformMapIterator :
457 optimizedStateWithCovarianceAndBiasPtr->getReferenceFrameTransforms().getTransformsMap()) {
458 // Case 1: If includes world frame --> everything child of world --------------------------------
459 if (framePairTransformMapIterator.first.first == staticTransformsPtr_->getWorldFrame() ||
460 framePairTransformMapIterator.first.second == staticTransformsPtr_->getWorldFrame()) {
461 // A. Get transform
462 Eigen::Isometry3d T_W_M;
463 std::string mapFrameName;
464 const std::string& worldFrameName = staticTransformsPtr_->getWorldFrame();
465
466 // If world is second, then map is second
467 if (framePairTransformMapIterator.first.second == worldFrameName) {
468 T_W_M = framePairTransformMapIterator.second.inverse();
469 mapFrameName = framePairTransformMapIterator.first.first;
470 }
471 // If world is first, then map is second, this is the case for holistic alignment
472 else {
473 T_W_M = framePairTransformMapIterator.second;
474 mapFrameName = framePairTransformMapIterator.first.second;
475
476 // B. Publish TransformStamped for Aligned Frames (dynamically create publisher) --> only for reference frames
477 std::string transformTopic = "/holistic_fusion/transform_" + worldFrameName + "_to_" + mapFrameName + referenceFrameAlignedNameId;
478 geometry_msgs::PoseWithCovarianceStampedPtr poseWithCovarianceStampedMsgPtr =
479 boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>();
481 poseWithCovarianceStampedMsgPtr, staticTransformsPtr_->getWorldFrame(),
482 ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK()), T_W_M,
483 optimizedStateWithCovarianceAndBiasPtr->getReferenceFrameTransformsCovariance().rv_T_frame1_frame2(
484 framePairTransformMapIterator.first.first, framePairTransformMapIterator.first.second));
485 // Check whether publisher already exists
486 if (pubPoseStampedByTopicMap_.find(transformTopic) == pubPoseStampedByTopicMap_.end()) {
487 pubPoseStampedByTopicMap_[transformTopic] = privateNode.advertise<geometry_msgs::PoseWithCovarianceStamped>(transformTopic, 1);
489 REGULAR_COUT << GREEN_START << " Initialized publisher for " << transformTopic << COLOR_END << std::endl;
490 }
491 }
492 if (pubPoseStampedByTopicMap_[transformTopic].getNumSubscribers() > 0) {
493 pubPoseStampedByTopicMap_[transformTopic].publish(poseWithCovarianceStampedMsgPtr);
494 }
495 }
496
497 // Print for debug
499 std::cout << "Transformation from " << mapFrameName << " to " << worldFrameName << std::endl;
500 std::cout << "Uncertainty: " << std::endl
501 << optimizedStateWithCovarianceAndBiasPtr->getReferenceFrameTransformsCovariance().rv_T_frame1_frame2(
502 framePairTransformMapIterator.first.first, framePairTransformMapIterator.first.second)
503 << std::endl;
504 }
505
506 // C. Publish TF Tree --> everything children of world
507 publishTfTreeTransform(worldFrameName, mapFrameName + referenceFrameAlignedNameId,
508 optimizedStateWithCovarianceAndBiasPtr->getTimeK(), T_W_M);
509 }
510 // Case 2: Other transformation (e.g. calibration) --> does usually not include world frame ----------------
511 else {
512 const std::string& sensorFrameName = framePairTransformMapIterator.first.first;
513 const std::string& sensorFrameNameCorrected = framePairTransformMapIterator.first.second;
514 const Eigen::Isometry3d& T_sensor_sensorCorrected = framePairTransformMapIterator.second;
515
516 // A. Publish TransformStamped for Corrected Frames
517 std::string transformTopic = "/holistic_fusion/transform_" + sensorFrameName + "_to_" + sensorFrameNameCorrected;
518 geometry_msgs::PoseWithCovarianceStampedPtr poseWithCovarianceStampedMsgPtr =
519 boost::make_shared<geometry_msgs::PoseWithCovarianceStamped>();
521 poseWithCovarianceStampedMsgPtr, sensorFrameName, ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK()),
522 T_sensor_sensorCorrected,
523 optimizedStateWithCovarianceAndBiasPtr->getReferenceFrameTransformsCovariance().rv_T_frame1_frame2(sensorFrameName,
524 sensorFrameNameCorrected));
525 // Check whether publisher already exists
526 if (pubPoseStampedByTopicMap_.find(transformTopic) == pubPoseStampedByTopicMap_.end()) {
527 pubPoseStampedByTopicMap_[transformTopic] = privateNode.advertise<geometry_msgs::PoseWithCovarianceStamped>(transformTopic, 1);
528 REGULAR_COUT << GREEN_START << " Initialized publisher for " << transformTopic << COLOR_END << std::endl;
529 }
530 if (pubPoseStampedByTopicMap_[transformTopic].getNumSubscribers() > 0) {
531 pubPoseStampedByTopicMap_[transformTopic].publish(poseWithCovarianceStampedMsgPtr);
532 }
533
534 // B. Publish TF Tree --> as children of sensor frame
535 publishTfTreeTransform(sensorFrameName, sensorFrameNameCorrected, optimizedStateWithCovarianceAndBiasPtr->getTimeK(),
536 T_sensor_sensorCorrected);
537 }
538 }
539
540 // Landmarks
541 for (const auto& landmarkTransformMapIterator : optimizedStateWithCovarianceAndBiasPtr->getLandmarkTransforms().getTransformsMap()) {
542 // A. Get transform
543 Eigen::Isometry3d T_W_L = landmarkTransformMapIterator.second;
544 std::string worldFrameName = landmarkTransformMapIterator.first.first;
545 std::string landmarkFrameName = landmarkTransformMapIterator.first.second;
546
547 // B. Publish TransformStamped for Landmarks
548 std::string landmarkName = "/holistic_fusion/landmark_" + staticTransformsPtr_->getWorldFrame() + "_to_" + landmarkFrameName;
550 ros::Time(optimizedStateWithCovarianceAndBiasPtr->getTimeK()), T_W_L, landmarkName);
551 // Publish if there is a subscriber
552 if (pubLandmarks_.getNumSubscribers() > 0) {
554 }
555 }
556 }
557}
558
559// Lower Level Functions
560void HolisticFusionRos::publishTfTreeTransform(const std::string& parentFrameName, const std::string& childFrameName, const double timeStamp,
561 const Eigen::Isometry3d& T_frame_childFrame) {
562 tf::Transform transform_frame_childFrame = isometry3ToTf(T_frame_childFrame);
563 tfBroadcaster.sendTransform(tf::StampedTransform(transform_frame_childFrame, ros::Time(timeStamp), parentFrameName, childFrameName));
564}
565
566void HolisticFusionRos::publishImuOdoms(const std::shared_ptr<const holistic_fusion::SafeIntegratedNavState>& preIntegratedNavStatePtr,
567 const Eigen::Matrix<double, 6, 6>& poseCovarianceRos,
568 const Eigen::Matrix<double, 6, 6>& twistCovarianceRos) const {
569 // Odom->imu with 100 Hz
570 if (pubEstOdomImu_.getNumSubscribers() > 0) {
572 ros::Time(preIntegratedNavStatePtr->getTimeK()), preIntegratedNavStatePtr->getT_O_Ik_gravityAligned(),
573 preIntegratedNavStatePtr->getI_v_W_I(), preIntegratedNavStatePtr->getI_w_W_I(), poseCovarianceRos, twistCovarianceRos);
575 }
576 // World->imu with 100 Hz
577 if (pubEstWorldImu_.getNumSubscribers() > 0) {
579 ros::Time(preIntegratedNavStatePtr->getTimeK()), preIntegratedNavStatePtr->getT_W_Ik(),
580 preIntegratedNavStatePtr->getI_v_W_I(), preIntegratedNavStatePtr->getI_w_W_I(), poseCovarianceRos, twistCovarianceRos);
582 }
583}
584
585void HolisticFusionRos::publishDiagVarianceVectors(const Eigen::Vector3d& posVarianceRos, const Eigen::Vector3d& rotVarianceRos,
586 const double timeStamp) const {
587 // World Position Variance
588 if (pubEstWorldPosVariance_.getNumSubscribers() > 0) {
589 estWorldPosVarianceMsgPtr_->header.stamp = ros::Time(timeStamp);
590 estWorldPosVarianceMsgPtr_->header.frame_id = staticTransformsPtr_->getWorldFrame();
591 estWorldPosVarianceMsgPtr_->vector.x = posVarianceRos(0);
592 estWorldPosVarianceMsgPtr_->vector.y = posVarianceRos(1);
593 estWorldPosVarianceMsgPtr_->vector.z = posVarianceRos(2);
595 }
596 // World Rotation Variance
597 if (pubEstWorldRotVariance_.getNumSubscribers() > 0) {
598 estWorldRotVarianceMsgPtr_->header.stamp = ros::Time(timeStamp);
599 estWorldRotVarianceMsgPtr_->header.frame_id = staticTransformsPtr_->getWorldFrame();
600 estWorldRotVarianceMsgPtr_->vector.x = rotVarianceRos(0);
601 estWorldRotVarianceMsgPtr_->vector.y = rotVarianceRos(1);
602 estWorldRotVarianceMsgPtr_->vector.z = rotVarianceRos(2);
604 }
605}
606
607void HolisticFusionRos::publishVelocityMarkers(const std::shared_ptr<const holistic_fusion::SafeIntegratedNavState>& navStatePtr) const {
608 // Color
609 Eigen::Vector3d colorRgb(1.0, 0.0, 0.0); // Red
610 // Velocity in Odom Frame Marker
611 visualization_msgs::Marker velocityMarker;
612 createVelocityMarker(staticTransformsPtr_->getImuFrame(), ros::Time(navStatePtr->getTimeK()), navStatePtr->getI_v_W_I(), colorRgb,
613 velocityMarker);
614 // Publish
615 if (pubVelocityMarker_.getNumSubscribers() > 0) {
616 pubVelocityMarker_.publish(velocityMarker);
617 }
618}
619
620void HolisticFusionRos::publishImuPaths(const std::shared_ptr<const holistic_fusion::SafeIntegratedNavState>& navStatePtr) const {
621 // odom->imu
622 addToPathMsg(estOdomImuPathPtr_, staticTransformsPtr_->getOdomFrame(), ros::Time(navStatePtr->getTimeK()),
623 navStatePtr->getT_O_Ik_gravityAligned().translation(), graphConfigPtr_->imuBufferLength_ * 20);
624 if (pubEstOdomImuPath_.getNumSubscribers() > 0) {
626 }
627 // world->imu
628 addToPathMsg(estWorldImuPathPtr_, staticTransformsPtr_->getWorldFrame(), ros::Time(navStatePtr->getTimeK()),
629 (navStatePtr->getT_W_Ik()).translation(), graphConfigPtr_->imuBufferLength_ * 20);
630 if (pubEstWorldImuPath_.getNumSubscribers() > 0) {
632 }
633}
634
635void HolisticFusionRos::publishAddedImuMeas(const Eigen::Matrix<double, 6, 1>& addedImuMeas, const ros::Time& stamp) const {
636 // Publish added imu measurement
637 if (pubAddedImuMeas_.getNumSubscribers() > 0) {
638 sensor_msgs::Imu addedImuMeasMsg;
639 addedImuMeasMsg.header.stamp = stamp;
640 addedImuMeasMsg.header.frame_id = staticTransformsPtr_->getImuFrame();
641 addedImuMeasMsg.linear_acceleration.x = addedImuMeas(0);
642 addedImuMeasMsg.linear_acceleration.y = addedImuMeas(1);
643 addedImuMeasMsg.linear_acceleration.z = addedImuMeas(2);
644 addedImuMeasMsg.angular_velocity.x = addedImuMeas(3);
645 addedImuMeasMsg.angular_velocity.y = addedImuMeas(4);
646 addedImuMeasMsg.angular_velocity.z = addedImuMeas(5);
647 pubAddedImuMeas_.publish(addedImuMeasMsg);
648 }
649}
650
651} // namespace holistic_fusion
#define REGULAR_COUT
#define COLOR_END
#define GREEN_START
#define RED_START
#define ROS_QUEUE_SIZE
#define YELLOW_START
#define NUM_MARKERS_IN_ARRAY
#define NUM_KEEP_MARKER_NAMES
static void addToLandmarkMarkerArrayMsg(const visualization_msgs::MarkerArrayPtr &markerArrayPtr, const std::string &referenceFrameName, const ros::Time &stamp, const Eigen::Isometry3d &T_R_L, const std::string &transformName)
virtual bool srvOfflineSmootherOptimizeCallback(holistic_fusion_ros_msgs::OfflineOptimizationTrigger::Request &req, holistic_fusion_ros_msgs::OfflineOptimizationTrigger::Response &res)
std::chrono::time_point< std::chrono::high_resolution_clock > startTime
static void createVelocityMarker(const std::string &referenceFrameName, const ros::Time &stamp, const Eigen::Vector3d &velocity, const Eigen::Vector3d &colorRgb, visualization_msgs::Marker &marker)
bool srvLogRealTimeNavStatesCallback(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
void setup(const std::shared_ptr< StaticTransforms > staticTransformsPtr)
virtual void initializeServices(ros::NodeHandle &privateNode)
std::chrono::time_point< std::chrono::high_resolution_clock > currentTime
static void addToPathMsg(const nav_msgs::PathPtr &pathPtr, const std::string &frameName, const ros::Time &stamp, const Eigen::Vector3d &t, int maxBufferLength)
visualization_msgs::MarkerArrayPtr landmarkMarkersMsgPtr_
void publishOptimizedStateAndBias(const std::shared_ptr< const holistic_fusion::SafeNavStateWithCovarianceAndBias > optimizedStateWithCovarianceAndBiasPtr, const Eigen::Matrix< double, 6, 6 > &poseCovarianceRos, const Eigen::Matrix< double, 6, 6 > &twistCovarianceRos)
void publishImuOdoms(const std::shared_ptr< const holistic_fusion::SafeIntegratedNavState > &preIntegratedNavStatePtr, const Eigen::Matrix< double, 6, 6 > &poseCovarianceRos, const Eigen::Matrix< double, 6, 6 > &twistCovarianceRos) const
geometry_msgs::Vector3StampedPtr estWorldPosVarianceMsgPtr_
std::map< std::string, ros::Publisher > pubPoseStampedByTopicMap_
static void addToOdometryMsg(const nav_msgs::OdometryPtr &msgPtr, const std::string &fixedFrame, const std::string &movingFrame, const ros::Time &stamp, const Eigen::Isometry3d &T, const Eigen::Vector3d &W_v_W_F, const Eigen::Vector3d &W_w_W_F, const Eigen::Matrix< double, 6, 6 > &poseCovariance=Eigen::Matrix< double, 6, 6 >::Zero(), const Eigen::Matrix< double, 6, 6 > &twistCovariance=Eigen::Matrix< double, 6, 6 >::Zero())
void publishVelocityMarkers(const std::shared_ptr< const holistic_fusion::SafeIntegratedNavState > &navStatePtr) const
virtual void initializeSubscribers(ros::NodeHandle &privateNode)
static void createContactMarker(const std::string &referenceFrameName, const ros::Time &stamp, const Eigen::Vector3d &position, const std::string &nameSpace, const int id, const Eigen::Vector3d &colorRgb, visualization_msgs::Marker &marker)
geometry_msgs::Vector3StampedPtr estWorldRotVarianceMsgPtr_
static void extractCovariancesFromOptimizedState(Eigen::Matrix< double, 6, 6 > &poseCovarianceRos, Eigen::Matrix< double, 6, 6 > &twistCovarianceRos, const std::shared_ptr< holistic_fusion::SafeNavStateWithCovarianceAndBias > &optimizedStateWithCovarianceAndBiasPtr)
virtual void readParams(const ros::NodeHandle &privateNode)
geometry_msgs::Vector3StampedPtr gyroBiasMsgPtr_
virtual void initializePublishers(ros::NodeHandle &privateNode)
virtual void imuCallback(const sensor_msgs::Imu::ConstPtr &imuPtr)
void publishNonTimeCriticalData(const Eigen::Matrix< double, 6, 6 > poseCovarianceRos, const Eigen::Matrix< double, 6, 6 > twistCovarianceRos, const Eigen::Vector3d positionVarianceRos, const Eigen::Vector3d orientationVarianceRos, const std::shared_ptr< const holistic_fusion::SafeIntegratedNavState > integratedNavStatePtr, const std::shared_ptr< const holistic_fusion::SafeNavStateWithCovarianceAndBias > optimizedStateWithCovarianceAndBiasPtr)
virtual void initializeMessages(ros::NodeHandle &privateNode)
void publishTfTreeTransform(const std::string &frameName, const std::string &childFrameName, double timeStamp, const Eigen::Isometry3d &T_frame_childFrame)
void publishDiagVarianceVectors(const Eigen::Vector3d &posVarianceRos, const Eigen::Vector3d &rotVarianceRos, const double timeStamp) const
HolisticFusionRos(const std::shared_ptr< ros::NodeHandle > &privateNodePtr)
static void addToPoseWithCovarianceStampedMsg(const geometry_msgs::PoseWithCovarianceStampedPtr &msgPtr, const std::string &frameName, const ros::Time &stamp, const Eigen::Isometry3d &T, const Eigen::Matrix< double, 6, 6 > &transformCovariance=Eigen::Matrix< double, 6, 6 >::Zero())
void publishAddedImuMeas(const Eigen::Matrix< double, 6, 1 > &addedImuMeas, const ros::Time &stamp) const
virtual void publishState(const std::shared_ptr< holistic_fusion::SafeIntegratedNavState > &integratedNavStatePtr, const std::shared_ptr< holistic_fusion::SafeNavStateWithCovarianceAndBias > &optimizedStateWithCovarianceAndBiasPtr)
void publishImuPaths(const std::shared_ptr< const holistic_fusion::SafeIntegratedNavState > &navStatePtr) const
geometry_msgs::Vector3StampedPtr accelBiasMsgPtr_
tf::TransformBroadcaster tfBroadcaster
void setup(const std::shared_ptr< GraphConfig > graphConfigPtr, const std::shared_ptr< StaticTransforms > staticTransformsPtr)
std::shared_ptr< GraphConfig > graphConfigPtr_
bool logRealTimeNavStates(const std::string &savePath)
bool addCoreImuMeasurementAndGetState(const Eigen::Vector3d &linearAcc, const Eigen::Vector3d &angularVel, const double imuTimeK, std::shared_ptr< SafeIntegratedNavState > &returnPreIntegratedNavStatePtr, std::shared_ptr< SafeNavStateWithCovarianceAndBias > &returnOptimizedStateWithCovarianceAndBiasPtr, Eigen::Matrix< double, 6, 1 > &returnAddedImuMeasurements)
Main: IMU --------------------—.
bool optimizeSlowBatchSmoother(int maxIterations, const std::string &savePath, const bool saveCovarianceFlag)
std::shared_ptr< StaticTransforms > staticTransformsPtr_
@ kFunctioningAndStateMachine1
Definition GraphConfig.h:17
tf::Transform isometry3ToTf(const Eigen::Isometry3d &T)
Eigen::Matrix< double, 6, 6 > convertCovarianceGtsamConventionToRosConvention(const Eigen::Matrix< double, 6, 6 > &covGtsam)
Convert a 6x6 covariance matrix from ROS convention to GTSAM convention.
Definition conversions.h:18