Swarm-SLAM  1.0.0
C-SLAM Framework
stereo_handler.cpp
Go to the documentation of this file.
2 #include <rtabmap_conversions/MsgConversion.h>
3 
4 using namespace rtabmap;
5 using namespace cslam;
6 
7 StereoHandler::StereoHandler(std::shared_ptr<rclcpp::Node> &node)
8  : RGBDHandler(node) {
9  node->declare_parameter<std::string>("frontend.left_image_topic", "left/image_rect");
10  node->declare_parameter<std::string>("frontend.right_image_topic", "right/image_rect");
11  node->declare_parameter<std::string>("frontend.left_camera_info_topic",
12  "left/camera_info");
13  node->declare_parameter<std::string>("frontend.right_camera_info_topic",
14  "right/camera_info");
15 
16  // Subscriber for stereo images
17  sub_image_rect_left_.subscribe(
18  node_.get(), node_->get_parameter("frontend.left_image_topic").as_string(), "raw",
19  rclcpp::QoS(max_queue_size_)
20  .reliability((rmw_qos_reliability_policy_t)2)
21  .get_rmw_qos_profile());
22  sub_image_rect_right_.subscribe(
23  node_.get(), node_->get_parameter("frontend.right_image_topic").as_string(), "raw",
24  rclcpp::QoS(max_queue_size_)
25  .reliability((rmw_qos_reliability_policy_t)2)
26  .get_rmw_qos_profile());
27  sub_camera_info_left_.subscribe(
28  node_.get(), node_->get_parameter("frontend.left_camera_info_topic").as_string(),
29  rclcpp::QoS(max_queue_size_)
30  .reliability((rmw_qos_reliability_policy_t)2)
31  .get_rmw_qos_profile());
32  sub_camera_info_right_.subscribe(
33  node_.get(), node_->get_parameter("frontend.right_camera_info_topic").as_string(),
34  rclcpp::QoS(max_queue_size_)
35  .reliability((rmw_qos_reliability_policy_t)2)
36  .get_rmw_qos_profile());
37 
38  stereo_sync_policy_ = new message_filters::Synchronizer<StereoSyncPolicy>(
39  StereoSyncPolicy(max_queue_size_), sub_image_rect_left_, sub_image_rect_right_,
40  sub_camera_info_left_, sub_camera_info_right_, sub_odometry_);
41  stereo_sync_policy_->registerCallback(
42  std::bind(&StereoHandler::stereo_callback, this, std::placeholders::_1,
43  std::placeholders::_2, std::placeholders::_3,
44  std::placeholders::_4, std::placeholders::_5));
45 
46 }
47 
49  const sensor_msgs::msg::Image::ConstSharedPtr image_rect_left,
50  const sensor_msgs::msg::Image::ConstSharedPtr image_rect_right,
51  const sensor_msgs::msg::CameraInfo::ConstSharedPtr camera_info_left,
52  const sensor_msgs::msg::CameraInfo::ConstSharedPtr camera_info_right,
53  const nav_msgs::msg::Odometry::ConstSharedPtr odom_ptr) {
54  // If odom tracking failed, do not process the frame
55  if (odom_ptr->pose.covariance[0] > 1000)
56  {
57  RCLCPP_WARN(node_->get_logger(), "Odom tracking failed, skipping frame");
58  return;
59  }
60  // Fix timestamps for logging
61  auto odom = std::make_shared<nav_msgs::msg::Odometry>(*odom_ptr);
62  odom->header.stamp = image_rect_left->header.stamp;
63 
64  if (!(image_rect_left->encoding.compare(
65  sensor_msgs::image_encodings::TYPE_8UC1) == 0 ||
66  image_rect_left->encoding.compare(sensor_msgs::image_encodings::MONO8) ==
67  0 ||
68  image_rect_left->encoding.compare(sensor_msgs::image_encodings::MONO16) ==
69  0 ||
70  image_rect_left->encoding.compare(sensor_msgs::image_encodings::BGR8) ==
71  0 ||
72  image_rect_left->encoding.compare(sensor_msgs::image_encodings::RGB8) ==
73  0 ||
74  image_rect_left->encoding.compare(sensor_msgs::image_encodings::BGRA8) ==
75  0 ||
76  image_rect_left->encoding.compare(sensor_msgs::image_encodings::RGBA8) ==
77  0) ||
78  !(image_rect_right->encoding.compare(
79  sensor_msgs::image_encodings::TYPE_8UC1) == 0 ||
80  image_rect_right->encoding.compare(sensor_msgs::image_encodings::MONO8) ==
81  0 ||
82  image_rect_right->encoding.compare(
83  sensor_msgs::image_encodings::MONO16) == 0 ||
84  image_rect_right->encoding.compare(sensor_msgs::image_encodings::BGR8) ==
85  0 ||
86  image_rect_right->encoding.compare(sensor_msgs::image_encodings::RGB8) ==
87  0 ||
88  image_rect_right->encoding.compare(sensor_msgs::image_encodings::BGRA8) ==
89  0 ||
90  image_rect_right->encoding.compare(sensor_msgs::image_encodings::RGBA8) ==
91  0)) {
92  RCLCPP_ERROR(
93  node_->get_logger(),
94  "Input type must be image=mono8,mono16,rgb8,bgr8,rgba8,bgra8 (mono8 "
95  "recommended), received types are %s (left) and %s (right)",
96  image_rect_left->encoding.c_str(), image_rect_right->encoding.c_str());
97  return;
98  }
99 
100  rclcpp::Time stamp =
101  rtabmap_conversions::timestampFromROS(image_rect_left->header.stamp) >
102  rtabmap_conversions::timestampFromROS(image_rect_right->header.stamp)
103  ? image_rect_left->header.stamp
104  : image_rect_right->header.stamp;
105 
106  Transform localTransform(0,0,0,0,0,0);
107  if (base_frame_id_ != "")
108  {
109  localTransform = rtabmap_conversions::getTransform(
110  base_frame_id_, image_rect_left->header.frame_id, stamp, *tf_buffer_, 0.1);
111  if (localTransform.isNull()) {
112  RCLCPP_INFO(node_->get_logger(),
113  "Could not get transform from %s to %s after 0.1 s!",
114  base_frame_id_.c_str(), image_rect_left->header.frame_id.c_str());
115  return;
116  }
117  }
118 
119  if (image_rect_left->data.size() && image_rect_right->data.size()) {
120  bool alreadyRectified = true;
121  rtabmap::Transform stereoTransform;
122  if (!alreadyRectified) {
123  stereoTransform = rtabmap_conversions::getTransform(
124  camera_info_right->header.frame_id, camera_info_left->header.frame_id,
125  camera_info_left->header.stamp, *tf_buffer_, 0.1);
126  if (stereoTransform.isNull()) {
127  RCLCPP_ERROR(node_->get_logger(),
128  "Parameter %s is false but we cannot get TF between the "
129  "two cameras! (between frames %s and %s)",
130  Parameters::kRtabmapImagesAlreadyRectified().c_str(),
131  camera_info_right->header.frame_id.c_str(),
132  camera_info_left->header.frame_id.c_str());
133  return;
134  } else if (stereoTransform.isIdentity()) {
135  RCLCPP_ERROR(node_->get_logger(),
136  "Parameter %s is false but we cannot get a valid TF "
137  "between the two cameras! "
138  "Identity transform returned between left and right "
139  "cameras. Verify that if TF between "
140  "the cameras is valid: \"rosrun tf tf_echo %s %s\".",
141  Parameters::kRtabmapImagesAlreadyRectified().c_str(),
142  camera_info_right->header.frame_id.c_str(),
143  camera_info_left->header.frame_id.c_str());
144  return;
145  }
146  }
147 
148  rtabmap::StereoCameraModel stereoModel =
149  rtabmap_conversions::stereoCameraModelFromROS(*camera_info_left, *camera_info_right,
150  localTransform, stereoTransform);
151 
152  if (stereoModel.baseline() == 0 && alreadyRectified) {
153  stereoTransform = rtabmap_conversions::getTransform(
154  camera_info_left->header.frame_id, camera_info_right->header.frame_id,
155  camera_info_left->header.stamp, *tf_buffer_, 0.1);
156 
157  if (!stereoTransform.isNull() && stereoTransform.x() > 0) {
158  static bool warned = false;
159  if (!warned) {
160  RCLCPP_WARN(
161  node_->get_logger(),
162  "Right camera info doesn't have Tx set but we are assuming that "
163  "stereo images are already rectified (see %s parameter). While "
164  "not "
165  "recommended, we used TF to get the baseline (%s->%s = %fm) for "
166  "convenience (e.g., D400 ir stereo issue). It is preferred to "
167  "feed "
168  "a valid right camera info if stereo images are already "
169  "rectified. This message is only printed once...",
170  rtabmap::Parameters::kRtabmapImagesAlreadyRectified().c_str(),
171  camera_info_right->header.frame_id.c_str(),
172  camera_info_left->header.frame_id.c_str(), stereoTransform.x());
173  warned = true;
174  }
175  stereoModel = rtabmap::StereoCameraModel(
176  stereoModel.left().fx(), stereoModel.left().fy(),
177  stereoModel.left().cx(), stereoModel.left().cy(),
178  stereoTransform.x(), stereoModel.localTransform(),
179  stereoModel.left().imageSize());
180  }
181  }
182 
183  if (alreadyRectified && stereoModel.baseline() <= 0) {
184  RCLCPP_ERROR(
185  node_->get_logger(),
186  "The stereo baseline (%f) should be positive (baseline=-Tx/fx). We "
187  "assume a horizontal left/right stereo "
188  "setup where the Tx (or P(0,3)) is negative in the right camera info "
189  "msg.",
190  stereoModel.baseline());
191  return;
192  }
193 
194  if (stereoModel.baseline() > 10.0) {
195  static bool shown = false;
196  if (!shown) {
197  RCLCPP_WARN(
198  node_->get_logger(),
199  "Detected baseline (%f m) is quite large! Is your "
200  "right camera_info P(0,3) correctly set? Note that "
201  "baseline=-P(0,3)/P(0,0). This warning is printed only once.",
202  stereoModel.baseline());
203  shown = true;
204  }
205  }
206 
207  cv_bridge::CvImagePtr ptrImageLeft = cv_bridge::toCvCopy(
208  image_rect_left, image_rect_left->encoding.compare(
209  sensor_msgs::image_encodings::TYPE_8UC1) == 0 ||
210  image_rect_left->encoding.compare(
211  sensor_msgs::image_encodings::MONO8) == 0
212  ? ""
213  : image_rect_left->encoding.compare(
214  sensor_msgs::image_encodings::MONO16) != 0
215  ? "bgr8"
216  : "mono8");
217  cv_bridge::CvImagePtr ptrImageRight = cv_bridge::toCvCopy(
218  image_rect_right, image_rect_right->encoding.compare(
219  sensor_msgs::image_encodings::TYPE_8UC1) == 0 ||
220  image_rect_right->encoding.compare(
221  sensor_msgs::image_encodings::MONO8) == 0
222  ? ""
223  : "mono8");
224 
225  auto data = std::make_shared<rtabmap::SensorData>(
226  ptrImageLeft->image, ptrImageRight->image, stereoModel,
227  0, rtabmap_conversions::timestampFromROS(stamp));
228 
229  received_data_queue_.push_back(std::make_pair(data, odom));
230  if (received_data_queue_.size() > max_queue_size_) {
231  // Remove the oldest keyframes if we exceed the maximum size
232  received_data_queue_.pop_front();
233  RCLCPP_WARN(
234  node_->get_logger(),
235  "Stereo: Maximum queue size (%d) exceeded, the oldest element was removed.",
237  }
238 
239  if (enable_gps_recording_) {
242  {
243  received_gps_queue_.pop_front();
244  }
245  }
246  } else {
247  RCLCPP_WARN(node_->get_logger(), "Odom: input images empty?!");
248  }
249 }
250 
252  const std::shared_ptr<
253  cslam_common_interfaces::msg::LocalImageDescriptors>
254  msg,
255  rtabmap::SensorData &sensor_data) {
256  // Fill descriptors
257  rtabmap::StereoCameraModel stereo_model =
258  rtabmap_conversions::stereoCameraModelFromROS(msg->data.rgb_camera_info,
259  msg->data.depth_camera_info,
260  rtabmap::Transform::getIdentity());
261  sensor_data = rtabmap::SensorData(
262  cv::Mat(), cv::Mat(), stereo_model, 0,
263  rtabmap_conversions::timestampFromROS(msg->data.header.stamp));
264 
265  std::vector<cv::KeyPoint> kpts;
266  rtabmap_conversions::keypointsFromROS(msg->data.key_points, kpts);
267  std::vector<cv::Point3f> kpts3D;
268  rtabmap_conversions::points3fFromROS(msg->data.points, kpts3D);
269  auto descriptors = rtabmap::uncompressData(msg->data.descriptors);
270  sensor_data.setFeatures(kpts, kpts3D, descriptors);
271 }
272 
273 void StereoHandler::send_visualization(const std::pair<std::shared_ptr<rtabmap::SensorData>, std::shared_ptr<const nav_msgs::msg::Odometry>> &keypoints_data)
274 {
275  send_visualization_keypoints(keypoints_data);
276 }
std::shared_ptr< rclcpp::Node > node_
Definition: rgbd_handler.h:223
void send_visualization_keypoints(const std::pair< std::shared_ptr< rtabmap::SensorData >, std::shared_ptr< const nav_msgs::msg::Odometry >> &keypoints_data)
Send keypoints for visualizations.
unsigned int max_queue_size_
Definition: rgbd_handler.h:225
sensor_msgs::msg::NavSatFix latest_gps_fix_
Definition: rgbd_handler.h:287
message_filters::Subscriber< nav_msgs::msg::Odometry > sub_odometry_
Definition: rgbd_handler.h:228
std::string base_frame_id_
Definition: rgbd_handler.h:276
std::deque< std::pair< std::shared_ptr< rtabmap::SensorData >, nav_msgs::msg::Odometry::ConstSharedPtr > > received_data_queue_
Definition: rgbd_handler.h:217
std::deque< sensor_msgs::msg::NavSatFix > received_gps_queue_
Definition: rgbd_handler.h:289
std::shared_ptr< tf2_ros::Buffer > tf_buffer_
Definition: rgbd_handler.h:273
void stereo_callback(const sensor_msgs::msg::Image::ConstSharedPtr image_rect_left, const sensor_msgs::msg::Image::ConstSharedPtr image_rect_right, const sensor_msgs::msg::CameraInfo::ConstSharedPtr camera_info_left, const sensor_msgs::msg::CameraInfo::ConstSharedPtr camera_info_right, const nav_msgs::msg::Odometry::ConstSharedPtr odom)
Callback receiving sync data from camera.
virtual void local_descriptors_msg_to_sensor_data(const std::shared_ptr< cslam_common_interfaces::msg::LocalImageDescriptors > msg, rtabmap::SensorData &sensor_data)
converts descriptors to sensore data
virtual void send_visualization(const std::pair< std::shared_ptr< rtabmap::SensorData >, std::shared_ptr< const nav_msgs::msg::Odometry >> &keypoints_data)
Send keypoints for visualizations.
Definition: __init__.py:1