rgbd_image_buffer
image_buffer.cpp
Go to the documentation of this file.
2 
4 
5 #include <geometry_msgs/TransformStamped.h>
6 
7 #include <rgbd/client.h>
8 #include <rgbd/image.h>
9 
10 #include <ros/rate.h>
11 #include <ros/time.h>
12 
13 #include <tf2_ros/transform_listener.h>
14 
15 namespace rgbd
16 {
17 
18 // ----------------------------------------------------------------------------------------------------
19 
20 ImageBuffer::ImageBuffer() : rgbd_client_(nullptr), tf_buffer_(), tf_listener_(nullptr), shutdown_(false)
21 {
22 }
23 
24 // ----------------------------------------------------------------------------------------------------
25 
27 {
28  shutdown_ = true;
30  worker_thread_ptr_->join();
31 }
32 
33 // ----------------------------------------------------------------------------------------------------
34 
35 void ImageBuffer::initialize(const std::string& topic, const std::string& root_frame, const float worker_thread_frequency)
36 {
37  root_frame_ = root_frame;
38 
39  if (!rgbd_client_)
40  rgbd_client_ = std::make_unique<rgbd::Client>();
41 
42  rgbd_client_->initialize(topic);
43 
44  if (!tf_listener_)
45  tf_listener_ = std::make_unique<tf2_ros::TransformListener>(tf_buffer_);
46 
47  worker_thread_ptr_ = std::make_unique<std::thread>(&ImageBuffer::workerThreadFunc, this, worker_thread_frequency);
48 }
49 
50 // ----------------------------------------------------------------------------------------------------
51 
52 bool ImageBuffer::waitForRecentImage(rgbd::ImageConstPtr& image, geo::Pose3D& sensor_pose, double timeout_sec, double check_rate)
53 {
54  if (!rgbd_client_)
55  {
56  ROS_ERROR_NAMED("image_buffer", "[IMAGE_BUFFER] No RGBD client");
57  return false;
58  }
59 
60  // - - - - - - - - - - - - - - - - - -
61  // Wait until we get a new image
62  ros::Time t_start = ros::Time::now();
63  ros::Time t_end = t_start + ros::Duration(timeout_sec);
64  if (check_rate <= 0)
65  {
66  ROS_DEBUG_STREAM_NAMED("iamge_buffer", "[IMAGE_BUFFER](waitForRecentImage) defaulting to 10Hz instead of '" << check_rate << "'");
67  check_rate = 10.;
68  }
69  ros::Rate r(check_rate);
70 
71  rgbd::ImageConstPtr rgbd_image;
72  do
73  {
74  rgbd_image = rgbd_client_->nextImage();
75 
76  if (rgbd_image)
77  {
78  break;
79  }
80  else if (ros::Time::now() > t_end)
81  {
82  ROS_ERROR_NAMED("image_buffer", "[IMAGE_BUFFER] timeout waiting for rgbd image");
83  return false;
84  }
85  else
86  r.sleep();
87  }
88  while(ros::ok()); // Give it minimal one go
89 
90 
91  // - - - - - - - - - - - - - - - - - -
92  // Wait until we have a tf
93  if (!tf_buffer_.canTransform(root_frame_, rgbd_image->getFrameId(), ros::Time(rgbd_image->getTimestamp()))) // Get the TF when it is available now
94  if (!tf_buffer_.canTransform(root_frame_, rgbd_image->getFrameId(), ros::Time(rgbd_image->getTimestamp()), t_end - ros::Time::now()))
95  {
96  ROS_ERROR_THROTTLE_NAMED(5, "image_buffer", "[IMAGE_BUFFER] timeout waiting for tf");
97  return false;
98  }
99 
100  // - - - - - - - - - - - - - - - - - -
101  // Calculate tf
102 
103  try
104  {
105  geometry_msgs::TransformStamped t_sensor_pose = tf_buffer_.lookupTransform(root_frame_, rgbd_image->getFrameId(), ros::Time(rgbd_image->getTimestamp()));
106  geo::convert(t_sensor_pose.transform, sensor_pose);
107  }
108  catch(tf2::TransformException& ex)
109  {
110  ROS_ERROR_NAMED("image_buffer", "[IMAGE_BUFFER] Could not get sensor pose: %s", ex.what());
111  return false;
112  }
113 
114  // Convert from ROS coordinate frame to geolib coordinate frame
115  sensor_pose.R = sensor_pose.R * geo::Matrix3(1, 0, 0, 0, -1, 0, 0, 0, -1);
116 
117  image = rgbd_image;
118 
119  return true;
120 }
121 
122 // ----------------------------------------------------------------------------------------------------
123 
124 bool ImageBuffer::waitForRecentImage(rgbd::ImageConstPtr& image, geo::Pose3D& sensor_pose, double timeout_sec, uint timeout_tries)
125 {
126  if (timeout_tries <= 0)
127  {
128  ROS_DEBUG_STREAM_NAMED("iamge_buffer", "[IMAGE_BUFFER](waitForRecentImage) defaulting to 25 tries instead of '" << timeout_tries << "'");
129  timeout_tries = 25;
130  }
131  double freq = timeout_sec > 0 ? timeout_tries/timeout_sec : 1000; // In case of no timeout, there will be no sleeping, so arbitrary number
132 
133  return waitForRecentImage(image, sensor_pose, timeout_sec, freq);
134 }
135 
136 // ----------------------------------------------------------------------------------------------------
137 
139 {
141  if(!recent_image_.first)
142  {
143  ROS_DEBUG("[IMAGE_BUFFER] No new image");
144  return false;
145  }
146 
147  image = recent_image_.first;
148  sensor_pose = recent_image_.second;
149 
150  recent_image_.first.reset(); // Invalidate the most recent image
151 
152  return true;
153 }
154 
155 // ----------------------------------------------------------------------------------------------------
156 
158 {
159  if (!rgbd_client_)
160  {
161  ROS_ERROR_NAMED("image_buffer", "[IMAGE_BUFFER] No RGBD client");
162  return false;
163  }
164 
165  // - - - - - - - - - - - - - - - - - -
166  // Fetch kinect image and place in image buffer
167 
168  {
169  rgbd::ImageConstPtr new_image = rgbd_client_->nextImage();
170  if (new_image)
171  {
172  image_buffer_.push_front(new_image);
173  ROS_DEBUG_STREAM_NAMED("image_buffer", "[IMAGE_BUFFER] New image from the RGBD client with timestamp: " << std::fixed << std::setprecision(12) << new_image->getTimestamp());
174  }
175  else
176  {
177  ROS_DEBUG_NAMED("image_buffer", "[IMAGE_BUFFER] No new image from the RGBD client");
178  }
179  }
180 
181  geo::Pose3D sensor_pose;
182 
184  {
185  rgbd::ImageConstPtr& rgbd_image = *it;
186  try
187  {
188  geometry_msgs::TransformStamped t_sensor_pose = tf_buffer_.lookupTransform(root_frame_, rgbd_image->getFrameId(), ros::Time(rgbd_image->getTimestamp()));
189  geo::convert(t_sensor_pose.transform, sensor_pose);
190  }
191  catch (tf2::ExtrapolationException& ex)
192  {
193  try
194  {
195  // Now we have to check if the error was an interpolation or extrapolation error (i.e., the image is too old or
196  // to new, respectively). If it is too old, discard it.
197  geometry_msgs::TransformStamped latest_sensor_pose = tf_buffer_.lookupTransform(root_frame_, rgbd_image->getFrameId(), ros::Time(0));
198  // If image time stamp is older than latest transform, the image is too old and the tf data is not available anymore
199  if ( latest_sensor_pose.header.stamp > ros::Time(rgbd_image->getTimestamp()) )
200  {
201 
202  ROS_DEBUG_STREAM_NAMED("image_buffer", "[IMAGE_BUFFER] Image too old to look-up tf. Deleting all images older than timestamp: " << std::fixed
203  << ros::Time(rgbd_image->getTimestamp()));
204  // Deleting this image and all older images
205  image_buffer_.erase_after(it, image_buffer_.end());
206  return false;
207  }
208  else
209  {
210  // Image is too new; continue to next image, which is older
211  ROS_DEBUG_STREAM_DELAYED_THROTTLE_NAMED(10, "image_buffer", "[IMAGE_BUFFER] Image too new to look-up tf: image timestamp: " << std::fixed << ros::Time(rgbd_image->getTimestamp()) << ", what: " << ex.what());
212  continue;
213  }
214  }
215  catch (tf2::TransformException& ex)
216  {
217  ROS_ERROR_DELAYED_THROTTLE_NAMED(10, "image_buffer", "[IMAGE_BUFFER] Could not get latest sensor pose (probably because tf is still initializing): %s", ex.what());
218  ROS_DEBUG_NAMED("image_buffer", "[IMAGE_BUFFER] Could not get latest sensor pose (probably because tf is still initializing): %s", ex.what());
219  continue;
220  }
221  }
222  catch (tf2::TransformException& ex)
223  {
224  ROS_ERROR_DELAYED_THROTTLE_NAMED(10, "image_buffer", "[IMAGE_BUFFER] Could not get sensor pose: %s", ex.what());
225  ROS_WARN_NAMED("image_buffer", "[IMAGE_BUFFER] Could not get sensor pose: %s", ex.what());
226  continue;
227  }
228 
229  // Convert from ROS coordinate frame to geolib coordinate frame
230  sensor_pose.R = sensor_pose.R * geo::Matrix3(1, 0, 0, 0, -1, 0, 0, 0, -1);
231 
232  {
234  recent_image_.first = rgbd_image;
235  recent_image_.second = sensor_pose;
236  }
237 
238  // Deleting this image and all older images
239  image_buffer_.erase_after(it, image_buffer_.end());
240 
241  return true;
242  }
243 
244  // Not been able to update the most recent image/TF combo
245  return false;
246 }
247 
248 // ----------------------------------------------------------------------------------------------------
249 
250 void ImageBuffer::workerThreadFunc(const float frequency)
251 {
252  ros::Rate r(frequency);
253  while(!shutdown_)
254  {
255  if (!getMostRecentImageTF())
256  ROS_ERROR_DELAYED_THROTTLE_NAMED(5, "image_buffer", "[IMAGE_BUFFER] Could not get a pose for any image in the buffer");
257  r.sleep();
258  }
259 }
260 
261 // ----------------------------------------------------------------------------------------------------
262 
263 } // namespace rgbd
std::setprecision
T setprecision(T... args)
rgbd::ImageBuffer::image_buffer_
std::forward_list< rgbd::ImageConstPtr > image_buffer_
Newer images should be pushed on the front. This will result in the front being the most recent and t...
Definition: image_buffer.h:90
rgbd::ImageBuffer::recent_image_mutex_
std::mutex recent_image_mutex_
For protecting ImageBuffer::recent_image_.
Definition: image_buffer.h:98
std::string
std::shared_ptr
rgbd::ImageBuffer::root_frame_
std::string root_frame_
Definition: image_buffer.h:80
std::lock_guard
client.h
geo::Transform3T
rgbd::ImageBuffer::shutdown_
bool shutdown_
Definition: image_buffer.h:100
rgbd::ImageBuffer::recent_image_
std::pair< rgbd::ImageConstPtr, geo::Pose3D > recent_image_
Definition: image_buffer.h:94
rgbd::ImageBuffer::tf_buffer_
tf2_ros::Buffer tf_buffer_
Definition: image_buffer.h:84
rgbd::ImageBuffer::getMostRecentImageTF
bool getMostRecentImageTF()
Iterates over the buffer and tries to get TF for the most recent image. Deletes image and all older i...
Definition: image_buffer.cpp:157
std::forward_list
rgbd::ImageBuffer::tf_listener_
std::unique_ptr< tf2_ros::TransformListener > tf_listener_
Definition: image_buffer.h:85
image.h
image_buffer.h
rgbd
rgbd::ImageBuffer::workerThreadFunc
void workerThreadFunc(const float frequency=20)
Calls ImageBuffer::getMostRecentImageTF on the specified frequency.
Definition: image_buffer.cpp:250
rgbd::ImageBuffer::rgbd_client_
std::unique_ptr< rgbd::Client > rgbd_client_
Definition: image_buffer.h:82
rgbd::ImageBuffer::ImageBuffer
ImageBuffer()
Definition: image_buffer.cpp:20
geo::convert
void convert(const geo::DepthCamera &cam_model, sensor_msgs::CameraInfo &msg)
rgbd::ImageBuffer::nextImage
bool nextImage(rgbd::ImageConstPtr &image, geo::Pose3D &sensor_pose)
Returns the most recent combination of image and transform, provided it is different from the previou...
Definition: image_buffer.cpp:138
rgbd::ImageBuffer::~ImageBuffer
~ImageBuffer()
Definition: image_buffer.cpp:26
rgbd::ImageBuffer::waitForRecentImage
bool waitForRecentImage(rgbd::ImageConstPtr &image, geo::Pose3D &sensor_pose, double timeout_sec, double check_rate)
Blocks until a new image with transform is found. Returns false if no image or TF could be found with...
Definition: image_buffer.cpp:52
geo::Transform3T::R
Mat3T< T > R
rgbd::ImageBuffer::initialize
void initialize(const std::string &topic, const std::string &root_frame="map", const float worker_thread_frequency=20)
Configure the buffer.
Definition: image_buffer.cpp:35
std::fixed
T fixed(T... args)
geo::Matrix3
Mat3 Matrix3
rgbd::ImageBuffer::worker_thread_ptr_
std::unique_ptr< std::thread > worker_thread_ptr_
Definition: image_buffer.h:99
msg_conversions.h