Skip to content

Commit

Permalink
odom: Added always_check_imu_tf parameter. sync: increased default sy…
Browse files Browse the repository at this point in the history
…nc_queue_size from 2 to 5 (to be more flexible to different hardware), added input and output diagnostics. rtabmap_viz: fixed node with same name redeclared warning.
  • Loading branch information
matlabbe committed Oct 20, 2024
1 parent 01793f7 commit b888fe3
Show file tree
Hide file tree
Showing 30 changed files with 394 additions and 112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.pydevproject
.settings
__pycache__
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ RTAB-Map's ROS2 package (branch `ros2`). **ROS2 Foxy minimum required**: current

* For sensor integration examples (stereo and RGB-D cameras, 3D LiDAR), see [rtabmap_examples](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_examples/launch) sub-folder.

* For robot integration examples (turtlebot3 and turtlebot4, nav2 integration), see [rtabmap_demos](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_demos/launch) sub-folder.
* For robot integration examples (turtlebot3 and turtlebot4, nav2 integration), see [rtabmap_demos](https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_demos) sub-folder.

# Installation

Expand Down
2 changes: 1 addition & 1 deletion rtabmap_conversions/src/MsgConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2169,7 +2169,7 @@ bool convertRGBDMsgs(
rtabmap::Transform localTransform = rtabmap_conversions::getTransform(frameId, !imageMsgs.empty()?imageMsgs[i]->header.frame_id:cameraInfoMsgs[i].header.frame_id, stamp, listener, waitForTransform);
if(localTransform.isNull())
{
UERROR("TF of received image %d at time %fs is not set!", i, stamp.seconds());
UERROR("TF of received image for camera %d at time %fs is not set!", i, stamp.seconds());
return false;
}
// sync with odometry stamp
Expand Down
10 changes: 7 additions & 3 deletions rtabmap_launch/launch/rtabmap.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
"always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
"config_path": LaunchConfiguration('cfg').perform(context),
Expand Down Expand Up @@ -210,6 +211,7 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
"always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"approx_sync_max_interval": LaunchConfiguration('approx_sync_max_interval'),
"config_path": LaunchConfiguration('cfg').perform(context),
Expand Down Expand Up @@ -247,6 +249,7 @@ def launch_setup(context, *args, **kwargs):
"ground_truth_base_frame_id": LaunchConfiguration('ground_truth_base_frame_id').perform(context),
"wait_for_transform": LaunchConfiguration('wait_for_transform'),
"wait_imu_to_init": LaunchConfiguration('wait_imu_to_init'),
"always_check_imu_tf": LaunchConfiguration('always_check_imu_tf'),
"approx_sync": LaunchConfiguration('approx_sync'),
"config_path": LaunchConfiguration('cfg').perform(context),
"topic_queue_size": LaunchConfiguration('topic_queue_size'),
Expand Down Expand Up @@ -431,7 +434,7 @@ def generate_launch_description():
DeclareLaunchArgument('namespace', default_value='rtabmap', description=''),
DeclareLaunchArgument('database_path', default_value='~/.ros/rtabmap.db', description='Where is the map saved/loaded.'),
DeclareLaunchArgument('topic_queue_size', default_value='10', description='Queue size of individual topic subscribers.'),
DeclareLaunchArgument('queue_size', default_value='2', description='Backward compatibility, use "sync_queue_size" instead.'),
DeclareLaunchArgument('queue_size', default_value='5', description='Backward compatibility, use "sync_queue_size" instead.'),
DeclareLaunchArgument('qos', default_value='1', description='General QoS used for sensor input data: 0=system default, 1=Reliable, 2=Best Effort.'),
DeclareLaunchArgument('wait_for_transform', default_value='0.2', description=''),
DeclareLaunchArgument('rtabmap_args', default_value='', description='Backward compatibility, use "args" instead.'),
Expand Down Expand Up @@ -492,11 +495,12 @@ def generate_launch_description():
DeclareLaunchArgument('odom_guess_frame_id', default_value='', description=''),
DeclareLaunchArgument('odom_guess_min_translation', default_value='0.0', description=''),
DeclareLaunchArgument('odom_guess_min_rotation', default_value='0.0', description=''),

# imu
DeclareLaunchArgument('imu_topic', default_value='/imu/data', description='Used with VIO approaches and for SLAM graph optimization (gravity constraints).'),
DeclareLaunchArgument('wait_imu_to_init', default_value='false', description=''),

DeclareLaunchArgument('always_check_imu_tf', default_value='true', description='The odometry node will always check if TF between IMU frame and base frame has changed. If false, it is checked till a valid transform is initialized.'),

# User Data
DeclareLaunchArgument('subscribe_user_data', default_value='false', description='User data synchronized subscription.'),
DeclareLaunchArgument('user_data_topic', default_value='/user_data', description=''),
Expand Down
5 changes: 4 additions & 1 deletion rtabmap_odom/include/rtabmap_odom/OdometryROS.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class OdometryROS : public rclcpp::Node, public UThread

protected:
rclcpp::CallbackGroup::SharedPtr dataCallbackGroup_;
void tick(const rclcpp::Time & stamp);

private:
rtabmap::Odometry * odometry_;
Expand Down Expand Up @@ -178,10 +179,12 @@ class OdometryROS : public rclcpp::Node, public UThread
bool compressionParallelized_;
int odomStrategy_;
bool waitIMUToinit_;
bool alwaysCheckImuTf_;
bool imuProcessed_;
std::map<double, rtabmap::IMU> imus_;
std::map<double, sensor_msgs::msg::Imu::ConstSharedPtr> imus_;
std::string configPath_;
rtabmap::Transform initialPose_;
rtabmap::Transform imuLocalTransform_;

rtabmap_util::ULogToRosout ulogToRosout_;

Expand Down
90 changes: 66 additions & 24 deletions rtabmap_odom/src/OdometryROS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
compressionParallelized_(true),
odomStrategy_(Parameters::defaultOdomStrategy()),
waitIMUToinit_(false),
alwaysCheckImuTf_(true),
imuProcessed_(false),
configPath_(),
initialPose_(Transform::getIdentity()),
Expand Down Expand Up @@ -146,6 +147,8 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
compressionParallelized_ = this->declare_parameter("sensor_data_parallel_compression", compressionParallelized_);

waitIMUToinit_ = this->declare_parameter("wait_imu_to_init", waitIMUToinit_);
alwaysCheckImuTf_ = this->declare_parameter("always_check_imu_tf", alwaysCheckImuTf_);


configPath_ = uReplaceChar(configPath_, '~', UDirectory::homeDir());
if(configPath_.size() && configPath_.at(0) != '/')
Expand Down Expand Up @@ -200,6 +203,7 @@ OdometryROS::OdometryROS(const std::string & name, const rclcpp::NodeOptions & o
RCLCPP_INFO(this->get_logger(), "Odometry: max_update_rate = %f Hz", maxUpdateRate_);
RCLCPP_INFO(this->get_logger(), "Odometry: min_update_rate = %f Hz", minUpdateRate_);
RCLCPP_INFO(this->get_logger(), "Odometry: wait_imu_to_init = %s", waitIMUToinit_?"true":"false");
RCLCPP_INFO(this->get_logger(), "Odometry: always_check_imu_tf = %s", alwaysCheckImuTf_?"true":"false");
RCLCPP_INFO(this->get_logger(), "Odometry: sensor_data_compression_format = %s", compressionImgFormat_.c_str());
RCLCPP_INFO(this->get_logger(), "Odometry: sensor_data_parallel_compression = %s", compressionParallelized_?"true":"false");
}
Expand Down Expand Up @@ -418,28 +422,20 @@ void OdometryROS::callbackIMU(const sensor_msgs::msg::Imu::SharedPtr msg)
double stamp = rtabmap_conversions::timestampFromROS(msg->header.stamp);
//RCLCPP_WARN(get_logger(), "Received imu: %f delay=%f", stamp, (now() - msg->header.stamp).seconds());

rtabmap::Transform localTransform = rtabmap::Transform::getIdentity();
if(this->frameId().compare(msg->header.frame_id) != 0)
{
localTransform = rtabmap_conversions::getTransform(this->frameId(), msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
}
if(localTransform.isNull())
UScopeMutex m(imuMutex_);

if(!imuProcessed_ && imus_.empty())
{
RCLCPP_ERROR(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF not available at time %f",
msg->header.frame_id.c_str(), this->frameId().c_str(), stamp);
return;
rtabmap::Transform localTransform = rtabmap_conversions::getTransform(this->frameId(), msg->header.frame_id, msg->header.stamp, *tfBuffer_, waitForTransform_);
if(localTransform.isNull())
{
RCLCPP_WARN(this->get_logger(), "Dropping imu data! A valid TF between %s and %s is required to initialize IMU.",
this->frameId().c_str(), msg->header.frame_id.c_str());
return;
}
}

IMU imu(cv::Vec4d(msg->orientation.x, msg->orientation.y, msg->orientation.z, msg->orientation.w),
cv::Mat(3,3,CV_64FC1,(void*)msg->orientation_covariance.data()).clone(),
cv::Vec3d(msg->angular_velocity.x, msg->angular_velocity.y, msg->angular_velocity.z),
cv::Mat(3,3,CV_64FC1,(void*)msg->angular_velocity_covariance.data()).clone(),
cv::Vec3d(msg->linear_acceleration.x, msg->linear_acceleration.y, msg->linear_acceleration.z),
cv::Mat(3,3,CV_64FC1,(void*)msg->linear_acceleration_covariance.data()).clone(),
localTransform);

UScopeMutex m(imuMutex_);
imus_.insert(std::make_pair(stamp, imu));
imus_.insert(std::make_pair(stamp, msg));

if(imus_.size() > 1000)
{
Expand Down Expand Up @@ -487,7 +483,7 @@ void OdometryROS::mainLoop()
SensorData & data = dataToProcess_;
std_msgs::msg::Header & header = dataHeaderToProcess_;

std::vector<std::pair<double, rtabmap::IMU> > imus;
std::vector<std::pair<double, sensor_msgs::msg::Imu::ConstSharedPtr> > imus;
{
UScopeMutex m(imuMutex_);

Expand All @@ -504,21 +500,58 @@ void OdometryROS::mainLoop()
return;
}
// process all imu data up to current image stamp (or just after so that underlying odom approach can do interpolation of imu at image stamp)
std::map<double, rtabmap::IMU>::iterator iterEnd = imus_.lower_bound(rtabmap_conversions::timestampFromROS(header.stamp));
std::map<double, sensor_msgs::msg::Imu::ConstSharedPtr>::iterator iterEnd = imus_.lower_bound(rtabmap_conversions::timestampFromROS(header.stamp));
if(iterEnd!= imus_.end())
{
++iterEnd;
}
for(std::map<double, rtabmap::IMU>::iterator iter=imus_.begin(); iter!=iterEnd;)
for(std::map<double, sensor_msgs::msg::Imu::ConstSharedPtr>::iterator iter=imus_.begin(); iter!=iterEnd;)
{
imus.push_back(*iter);
imus_.erase(iter++);
}
} // end imu lock

bool imuWarnShown = false;
for(size_t i=0; i<imus.size(); ++i)
{
SensorData dataIMU(imus[i].second, 0, imus[i].first);
if((alwaysCheckImuTf_ && !imuWarnShown) || imuLocalTransform_.isNull())
{
if(this->frameId().compare(imus[i].second->header.frame_id) != 0)
{
// We should not have to wait for IMU TF (imu delay <<< sensor data delay), so don't
rtabmap::Transform localTransform = rtabmap_conversions::getTransform(this->frameId(), imus[i].second->header.frame_id, imus[i].second->header.stamp, *tfBuffer_, 0);
if(localTransform.isNull())
{
if(imuLocalTransform_.isNull()) {
RCLCPP_ERROR(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF is not available at IMU msg time %f. All IMU msgs up to sensor data time %f are skipped! If IMU TF is not static, make sure to publish it before the the imu topic is published.",
imus[i].second->header.frame_id.c_str(), this->frameId().c_str(), rtabmap_conversions::timestampFromROS(imus[i].second->header.stamp), data.stamp());
break;
} else if(!imuWarnShown) {
imuWarnShown = true; // show only one time
RCLCPP_WARN(this->get_logger(), "Could not transform IMU msg from frame \"%s\" to frame \"%s\", TF is not available at IMU msg time %f. We will use latest known IMU local transform (if TF between camera/lidar and the IMU is static, you can safely ignore this warning and set always_check_imu_tf to false).",
imus[i].second->header.frame_id.c_str(), this->frameId().c_str(), rtabmap_conversions::timestampFromROS(imus[i].second->header.stamp));
}
}
else {
imuLocalTransform_ = localTransform;
}
}
else if(imuLocalTransform_.isNull())
{
imuLocalTransform_.setIdentity();
}
}

IMU imu(cv::Vec4d(imus[i].second->orientation.x, imus[i].second->orientation.y, imus[i].second->orientation.z, imus[i].second->orientation.w),
cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->orientation_covariance.data()).clone(),
cv::Vec3d(imus[i].second->angular_velocity.x, imus[i].second->angular_velocity.y, imus[i].second->angular_velocity.z),
cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->angular_velocity_covariance.data()).clone(),
cv::Vec3d(imus[i].second->linear_acceleration.x, imus[i].second->linear_acceleration.y, imus[i].second->linear_acceleration.z),
cv::Mat(3,3,CV_64FC1,(void*)imus[i].second->linear_acceleration_covariance.data()).clone(),
imuLocalTransform_);

SensorData dataIMU(imu, 0, imus[i].first);
odometry_->process(dataIMU);
imuProcessed_ = true;
}
Expand Down Expand Up @@ -1075,7 +1108,7 @@ void OdometryROS::mainLoop()
if(syncDiagnostic_.get())
{
double curentRate = 1.0/(this->now()-timeStart).seconds();
syncDiagnostic_->tick(header.stamp,
syncDiagnostic_->tickOutput(header.stamp,
maxUpdateRate_>0 ? maxUpdateRate_:
expectedUpdateRate_>0 && expectedUpdateRate_ < curentRate ? expectedUpdateRate_:
previousStamp_ == 0.0 || rtabmap_conversions::timestampFromROS(header.stamp) - previousStamp_ > 1.0/curentRate?0:curentRate);
Expand Down Expand Up @@ -1117,6 +1150,7 @@ void OdometryROS::reset(const Transform & pose)
imuMutex_.lock();
imus_.clear();
imuMutex_.unlock();
imuLocalTransform_.setNull();
this->flushCallbacks();
}

Expand Down Expand Up @@ -1213,6 +1247,14 @@ void OdometryROS::OdomStatusTask::run(diagnostic_updater::DiagnosticStatusWrappe
}
}

