plane_finder.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2018 Michael Ferguson
3  * Copyright (C) 2015-2017 Fetch Robotics Inc.
4  * Copyright (C) 2013-2014 Unbounded Robotics Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 // Author: Niharika Arora, Michael Ferguson
20 
21 #include <math.h>
26 
28 
29 namespace robot_calibration
30 {
31 
32 const unsigned X = 0;
33 const unsigned Y = 1;
34 const unsigned Z = 2;
35 
36 PlaneFinder::PlaneFinder() :
37  tfListener_(tfBuffer_), waiting_(false)
38 {
39 }
40 
41 bool PlaneFinder::init(const std::string& name,
42  ros::NodeHandle & nh)
43 {
44  if (!FeatureFinder::init(name, nh))
45  return false;
46 
47  // We subscribe to a PointCloud2
48  std::string topic_name;
49  nh.param<std::string>("topic", topic_name, "/points");
50  subscriber_ = nh.subscribe(topic_name,
51  1,
53  this);
54 
55  // Name of the sensor model that will be used during optimization
56  nh.param<std::string>("camera_sensor_name", camera_sensor_name_, "camera");
57 
58  // Maximum number of valid points to include in the observation
59  nh.param<double>("points_max", points_max_, 60);
60 
61  // Frame to transform point cloud into before applying limits below
62  // if specified as "none", cloud will be processed in sensor frame
63  nh.param<std::string>("transform_frame", transform_frame_, "base_link");
64 
65  // Valid points must lie within this box, in the transform_frame
66  nh.param<double>("min_x", min_x_, -2.0);
67  nh.param<double>("max_x", max_x_, 2.0);
68  nh.param<double>("min_y", min_y_, -2.0);
69  nh.param<double>("max_y", max_y_, 2.0);
70  nh.param<double>("min_z", min_z_, 0.0);
71  nh.param<double>("max_z", max_z_, 2.0);
72 
73  // Should we include debug image/cloud in observations
74  nh.param<bool>("debug", output_debug_, false);
75 
76  // Publish the observation as a PointCloud2
77  publisher_ = nh.advertise<sensor_msgs::PointCloud2>(getName() + "_points", 10);
78 
79  // Make sure we have CameraInfo before starting
80  if (!depth_camera_manager_.init(nh))
81  {
82  // Error will have been printed by manager
83  return false;
84  }
85 
86  return true;
87 }
88 
89 void PlaneFinder::cameraCallback(const sensor_msgs::PointCloud2& cloud)
90 {
91  if (waiting_)
92  {
93  cloud_ = cloud;
94  waiting_ = false;
95  }
96 }
97 
99 {
100  ros::Duration(1/10.0).sleep();
101 
102  waiting_ = true;
103  int count = 250;
104  while (--count)
105  {
106  if (!waiting_)
107  {
108  // success
109  return true;
110  }
111  ros::Duration(0.01).sleep();
112  ros::spinOnce();
113  }
114  ROS_ERROR("Failed to get cloud");
115  return !waiting_;
116 }
117 
118 bool PlaneFinder::find(robot_calibration_msgs::CalibrationData * msg)
119 {
120  if (!waitForCloud())
121  {
122  ROS_ERROR("No point cloud data");
123  return false;
124  }
125 
126  // Remove any point that is invalid or not with our tolerance
127  size_t num_points = cloud_.width * cloud_.height;
130 
131  bool do_transform = transform_frame_ != "none"; // This can go away once the cloud gets transformed outside loop
132  size_t j = 0;
133  for (size_t i = 0; i < num_points; i++)
134  {
135  geometry_msgs::PointStamped p;
136  p.point.x = (xyz + i)[X];
137  p.point.y = (xyz + i)[Y];
138  p.point.z = (xyz + i)[Z];
139 
140  // Remove the NaNs in the point cloud
141  if (!std::isfinite(p.point.x) || !std::isfinite(p.point.y) || !std::isfinite(p.point.z))
142  continue;
143 
144  // Remove the points immediately in front of the camera in the point cloud
145  // NOTE : This is to handle sensors that publish zeros instead of NaNs in the point cloud
146  if (p.point.z == 0)
147  continue;
148 
149  // Get transform (if any)
150  geometry_msgs::PointStamped p_out;
151  if (do_transform)
152  {
153  p.header.stamp = ros::Time(0);
154  p.header.frame_id = cloud_.header.frame_id;
155  try
156  {
158  }
159  catch (tf2::TransformException& ex)
160  {
161  ROS_ERROR("%s", ex.what());
162  ros::Duration(1.0).sleep();
163  continue;
164  }
165  }
166  else
167  {
168  p_out = p;
169  }
170 
171  // Test the transformed point
172  if (p_out.point.x < min_x_ || p_out.point.x > max_x_ || p_out.point.y < min_y_ || p_out.point.y > max_y_ ||
173  p_out.point.z < min_z_ || p_out.point.z > max_z_)
174  {
175  continue;
176  }
177 
178  // This is a valid point, move it forward
179  (cloud_iter + j)[X] = (xyz + i)[X];
180  (cloud_iter + j)[Y] = (xyz + i)[Y];
181  (cloud_iter + j)[Z] = (xyz + i)[Z];
182  j++;
183  }
184  cloud_.height = 1;
185  cloud_.width = j;
186  cloud_.data.resize(cloud_.width * cloud_.point_step);
187 
188  // Determine number of points to output
189  size_t points_total = std::min(static_cast<size_t>(points_max_), j);
190  ROS_INFO_STREAM("Got " << j << " points from plane, using " << points_total);
191 
192  // Create PointCloud2 to publish
193  sensor_msgs::PointCloud2 viz_cloud;
194  viz_cloud.width = 0;
195  viz_cloud.height = 0;
196  viz_cloud.header.stamp = ros::Time::now();
197  viz_cloud.header.frame_id = cloud_.header.frame_id;
198  sensor_msgs::PointCloud2Modifier cloud_mod(viz_cloud);
199  cloud_mod.setPointCloud2FieldsByString(1, "xyz");
200  cloud_mod.resize(points_total);
201  sensor_msgs::PointCloud2Iterator<float> viz_cloud_iter(viz_cloud, "x");
202 
203  // Setup observation
204  int idx_cam = msg->observations.size();
205  msg->observations.resize(msg->observations.size() + 1);
206  msg->observations[idx_cam].sensor_name = camera_sensor_name_;
207  msg->observations[idx_cam].ext_camera_info = depth_camera_manager_.getDepthCameraInfo();
208 
209  // Fill in observation
210  size_t step = cloud_.width / points_total;
211  size_t index = step / 2;
212  for (size_t i = 0; index < cloud_.width; i++)
213  {
214  // Get (untransformed) 3d point
215  geometry_msgs::PointStamped rgbd;
216  rgbd.point.x = (xyz + index)[X];
217  rgbd.point.y = (xyz + index)[Y];
218  rgbd.point.z = (xyz + index)[Z];
219 
220  // Add to observation
221  msg->observations[idx_cam].features.push_back(rgbd);
222 
223  // Copy to cloud for publishing
224  viz_cloud_iter[0] = rgbd.point.x;
225  viz_cloud_iter[1] = rgbd.point.y;
226  viz_cloud_iter[2] = rgbd.point.z;
227  ++viz_cloud_iter;
228 
229  // Next point
230  index += step;
231  }
232 
233  // Add debug cloud to message
234  if (output_debug_)
235  {
236  msg->observations[idx_cam].cloud = cloud_;
237  }
238 
239  // Publish debug info
240  publisher_.publish(viz_cloud);
241 
242  // Report success
243  return true;
244 }
245 
246 } // namespace robot_calibration
void publish(const boost::shared_ptr< M > &message) const
bool init(const std::string &name, ros::NodeHandle &n)
Initialize the feature finder.
Base class for a feature finder.
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
bool sleep() const
DepthCameraInfoManager depth_camera_manager_
Definition: plane_finder.h:49
robot_calibration_msgs::ExtendedCameraInfo getDepthCameraInfo()
Definition: depth_camera.h:75
virtual bool init(const std::string &name, ros::NodeHandle &nh)
Initialize the feature finder.
bool param(const std::string &param_name, T &param_val, const T &default_val) const
sensor_msgs::PointCloud2 cloud_
Definition: plane_finder.h:48
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool find(robot_calibration_msgs::CalibrationData *msg)
Once the robot has been moved into the proper position and settled, this function will be called...
std::string getName()
Get the name of this feature finder.
Calibration code lives under this namespace.
unsigned int step
#define ROS_INFO_STREAM(args)
static Time now()
T & transform(const T &in, T &out, const std::string &target_frame, ros::Duration timeout=ros::Duration(0.0)) const
ROSCPP_DECL void spinOnce()
void setPointCloud2FieldsByString(int n_fields,...)
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define ROS_ERROR(...)
void cameraCallback(const sensor_msgs::PointCloud2 &cloud)


robot_calibration
Author(s): Michael Ferguson
autogenerated on Tue Nov 3 2020 17:30:30