checkerboard_finder.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015 Fetch Robotics Inc.
00003  * Copyright (C) 2013-2014 Unbounded Robotics Inc.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 // Author: Michael Ferguson
00019 
00020 #include <robot_calibration/capture/checkerboard_finder.h>
00021 #include <sensor_msgs/point_cloud2_iterator.h>
00022 
00023 namespace robot_calibration
00024 {
00025 
00026 // We use a number of PC2 iterators, define the indexes here
00027 const unsigned X = 0;
00028 const unsigned Y = 1;
00029 const unsigned Z = 2;
00030 
00031 CheckerboardFinder::CheckerboardFinder(ros::NodeHandle & nh) :
00032   FeatureFinder(nh),
00033   waiting_(false)
00034 {
00035   // Setup Scriber
00036   std::string topic_name;
00037   nh.param<std::string>("topic", topic_name, "/points");
00038   subscriber_ = nh.subscribe(topic_name,
00039                              1,
00040                              &CheckerboardFinder::cameraCallback,
00041                              this);
00042 
00043   // Size of checkerboard
00044   nh.param<int>("points_x", points_x_, 5);
00045   nh.param<int>("points_y", points_y_, 4);
00046   nh.param<double>("size", square_size_, 0.0245);
00047 
00048   // Should we output debug image/cloud
00049   nh.param<bool>("debug", output_debug_, false);
00050 
00051   // Get sensor names
00052   nh.param<std::string>("camera_sensor_name", camera_sensor_name_, "camera");
00053   nh.param<std::string>("chain_sensor_name", chain_sensor_name_, "arm");
00054 
00055   // Publish where checkerboard points were seen
00056   publisher_ = nh.advertise<sensor_msgs::PointCloud2>("checkerboard_points", 10);
00057 
00058   // Setup to get camera depth info
00059   if (!depth_camera_manager_.init(nh))
00060   {
00061     // Error will be printed in manager
00062     throw;
00063   }
00064 }
00065 
00066 void CheckerboardFinder::cameraCallback(const sensor_msgs::PointCloud2& cloud)
00067 {
00068   if (waiting_)
00069   {
00070     cloud_ = cloud;
00071     waiting_ = false;
00072   }
00073 }
00074 
00075 // Returns true if we got a message, false if we timeout
00076 bool CheckerboardFinder::waitForCloud()
00077 {
00078   // Initial wait cycle so that camera is definitely up to date.
00079   ros::Duration(1/10.0).sleep();
00080 
00081   waiting_ = true;
00082   int count = 250;
00083   while (--count)
00084   {
00085     if (!waiting_)
00086     {
00087       // success
00088       return true;
00089     }
00090     ros::Duration(0.01).sleep();
00091     ros::spinOnce();
00092   }
00093   ROS_ERROR("Failed to get cloud");
00094   return !waiting_;
00095 }
00096 
00097 bool CheckerboardFinder::find(robot_calibration_msgs::CalibrationData * msg)
00098 {
00099   // Try up to 50 frames
00100   for (int i = 0; i < 50; ++i)
00101   {
00102     // temporary copy of msg, so we throw away all changes if findInternal() returns false
00103     robot_calibration_msgs::CalibrationData tmp_msg(*msg);
00104     if (findInternal(&tmp_msg))
00105     {
00106       *msg = tmp_msg;
00107       return true;
00108     }
00109   }
00110   return false;
00111 }
00112 
00113 bool CheckerboardFinder::findInternal(robot_calibration_msgs::CalibrationData * msg)
00114 {
00115   geometry_msgs::PointStamped rgbd;
00116   geometry_msgs::PointStamped world;
00117 
00118   // Get cloud
00119   if (!waitForCloud())
00120   {
00121     ROS_ERROR("No point cloud data");
00122     return false;
00123   }
00124 
00125   // Get an image message from point cloud
00126   sensor_msgs::ImagePtr image_msg(new sensor_msgs::Image);
00127   sensor_msgs::PointCloud2ConstIterator<uint8_t> rgb(cloud_, "rgb");
00128   image_msg->encoding = "bgr8";
00129   image_msg->height = cloud_.height;
00130   image_msg->width = cloud_.width;
00131   image_msg->step = image_msg->width * sizeof (uint8_t) * 3;
00132   image_msg->data.resize(image_msg->step * image_msg->height);
00133   for (size_t y = 0; y < cloud_.height; y++)
00134   {
00135     for (size_t x = 0; x < cloud_.width; x++)
00136     {
00137       uint8_t* pixel = &(image_msg->data[y * image_msg->step + x * 3]);
00138       pixel[0] = rgb[0];
00139       pixel[1] = rgb[1];
00140       pixel[2] = rgb[2];
00141       ++rgb;
00142     }
00143   }
00144 
00145   // Get an OpenCV image from the cloud
00146   cv_bridge::CvImagePtr bridge;
00147   try
00148   {
00149     bridge = cv_bridge::toCvCopy(image_msg, "mono8");  // TODO: was rgb8? does this work?
00150   }
00151   catch(cv_bridge::Exception& e)
00152   {
00153     ROS_ERROR("Conversion failed");
00154     return false;
00155   }
00156 
00157   // Find checkerboard
00158   std::vector<cv::Point2f> points;
00159   points.resize(points_x_ * points_y_);
00160   cv::Size checkerboard_size(points_x_, points_y_);
00161   int found = cv::findChessboardCorners(bridge->image, checkerboard_size,
00162                                         points, CV_CALIB_CB_ADAPTIVE_THRESH);
00163 
00164   if (found)
00165   {
00166     ROS_INFO("Found the checkboard");
00167 
00168     // Create PointCloud2 to publish
00169     sensor_msgs::PointCloud2 cloud;
00170     cloud.width = 0;
00171     cloud.height = 0;
00172     cloud.header.stamp = ros::Time::now();
00173     cloud.header.frame_id = cloud_.header.frame_id;
00174     sensor_msgs::PointCloud2Modifier cloud_mod(cloud);
00175     cloud_mod.setPointCloud2FieldsByString(1, "xyz");
00176     cloud_mod.resize(points_x_ * points_y_);
00177     sensor_msgs::PointCloud2Iterator<float> iter_cloud(cloud_, "x");
00178 
00179     // Set msg size
00180     int idx_cam = msg->observations.size() + 0;
00181     int idx_chain = msg->observations.size() + 1;
00182     msg->observations.resize(msg->observations.size() + 2);
00183     msg->observations[idx_cam].sensor_name = camera_sensor_name_;
00184     msg->observations[idx_chain].sensor_name = chain_sensor_name_;
00185          
00186     msg->observations[idx_cam].features.resize(points_x_ * points_y_);
00187     msg->observations[idx_chain].features.resize(points_x_ * points_y_);
00188 
00189 
00190     // Fill in the headers
00191     rgbd.header = cloud_.header;
00192     world.header.frame_id = "checkerboard";
00193 
00194     // Fill in message
00195     sensor_msgs::PointCloud2ConstIterator<float> xyz(cloud_, "x");
00196     for (size_t i = 0; i < points.size(); ++i)
00197     {
00198       //world.point.x = (i % points_x_) * square_size_;
00199       //world.point.y = (i / points_x_) * square_size_;
00200       world.point.z = (i % points_x_) * -square_size_;
00201       world.point.x = (i / points_x_) * -square_size_;
00202 
00203       // Get 3d point
00204       int index = (int)(points[i].y) * cloud_.width + (int)(points[i].x);
00205       rgbd.point.x = (xyz + index)[X];
00206       rgbd.point.y = (xyz + index)[Y];
00207       rgbd.point.z = (xyz + index)[Z];
00208 
00209       // Do not accept NANs
00210       if (isnan(rgbd.point.x) ||
00211           isnan(rgbd.point.y) ||
00212           isnan(rgbd.point.z))
00213       {
00214         ROS_ERROR_STREAM("NAN point on " << i);
00215         return false;
00216       }
00217 
00218       msg->observations[idx_cam].features[i] = rgbd;
00219       msg->observations[idx_cam].ext_camera_info = depth_camera_manager_.getDepthCameraInfo();
00220       msg->observations[idx_chain].features[i] = world;
00221 
00222       // Visualize
00223       iter_cloud[0] = rgbd.point.x;
00224       iter_cloud[1] = rgbd.point.y;
00225       iter_cloud[2] = rgbd.point.z;
00226       ++iter_cloud;
00227     }
00228 
00229     // Add debug cloud to message
00230     if (output_debug_)
00231     {
00232       msg->observations[idx_cam].cloud = cloud_;
00233     }
00234 
00235     // Publish results
00236     publisher_.publish(cloud);
00237 
00238     // Found all points
00239     return true;
00240   }
00241 
00242   return false;
00243 }
00244 
00245 }  // namespace robot_calibration


robot_calibration
Author(s): Michael Ferguson
autogenerated on Sat May 20 2017 02:35:31