void OdometryROS::tick(const rclcpp::Time & stamp)
{
if(syncDiagnostic_.get())
{
syncDiagnostic_->tickInput(stamp);
}
}

}

#include "rclcpp_components/register_node_macro.hpp"
Expand Down
6 changes: 6 additions & 0 deletions rtabmap_odom/src/nodelets/icp_odometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ void ICPOdometry::callbackScan(const sensor_msgs::msg::LaserScan::SharedPtr scan
scan_sub_.reset();
return;
}

tick(scanMsg->header.stamp);

scanReceived_ = true;
if(this->isPaused())
{
Expand Down Expand Up @@ -523,6 +526,9 @@ void ICPOdometry::callbackCloud(const sensor_msgs::msg::PointCloud2::SharedPtr p
cloud_sub_.reset();
return;
}

tick(pointCloudMsg->header.stamp);

cloudReceived_ = true;
if(this->isPaused())
{
Expand Down
18 changes: 17 additions & 1 deletion rtabmap_odom/src/nodelets/rgbd_odometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ RGBDOdometry::RGBDOdometry(const rclcpp::NodeOptions & options) :
approxSync6_(0),
exactSync6_(0),
topicQueueSize_(10),
syncQueueSize_(2),
syncQueueSize_(5),
keepColor_(false)
{
OdometryROS::init(false, true, false);
Expand Down Expand Up @@ -569,6 +569,8 @@ void RGBDOdometry::callback(
const sensor_msgs::msg::Image::ConstSharedPtr depth,
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfo)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(1);
Expand Down Expand Up @@ -597,6 +599,8 @@ void RGBDOdometry::callback(
void RGBDOdometry::callbackRGBDX(
const rtabmap_msgs::msg::RGBDImages::ConstSharedPtr images)
{
tick(images->header.stamp);

if(!this->isPaused())
{
if(images->rgbd_images.empty())
Expand All @@ -620,6 +624,8 @@ void RGBDOdometry::callbackRGBDX(
void RGBDOdometry::callbackRGBD(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(1);
Expand All @@ -636,6 +642,8 @@ void RGBDOdometry::callbackRGBD2(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(2);
Expand All @@ -655,6 +663,8 @@ void RGBDOdometry::callbackRGBD3(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image2,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(3);
Expand All @@ -677,6 +687,8 @@ void RGBDOdometry::callbackRGBD4(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image3,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(4);
Expand All @@ -702,6 +714,8 @@ void RGBDOdometry::callbackRGBD5(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image4,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(5);
Expand Down Expand Up @@ -730,6 +744,8 @@ void RGBDOdometry::callbackRGBD6(
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image5,
const rtabmap_msgs::msg::RGBDImage::ConstSharedPtr image6)
{
tick(image->header.stamp);

if(!this->isPaused())
{
std::vector<cv_bridge::CvImageConstPtr> imageMsgs(6);
Expand Down
Loading

0 comments on commit b888fe3

Please sign in to comment.