stereo_odometer.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 #include <sensor_msgs/image_encodings.h>
00003 #include <image_geometry/stereo_camera_model.h>
00004 #include <cv_bridge/cv_bridge.h>
00005 #include <pcl_ros/point_cloud.h>
00006 #include <pcl/point_types.h>
00007 
00008 #include <viso_stereo.h>
00009 
00010 #include <viso2_ros/VisoInfo.h>
00011 
00012 #include "stereo_processor.h"
00013 #include "odometer_base.h"
00014 #include "odometry_params.h"
00015 
00016 // to remove after debugging
00017 #include <opencv2/highgui/highgui.hpp>
00018 
00019 #include "cv.h"
00020 #include "highgui.h"
00021 
00022 namespace viso2_ros
00023 {
00024 
00025     // some arbitrary values (0.1m^2 linear cov. 10deg^2. angular cov.)
00026     static const boost::array<double, 36> STANDARD_POSE_COVARIANCE =
00027     { { 0.1, 0, 0, 0, 0, 0,
00028           0, 0.1, 0, 0, 0, 0,
00029           0, 0, 0.1, 0, 0, 0,
00030           0, 0, 0, 0.17, 0, 0,
00031           0, 0, 0, 0, 0.17, 0,
00032           0, 0, 0, 0, 0, 0.17 } };
00033     static const boost::array<double, 36> STANDARD_TWIST_COVARIANCE =
00034     { { 0.05, 0, 0, 0, 0, 0,
00035           0, 0.05, 0, 0, 0, 0,
00036           0, 0, 0.05, 0, 0, 0,
00037           0, 0, 0, 0.09, 0, 0,
00038           0, 0, 0, 0, 0.09, 0,
00039           0, 0, 0, 0, 0, 0.09 } };
00040     static const boost::array<double, 36> BAD_COVARIANCE =
00041     { { 9999, 0, 0, 0, 0, 0,
00042           0, 9999, 0, 0, 0, 0,
00043           0, 0, 9999, 0, 0, 0,
00044           0, 0, 0, 9999, 0, 0,
00045           0, 0, 0, 0, 9999, 0,
00046           0, 0, 0, 0, 0, 9999 } };
00047 
00048 
00049     class StereoOdometer : public StereoProcessor, public OdometerBase
00050     {
00051 
00052         private:
00053 
00054             boost::shared_ptr<VisualOdometryStereo> visual_odometer_;
00055             VisualOdometryStereo::parameters visual_odometer_params_;
00056 
00057             ros::Publisher point_cloud_pub_;
00058             ros::Publisher info_pub_;
00059 
00060             bool got_lost_;
00061 
00062             // change reference frame method. 0, 1 or 2. 0 means allways change. 1 and 2 explained below
00063             int ref_frame_change_method_;
00064             //0: none, 1: show left and right matches, 2: show pre and cur matches;
00065             int show_matches;
00066             bool change_reference_frame_;
00067             double ref_frame_motion_threshold_; // method 1. Change the reference frame if last motion is small
00068             int ref_frame_inlier_threshold_; // method 2. Change the reference frame if the number of inliers is low
00069             Matrix reference_motion_;
00070 
00071         public:
00072 
00073             typedef pcl::PointCloud<pcl::PointXYZRGB> PointCloud;
00074 
00075             StereoOdometer(const std::string& transport) :
00076                 StereoProcessor(transport), OdometerBase(),
00077                 got_lost_(false), change_reference_frame_(false)
00078         {
00079             // Read local parameters
00080             ros::NodeHandle local_nh("~");
00081             odometry_params::loadParams(local_nh, visual_odometer_params_);
00082 
00083             local_nh.param("show_matches", show_matches, 0);
00084             local_nh.param("ref_frame_change_method", ref_frame_change_method_, 1);
00085             local_nh.param("ref_frame_motion_threshold", ref_frame_motion_threshold_, 5.0);
00086             local_nh.param("ref_frame_inlier_threshold", ref_frame_inlier_threshold_, 150);
00087 
00088             point_cloud_pub_ = local_nh.advertise<PointCloud>("point_cloud", 1);
00089             info_pub_ = local_nh.advertise<VisoInfo>("info", 1);
00090 
00091             reference_motion_ = Matrix::eye(4);
00092         }
00093 
00094         protected:
00095 
00096             void initOdometer(
00097                     const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00098                     const sensor_msgs::CameraInfoConstPtr& r_info_msg)
00099             {
00100                 // read calibration info from camera info message
00101                 // to fill remaining parameters
00102                 image_geometry::StereoCameraModel model;
00103                 model.fromCameraInfo(*l_info_msg, *r_info_msg);
00104                 visual_odometer_params_.base = model.baseline();
00105                 visual_odometer_params_.calib.f = model.left().fx();
00106                 visual_odometer_params_.calib.cu = model.left().cx();
00107                 visual_odometer_params_.calib.cv = model.left().cy();
00108                 visual_odometer_.reset(new VisualOdometryStereo(visual_odometer_params_));
00109                 if (l_info_msg->header.frame_id != "") setSensorFrameId(l_info_msg->header.frame_id);
00110                 ROS_INFO_STREAM("Initialized libviso2 stereo odometry "
00111                         "with the following parameters:" << std::endl <<
00112                         visual_odometer_params_ <<
00113                         "  ref_frame_change_method = " << ref_frame_change_method_ << std::endl <<
00114                         "  ref_frame_motion_threshold = " << ref_frame_motion_threshold_ << std::endl <<
00115                         "  ref_frame_inlier_threshold = " << ref_frame_inlier_threshold_);
00116             }
00117 
00118             void imageCallback(
00119                     const sensor_msgs::ImageConstPtr& l_image_msg,
00120                     const sensor_msgs::ImageConstPtr& r_image_msg,
00121                     const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00122                     const sensor_msgs::CameraInfoConstPtr& r_info_msg)
00123             {
00124                 ros::WallTime start_time = ros::WallTime::now();
00125                 bool first_run = false;
00126                 // create odometer if not exists
00127                 if (!visual_odometer_)
00128                 {
00129                     first_run = true;
00130                     initOdometer(l_info_msg, r_info_msg);
00131                 }
00132 
00133                 // convert images if necessary
00134                 uint8_t *l_image_data, *r_image_data;
00135                 int l_step, r_step;
00136                 cv_bridge::CvImageConstPtr l_cv_ptr, r_cv_ptr;
00137                 l_cv_ptr = cv_bridge::toCvShare(l_image_msg, sensor_msgs::image_encodings::MONO8);
00138                 r_cv_ptr = cv_bridge::toCvShare(r_image_msg, sensor_msgs::image_encodings::MONO8);
00139 
00140                 l_image_data = l_cv_ptr->image.data;
00141                 l_step = l_cv_ptr->image.step[0];
00142                 r_image_data = r_cv_ptr->image.data;
00143                 r_step = r_cv_ptr->image.step[0];
00144                 int32_t dims[] = {l_image_msg->width, l_image_msg->height, l_step};
00145 
00146                 ROS_ASSERT(l_step == r_step);
00147                 ROS_ASSERT(l_image_msg->width == r_image_msg->width);
00148                 ROS_ASSERT(l_image_msg->height == r_image_msg->height);
00149 
00150                 // on first run or when odometer got lost, only feed the odometer with
00151                 // images without retrieving data
00152                 if (first_run || got_lost_)
00153                 {
00154                     visual_odometer_->process(l_image_data, r_image_data, dims);
00155                     got_lost_ = false;
00156                     // on first run publish zero once
00157                     if (first_run)
00158                     {
00159                         tf::Transform delta_transform;
00160                         delta_transform.setIdentity();
00161                         integrateAndPublish(delta_transform, l_image_msg->header.stamp);
00162                     }
00163                 }
00164                 else
00165                 {
00166                     bool success = visual_odometer_->process(
00167                             l_image_data, r_image_data, dims, change_reference_frame_);
00168                     if (success)
00169                     {
00170                         Matrix motion = Matrix::inv(visual_odometer_->getMotion());
00171                         ROS_WARN_THROTTLE(1.0,"Found %i matches with %i inliers.",
00172                                 visual_odometer_->getNumberOfMatches(),
00173                                 visual_odometer_->getNumberOfInliers());
00174                         ROS_DEBUG_STREAM("libviso2 returned the following motion:\n" << motion);
00175                         Matrix camera_motion;
00176                         // if image was replaced due to small motion we have to subtract the
00177                         // last motion to get the increment
00178                         if (change_reference_frame_)
00179                         {
00180                             camera_motion = Matrix::inv(reference_motion_) * motion;
00181                         }
00182                         else
00183                         {
00184                             // image was not replaced, report full motion from odometer
00185                             camera_motion = motion;
00186                         }
00187                         reference_motion_ = motion; // store last motion as reference
00188 
00189                         tf::Matrix3x3 rot_mat(
00190                                 camera_motion.val[0][0], camera_motion.val[0][1], camera_motion.val[0][2],
00191                                 camera_motion.val[1][0], camera_motion.val[1][1], camera_motion.val[1][2],
00192                                 camera_motion.val[2][0], camera_motion.val[2][1], camera_motion.val[2][2]);
00193                         tf::Vector3 t(camera_motion.val[0][3], camera_motion.val[1][3], camera_motion.val[2][3]);
00194                         tf::Transform delta_transform(rot_mat, t);
00195 
00196                         setPoseCovariance(STANDARD_POSE_COVARIANCE);
00197                         setTwistCovariance(STANDARD_TWIST_COVARIANCE);
00198 
00199                         integrateAndPublish(delta_transform, l_image_msg->header.stamp);
00200 
00201                         if(1 == show_matches)
00202                         {
00203                             drawMatches(l_info_msg, l_image_msg, r_info_msg,r_image_msg,
00204                                     visual_odometer_->getMatches(),
00205                                     visual_odometer_->getInlierIndices());
00206                         }
00207                         else if(2 == show_matches)
00208                         {
00209                             drawMatchesPreAndCurrent(l_info_msg, l_image_msg, r_info_msg,r_image_msg,
00210                                     visual_odometer_->getMatches(),
00211                                     visual_odometer_->getInlierIndices());
00212                         }
00213 
00214                         if (point_cloud_pub_.getNumSubscribers() > 0)
00215                         {
00216                             computeAndPublishPointCloud(l_info_msg, l_image_msg, r_info_msg,
00217                                     visual_odometer_->getMatches(),
00218                                     visual_odometer_->getInlierIndices());
00219                         }
00220                     }
00221                     else
00222                     {
00223                         setPoseCovariance(BAD_COVARIANCE);
00224                         setTwistCovariance(BAD_COVARIANCE);
00225                         tf::Transform delta_transform;
00226                         delta_transform.setIdentity();
00227                         integrateAndPublish(delta_transform, l_image_msg->header.stamp);
00228 
00229                         ROS_DEBUG("Call to VisualOdometryStereo::process() failed.");
00230                         ROS_WARN_THROTTLE(1.0, "Visual Odometer got lost!");
00231                         got_lost_ = true;
00232                     }
00233 
00234                     if(success)
00235                     {
00236 
00237                         // Proceed depending on the reference frame change method
00238                         switch ( ref_frame_change_method_ )
00239                         {
00240                             case 1:
00241                                 {
00242                                     // calculate current feature flow
00243                                     double feature_flow = computeFeatureFlow(visual_odometer_->getMatches());
00244                                     change_reference_frame_ = (feature_flow < ref_frame_motion_threshold_);
00245                                     ROS_DEBUG_STREAM("Feature flow is " << feature_flow
00246                                             << ", marking last motion as "
00247                                             << (change_reference_frame_ ? "small." : "normal."));
00248                                     break;
00249                                 }
00250                             case 2:
00251                                 {
00252                                     change_reference_frame_ = (visual_odometer_->getNumberOfInliers() > ref_frame_inlier_threshold_);
00253                                     break;
00254                                 }
00255                             default:
00256                                 change_reference_frame_ = false;
00257                         }
00258 
00259                     }
00260                     else
00261                         change_reference_frame_ = false;
00262 
00263                     if(!change_reference_frame_)
00264                         ROS_DEBUG_STREAM("Changing reference frame");
00265 
00266                     // create and publish viso2 info msg
00267                     VisoInfo info_msg;
00268                     info_msg.header.stamp = l_image_msg->header.stamp;
00269                     info_msg.got_lost = !success;
00270                     info_msg.change_reference_frame = !change_reference_frame_;
00271                     info_msg.num_matches = visual_odometer_->getNumberOfMatches();
00272                     info_msg.num_inliers = visual_odometer_->getNumberOfInliers();
00273                     ros::WallDuration time_elapsed = ros::WallTime::now() - start_time;
00274                     info_msg.runtime = time_elapsed.toSec();
00275                     info_pub_.publish(info_msg);
00276                 }
00277             }
00278 
00279             double computeFeatureFlow(
00280                     const std::vector<Matcher::p_match>& matches)
00281             {
00282                 double total_flow = 0.0;
00283                 for (size_t i = 0; i < matches.size(); ++i)
00284                 {
00285                     double x_diff = matches[i].u1c - matches[i].u1p;
00286                     double y_diff = matches[i].v1c - matches[i].v1p;
00287                     total_flow += sqrt(x_diff * x_diff + y_diff * y_diff);
00288                 }
00289                 return total_flow / matches.size();
00290             }
00291 
00292             //Draw matches left and right
00293             void drawMatches(
00294                     const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00295                     const sensor_msgs::ImageConstPtr& l_image_msg,
00296                     const sensor_msgs::CameraInfoConstPtr& r_info_msg,
00297                     const sensor_msgs::ImageConstPtr& r_image_msg,
00298                     const std::vector<Matcher::p_match>& matches,
00299                     const std::vector<int32_t>& inlier_indices)
00300             {
00301 
00302                 try
00303                 {
00304                     using namespace cv;
00305                     cv_bridge::CvImageConstPtr cv_ptr_l,cv_ptr_r;
00306                     cv_ptr_l = cv_bridge::toCvShare(l_image_msg, sensor_msgs::image_encodings::RGB8);
00307                     cv::Mat img_l = cv_ptr_l->image;
00308                     cv_ptr_r = cv_bridge::toCvShare(r_image_msg, sensor_msgs::image_encodings::RGB8);
00309                     cv::Mat img_r = cv_ptr_r->image;
00310 
00311                     // read calibration info from camera info message
00312                     image_geometry::StereoCameraModel model;
00313                     model.fromCameraInfo(*l_info_msg, *r_info_msg);
00314 
00315                     Mat imgResult(img_l.rows,2*img_l.cols,img_l.type());
00316                     Mat roiImgResult_Left = imgResult(Rect(0,0,img_l.cols,img_l.rows));
00317                     Mat roiImgResult_Right = imgResult(Rect(img_l.cols,0,img_l.cols,img_l.rows));
00318                     Mat roiImg1 = img_l(Rect(0,0,img_l.cols,img_l.rows));
00319                     Mat roiImg2 = img_r(Rect(0,0,img_r.cols,img_r.rows));
00320                     roiImg1.copyTo(roiImgResult_Left);
00321                     roiImg2.copyTo(roiImgResult_Right);
00322 
00323                     for (size_t i = 0; i < inlier_indices.size(); ++i)
00324                     {
00325                         double u1c,v1c,u2c,v2c;
00326                         u1c = matches[inlier_indices[i]].u1c;
00327                         v1c = matches[inlier_indices[i]].v1c;
00328                         u2c = matches[inlier_indices[i]].u2c;
00329                         v2c = matches[inlier_indices[i]].v2c;
00330                         line( imgResult,
00331                                 Point(u1c,v1c),
00332                                 Point(u2c+img_l.cols,v2c),
00333                                 Scalar( 255, 0, 0 ),
00334                                 2 ,
00335                                 8 );
00336                     }
00337                     //cv::imwrite("左右匹配图.png",imgResult);
00338                     imshow("Finalimg",imgResult);
00339                     cv::waitKey(2);
00340                 }
00341                 catch (cv_bridge::Exception& e)
00342                 {
00343                     ROS_ERROR("cv_bridge exception: %s", e.what());
00344                 }
00345             }
00346 
00347             cv::Mat pre;
00348             void drawMatchesPreAndCurrent(
00349                     const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00350                     const sensor_msgs::ImageConstPtr& l_image_msg,
00351                     const sensor_msgs::CameraInfoConstPtr& r_info_msg,
00352                     const sensor_msgs::ImageConstPtr& r_image_msg,
00353                     const std::vector<Matcher::p_match>& matches,
00354                     const std::vector<int32_t>& inlier_indices)
00355             {
00356                 //cout<<"matches.size="<<matches.size()<<" inlier_indices.size="<<inlier_indices.size()<<endl;
00357                 try
00358                 {
00359                     using namespace cv;
00360                     cv_bridge::CvImageConstPtr cv_ptr_l;
00361                     cv_ptr_l = cv_bridge::toCvShare(l_image_msg, sensor_msgs::image_encodings::RGB8);
00362                     cv::Mat img_l = cv_ptr_l->image;
00363 
00364                     if(pre.rows == 0)
00365                     {
00366                         pre = img_l;
00367                         return;
00368                     }
00369 
00370                     // read calibration info from camera info message
00371                     image_geometry::StereoCameraModel model;
00372                     model.fromCameraInfo(*l_info_msg, *r_info_msg);
00373 
00374                     Mat imgResult(img_l.rows,2*img_l.cols,img_l.type());
00375                     Mat roiImgResult_Left = imgResult(Rect(0,0,img_l.cols,img_l.rows));
00376                     Mat roiImgResult_Right = imgResult(Rect(img_l.cols,0,img_l.cols,img_l.rows));
00377                     Mat roiImg1 = img_l(Rect(0,0,img_l.cols,img_l.rows));
00378                     Mat roiImg2 = pre(Rect(0,0,pre.cols,pre.rows));
00379                     roiImg1.copyTo(roiImgResult_Left);
00380                     roiImg2.copyTo(roiImgResult_Right);
00381                     for (size_t i = 0; i < matches.size(); ++i)
00382                     {
00383                         double u1c,v1c,u1p,v1p;
00384                         u1c = matches[i].u1c;
00385                         v1c = matches[i].v1c;
00386                         u1p = matches[i].u1p;
00387                         v1p = matches[i].v1p;
00388                         line( imgResult,
00389                                 Point(u1c,v1c),
00390                                 Point(u1p,v1p),
00391                                 Scalar( 0, 0, 255),
00392                                 2 ,
00393                                 8 );
00394                     }
00395                     for (size_t i = 0; i < inlier_indices.size(); ++i)
00396                     {
00397                         double u1c,v1c,u1p,v1p;
00398                         u1c = matches[inlier_indices[i]].u1c;
00399                         v1c = matches[inlier_indices[i]].v1c;
00400                         u1p = matches[inlier_indices[i]].u1p;
00401                         v1p = matches[inlier_indices[i]].v1p;
00402                         line( imgResult,
00403                                 Point(u1c,v1c),
00404                                 Point(u1p,v1p),
00405                                 Scalar( 0, 255, 0 ),
00406                                 2 ,
00407                                 8 );
00408                     }
00409                     imshow("Finalimg",imgResult);
00410                     //cv::imwrite("前后匹配图.png",imgResult);
00411                     cv::waitKey(2);
00412                     pre = img_l;
00413                 }
00414                 catch (cv_bridge::Exception& e)
00415                 {
00416                     ROS_ERROR("cv_bridge exception: %s", e.what());
00417                 }
00418             }
00419 
00420 
00421 
00422             void computeAndPublishPointCloud(
00423                     const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00424                     const sensor_msgs::ImageConstPtr& l_image_msg,
00425                     const sensor_msgs::CameraInfoConstPtr& r_info_msg,
00426                     const std::vector<Matcher::p_match>& matches,
00427                     const std::vector<int32_t>& inlier_indices)
00428             {
00429                 try
00430                 {
00431                     cv_bridge::CvImageConstPtr cv_ptr;
00432                     cv_ptr = cv_bridge::toCvShare(l_image_msg, sensor_msgs::image_encodings::RGB8);
00433                     // read calibration info from camera info message
00434                     image_geometry::StereoCameraModel model;
00435                     model.fromCameraInfo(*l_info_msg, *r_info_msg);
00436                     PointCloud::Ptr point_cloud(new PointCloud());
00437                     point_cloud->header.frame_id = getSensorFrameId();
00438                     point_cloud->header.stamp = pcl_conversions::toPCL(l_info_msg->header).stamp;
00439                     point_cloud->width = 1;
00440                     point_cloud->height = inlier_indices.size();
00441                     point_cloud->points.resize(inlier_indices.size());
00442 
00443                     for (size_t i = 0; i < inlier_indices.size(); ++i)
00444                     {
00445                         const Matcher::p_match& match = matches[inlier_indices[i]];
00446                         cv::Point2d left_uv;
00447                         left_uv.x = match.u1c;
00448                         left_uv.y = match.v1c;
00449                         cv::Point3d point;
00450                         double disparity = match.u1c - match.u2c;
00451                         model.projectDisparityTo3d(left_uv, disparity, point);
00452                         point_cloud->points[i].x = point.x;
00453                         point_cloud->points[i].y = point.y;
00454                         point_cloud->points[i].z = point.z;
00455                         cv::Vec3b color = cv_ptr->image.at<cv::Vec3b>(left_uv.y,left_uv.x);
00456                         point_cloud->points[i].r = color[0];
00457                         point_cloud->points[i].g = color[1];
00458                         point_cloud->points[i].b = color[2];
00459                     }
00460 
00461                     ROS_DEBUG("Publishing point cloud with %zu points.", point_cloud->size());
00462                     point_cloud_pub_.publish(point_cloud);
00463                 }
00464                 catch (cv_bridge::Exception& e)
00465                 {
00466                     ROS_ERROR("cv_bridge exception: %s", e.what());
00467                 }
00468             }
00469     };
00470 
00471 } // end of namespace
00472 
00473 
00474 int main(int argc, char **argv)
00475 {
00476     ros::init(argc, argv, "stereo_odometer");
00477     if (ros::names::remap("stereo") == "stereo") {
00478         ROS_WARN("'stereo' has not been remapped! Example command-line usage:\n"
00479                 "\t$ rosrun viso2_ros stereo_odometer stereo:=narrow_stereo image:=image_rect");
00480     }
00481     if (ros::names::remap("image").find("rect") == std::string::npos) {
00482         ROS_WARN("stereo_odometer needs rectified input images. The used image "
00483                 "topic is '%s'. Are you sure the images are rectified?",
00484                 ros::names::remap("image").c_str());
00485     }
00486 
00487     std::string transport = argc > 1 ? argv[1] : "raw";
00488     viso2_ros::StereoOdometer odometer(transport);
00489 
00490     ros::spin();
00491     return 0;
00492 }
00493 


dlut_viso2_ros
Author(s): Zhuang Yan , Yan Fei, Wu Nai Liang
autogenerated on Thu Jun 6 2019 20:03:36