calibration.cpp
Go to the documentation of this file.
00001 
00013 #include <rail_ceiling/calibration.hpp>
00014 #include <tf/transform_listener.h>
00015 #include <sstream>
00016 #include <fstream>
00017 
00018 using namespace std;
00019 
00020 calibration::calibration() :
00021     pnh_("~")
00022 {
00023   this->calibrated_ = false;
00024 
00025   // grab the number of cameras and samples to take
00026   pnh_.param("fixed_frame", this->fixed_frame_, string("map"));
00027   pnh_.param("camera_frame_id_prefix", this->camera_frame_id_prefix_, string("ceiling_cam_"));
00028   pnh_.param("num_cameras", this->num_cameras_, 1);
00029   pnh_.param("num_samples", this->num_samples_, 10);
00030 
00031   // subscribe to each marker topic and grab the parameters
00032   for (int i = 0; i < this->num_cameras_; i++)
00033   {
00034     // construct the topic and parameter names
00035     stringstream topic_ss, x_pos_ss, y_pos_ss, z_pos_ss, x_rot_ss, y_rot_ss, z_rot_ss, w_rot_ss;
00036     topic_ss << "ceiling_cam_tracker_" << i << "/ar_pose_marker";
00037     x_pos_ss << "ceiling_cam_" << i << "_pos_x";
00038     y_pos_ss << "ceiling_cam_" << i << "_pos_y";
00039     z_pos_ss << "ceiling_cam_" << i << "_pos_z";
00040     x_rot_ss << "ceiling_cam_" << i << "_rot_x";
00041     y_rot_ss << "ceiling_cam_" << i << "_rot_y";
00042     z_rot_ss << "ceiling_cam_" << i << "_rot_z";
00043     w_rot_ss << "ceiling_cam_" << i << "_rot_w";
00044 
00045     // create the subscription
00046     ros::Subscriber sub = this->nh_.subscribe<ar_track_alvar_msgs::AlvarMarkers>(
00047         topic_ss.str(), 1, boost::bind(&calibration::marker_cback, this, _1, i));
00048     // add it to the list
00049     this->marker_subs_.push_back(sub);
00050     // add a samples vector
00051     vector<geometry_msgs::Pose> samples;
00052     this->samples_.push_back(samples);
00053 
00054     // search for parameters
00055     geometry_msgs::Pose pose;
00056     pnh_.param(x_pos_ss.str(), pose.position.x, 0.0);
00057     pnh_.param(y_pos_ss.str(), pose.position.y, 0.0);
00058     pnh_.param(z_pos_ss.str(), pose.position.z, 0.0);
00059     pnh_.param(x_rot_ss.str(), pose.orientation.x, 0.0);
00060     pnh_.param(y_rot_ss.str(), pose.orientation.y, 0.0);
00061     pnh_.param(z_rot_ss.str(), pose.orientation.z, 0.0);
00062     pnh_.param(w_rot_ss.str(), pose.orientation.w, 1.0);
00063     this->fixed_poses_.push_back(pose);
00064   }
00065 
00066   ROS_INFO("Waiting to find %i samples of the calibration markers for each camera...", this->num_samples_);
00067 }
00068 
00069 void calibration::marker_cback(const ar_track_alvar_msgs::AlvarMarkers::ConstPtr& msg, int camera)
00070 {
00071   // check if we need anymore samples
00072   vector<geometry_msgs::Pose> &samples = this->samples_.at(camera);
00073   if (samples.size() < this->num_samples_)
00074   {
00075     // search for the correct marker for this camera
00076     int dest_marker = camera + 100;
00077     for (int i = 0; i < msg->markers.size(); i++)
00078     {
00079       ar_track_alvar_msgs::AlvarMarker marker = msg->markers.at(i);
00080       if (marker.id == dest_marker)
00081         // save the pose
00082         samples.push_back(marker.pose.pose);
00083     }
00084   }
00085 }
00086 
00087 void calibration::publish_tf()
00088 {
00089   // go through each marker
00090   for (int i = 0; i < this->num_cameras_; i++)
00091   {
00092     // publish the fixed pose
00093     geometry_msgs::Pose &fixed_pose = this->fixed_poses_.at(i);
00094     // create a TF
00095     tf::Transform tf_fixed;
00096     tf_fixed.setOrigin(tf::Vector3(fixed_pose.position.x, fixed_pose.position.y, fixed_pose.position.z));
00097     tf_fixed.setRotation(
00098         tf::Quaternion(fixed_pose.orientation.x, fixed_pose.orientation.y, fixed_pose.orientation.z,
00099                        fixed_pose.orientation.w));
00100     // publish the fixed pose TF
00101     stringstream ss_fixed;
00102     ss_fixed << FIXED_LINK_NAME << i;
00103     br.sendTransform(tf::StampedTransform(tf_fixed, ros::Time::now(), this->fixed_frame_, ss_fixed.str()));
00104 
00105     // publish the average pose from the camera if ready
00106     if (this->average_poses_.size() > i)
00107     {
00108       // publish the average pose
00109       geometry_msgs::Pose &average_pose = this->average_poses_.at(i);
00110       // create a TF
00111       tf::Transform tf_average;
00112       tf_average.setRotation(tf::Quaternion(0, 0, 0, 1));
00113       tf_average.setOrigin(tf::Vector3(average_pose.position.x, average_pose.position.y, average_pose.position.z));
00114       tf_average.setRotation(
00115           tf::Quaternion(average_pose.orientation.x, average_pose.orientation.y, average_pose.orientation.z,
00116                          average_pose.orientation.w).normalize());
00117       // now invert it
00118       tf::Transform tf_average_inverse = tf_average.inverse();
00119       stringstream ss_camera;
00120       ss_camera << CAMERA_LINK_NAME << i;
00121       br.sendTransform(tf::StampedTransform(tf_average_inverse, ros::Time::now(), ss_fixed.str(), ss_camera.str()));
00122     }
00123   }
00124 }
00125 
00126 void calibration::attempt_calibration()
00127 {
00128   // check if we finished
00129   if (!this->calibrated_)
00130   {
00131     // check for all the samples
00132     bool ready = true;
00133     for (int i = 0; i < this->num_cameras_; i++)
00134       ready &= this->samples_.at(i).size() >= this->num_samples_;
00135 
00136     if (ready)
00137     {
00138       ROS_INFO("Sample collection complete.");
00139 
00140       // unsubscribe
00141       for (int i = 0; i < this->num_cameras_; i++)
00142         this->marker_subs_.at(i).shutdown();
00143 
00144       // calculate the average pose
00145       for (int i = 0; i < this->num_cameras_; i++)
00146       {
00147         geometry_msgs::Pose pose;
00148         vector<geometry_msgs::Pose> &samples = this->samples_.at(i);
00149         for (int j = 0; j < this->num_samples_; j++)
00150         {
00151           // calculate the average as we go
00152           geometry_msgs::Pose &sample = samples.at(j);
00153           int n = j + 1;
00154           pose.position.x = (((n - 1) * pose.position.x + sample.position.x) / (float)n);
00155           pose.position.y = (((n - 1) * pose.position.y + sample.position.y) / (float)n);
00156           pose.position.z = (((n - 1) * pose.position.z + sample.position.z) / (float)n);
00157           pose.orientation.w = (((n - 1) * pose.orientation.w + sample.orientation.w) / (float)n);
00158           pose.orientation.x = (((n - 1) * pose.orientation.x + sample.orientation.x) / (float)n);
00159           pose.orientation.y = (((n - 1) * pose.orientation.y + sample.orientation.y) / (float)n);
00160           pose.orientation.z = (((n - 1) * pose.orientation.z + sample.orientation.z) / (float)n);
00161         }
00162         // save the pose
00163         this->average_poses_.push_back(pose);
00164       }
00165 
00166       // publish transforms from the marker to the camera
00167       this->publish_tf();
00168 
00169       // write the calibration file
00170       this->write_calibration();
00171 
00172       this->calibrated_ = true;
00173       ROS_INFO("Calibration complete!");
00174     }
00175   }
00176 }
00177 
00178 void calibration::write_calibration()
00179 {
00180   // get the current TFs
00181   tf::TransformListener listener;
00182   vector<tf::StampedTransform> tfs;
00183 
00184   for (int i = 0; i < this->num_cameras_; i++)
00185   {
00186     stringstream ss_frame;
00187     ss_frame << CAMERA_LINK_NAME << i;
00188 
00189     // wait for the TF to come back
00190     bool found = false;
00191     while (!found)
00192     {
00193       try
00194       {
00195         // try and get the frame
00196         tf::StampedTransform tf;
00197         listener.lookupTransform(this->fixed_frame_, ss_frame.str(), ros::Time(0), tf);
00198         tfs.push_back(tf);
00199         found = true;
00200       }
00201       catch (tf::TransformException &ex)
00202       {
00203         // republish the TF
00204         this->publish_tf();
00205         // sleep and continue
00206         ros::Duration(1.0).sleep();
00207       }
00208     }
00209   }
00210 
00211   stringstream ss;
00212   ss << getenv("HOME") << "/" << URDF;
00213   string file_name = ss.str();
00214 
00215   // open the file for writing
00216   ofstream urdf;
00217   urdf.open(file_name.c_str());
00218   if (!urdf.is_open())
00219     ROS_ERROR("Failed to open '~/%s' for writing.", file_name.c_str());
00220   else
00221   {
00222     urdf << "<?xml version=\"1.0\"?>\n";
00223     urdf << "<robot xmlns:xacro=\"http://www.ros.org/wiki/xacro\" name=\"ceiling\">\n\n";
00224 
00225     urdf << "  <!-- Auto-Generated from rail_ceiling/calibration Node -->\n\n";
00226 
00227     urdf << "  <xacro:include filename=\"$(find rail_ceiling)/urdf/camera.urdf.xacro\" />\n\n";
00228 
00229     urdf << "  <xacro:property name=\"PARENT\" value=\"" << this->fixed_frame_ << "\" />\n\n";
00230 
00231     urdf << "  <!-- fixed frame -->\n";
00232     urdf << "  <link name=\"${PARENT}\" />\n\n";
00233 
00234     urdf << "  <!-- " << this->num_cameras_ << " Camera(s) -->\n";
00235     for (int i = 0; i < this->num_cameras_; i++)
00236     {
00237       // grab the TF info
00238       tf::StampedTransform &tf = tfs.at(i);
00239       tf::Vector3 &pos = tf.getOrigin();
00240       double roll, pitch, yaw;
00241       tf.getBasis().getRPY(roll, pitch, yaw);
00242       urdf << "  <xacro:ceiling_cam parent=\"${PARENT}\" link=\"" << this->camera_frame_id_prefix_ << i << "\">\n";
00243       urdf << "    <origin xyz=\"" << pos.getX() << " " << pos.getY() << " " << pos.getZ() << "\" rpy=\"" << roll << " "
00244           << pitch << " " << yaw << "\" />\n";
00245       urdf << "  </xacro:ceiling_cam>\n";
00246     }
00247     urdf << "</robot>\n\n";
00248 
00249     urdf.close();
00250     ROS_INFO("Calibration written to '%s'.", file_name.c_str());
00251   }
00252 }
00253 
00254 int main(int argc, char **argv)
00255 {
00256   // initialize ROS and the node
00257   ros::init(argc, argv, "calibration");
00258 
00259   // initialize the calibration object
00260   calibration calib;
00261 
00262   // continue until a ctrl-c has occurred
00263   ros::Rate r(60);
00264   while (ros::ok())
00265   {
00266     calib.publish_tf();
00267     calib.attempt_calibration();
00268     ros::spinOnce();
00269     r.sleep();
00270   }
00271 
00272   return EXIT_SUCCESS;
00273 }


rail_ceiling
Author(s): Russell Toris , David Kent
autogenerated on Sat Jun 8 2019 20:39:22