@@ -84,6 +84,12 @@ ElevationMappingNode::ElevationMappingNode(ros::NodeHandle& nh)
8484 channels_[key].push_back (" x" );
8585 channels_[key].push_back (" y" );
8686 channels_[key].push_back (" z" );
87+ // Optional override of the frame used as the visibility-cleanup ray source. If the cloud is
88+ // published in a non-sensor frame (e.g. deskewed into odom), set this to the true sensor frame.
89+ if (subscriber.second .hasMember (" ray_source_frame" )) {
90+ raySourceFrames_[key] = static_cast <std::string>(subscriber.second [" ray_source_frame" ]);
91+ ROS_INFO_STREAM (" [" << key << " ] ray_source_frame override: " << raySourceFrames_[key]);
92+ }
8793 boost::function<void (const sensor_msgs::PointCloud2&)> f = boost::bind (&ElevationMappingNode::pointcloudCallback, this , _1, key);
8894 ros::Subscriber sub = nh_.subscribe <sensor_msgs::PointCloud2>(pointcloud_topic, 1 , f);
8995 pointcloudSubs_.push_back (sub);
@@ -310,14 +316,14 @@ void ElevationMappingNode::pointcloudCallback(const sensor_msgs::PointCloud2& cl
310316 auto & field = fields[it];
311317 channels.push_back (field.name );
312318 }
313- inputPointCloud (cloud, channels);
319+ inputPointCloud (cloud, channels, key );
314320
315321 // This is used for publishing as statistics.
316322 pointCloudProcessCounter_++;
317323}
318324
319325void ElevationMappingNode::inputPointCloud (const sensor_msgs::PointCloud2& cloud,
320- const std::vector<std::string>& channels) {
326+ const std::vector<std::string>& channels, const std::string& key ) {
321327 auto start = ros::Time::now ();
322328 auto * pcl_pc = new pcl::PCLPointCloud2;
323329 pcl::PCLPointCloud2ConstPtr cloudPtr (pcl_pc);
@@ -351,15 +357,53 @@ void ElevationMappingNode::inputPointCloud(const sensor_msgs::PointCloud2& cloud
351357 return ;
352358 }
353359
360+ // Resolve the sensor origin used for visibility-cleanup ray tracing and point validity.
361+ // By default this is the origin of the cloud's header frame. If a ray_source_frame is
362+ // configured for this subscriber (e.g. because the cloud is deskewed into odom and its
363+ // header frame no longer coincides with the sensor), look up that frame's origin instead.
364+ Eigen::Vector3d rayOrigin = transformationSensorToMap.translation ();
365+ std::string rayFrameUsed = sensorFrameId;
366+ auto rayFrameIt = raySourceFrames_.find (key);
367+ if (rayFrameIt != raySourceFrames_.end () && !rayFrameIt->second .empty ()) {
368+ tf::StampedTransform raySourceTf;
369+ Eigen::Affine3d raySourceToMap;
370+ try {
371+ transformListener_.waitForTransform (mapFrameId_, rayFrameIt->second , timeStamp, ros::Duration (1.0 ));
372+ transformListener_.lookupTransform (mapFrameId_, rayFrameIt->second , timeStamp, raySourceTf);
373+ poseTFToEigen (raySourceTf, raySourceToMap);
374+ rayOrigin = raySourceToMap.translation ();
375+ rayFrameUsed = rayFrameIt->second ;
376+ } catch (tf::TransformException& ex) {
377+ // A configured ray_source_frame that cannot be resolved must NOT silently fall back to the
378+ // cloud header frame: that origin (e.g. odom) is exactly what causes the supporting surface
379+ // to be erased by visibility cleanup. Drop this cloud instead and raise a loud alert.
380+ ROS_ERROR_STREAM_THROTTLE (
381+ 2.0 , " \n "
382+ << " ========================================================================\n "
383+ << " [ELEVATION MAPPING] ray_source_frame NOT AVAILABLE -- SKIPPING CLOUD\n "
384+ << " subscriber key : " << key << " \n "
385+ << " ray_source_frame : " << rayFrameIt->second << " \n "
386+ << " map frame : " << mapFrameId_ << " \n "
387+ << " TF error : " << ex.what () << " \n "
388+ << " This point cloud is NOT used for mapping. Fix the TF tree or the\n "
389+ << " ray_source_frame setting in the sensor parameter YAML.\n "
390+ << " ========================================================================" );
391+ return ;
392+ }
393+ }
394+ ROS_INFO_STREAM_THROTTLE (5.0 , " [" << key << " ] point frame: " << sensorFrameId << " , cleanup ray source frame: "
395+ << rayFrameUsed << " , ray origin in " << mapFrameId_ << " = ["
396+ << rayOrigin.x () << " , " << rayOrigin.y () << " , " << rayOrigin.z () << " ]" );
397+
354398 double positionError{0.0 };
355399 double orientationError{0.0 };
356400 {
357401 std::lock_guard<std::mutex> lock (errorMutex_);
358402 positionError = positionError_;
359403 orientationError = orientationError_;
360404 }
361- map_.input (points, channels, transformationSensorToMap.rotation (), transformationSensorToMap.translation (), positionError ,
362- orientationError);
405+ map_.input (points, channels, transformationSensorToMap.rotation (), transformationSensorToMap.translation (), rayOrigin ,
406+ positionError, orientationError);
363407
364408 if (enableDriftCorrectedTFPublishing_) {
365409 publishMapToOdom (map_.get_additive_mean_error ());
0 commit comments