00001
00024 #include "ccny_rgbd/apps/visual_odometry.h"
00025
00026 namespace ccny_rgbd {
00027
00028 VisualOdometry::VisualOdometry(
00029 const ros::NodeHandle& nh,
00030 const ros::NodeHandle& nh_private):
00031 nh_(nh),
00032 nh_private_(nh_private),
00033 initialized_(false),
00034 frame_count_(0)
00035 {
00036 ROS_INFO("Starting RGBD Visual Odometry");
00037
00038
00039
00040 initParams();
00041
00042
00043
00044 f2b_.setIdentity();
00045
00046
00047
00048 odom_publisher_ = nh_.advertise<OdomMsg>(
00049 "vo", queue_size_);
00050 cloud_publisher_ = nh_.advertise<PointCloudFeature>(
00051 "feature/cloud", 1);
00052 path_pub_ = nh_.advertise<PathMsg>(
00053 "path", queue_size_);
00054
00055
00056
00057 ImageTransport rgb_it(nh_);
00058 ImageTransport depth_it(nh_);
00059
00060 sub_rgb_.subscribe(rgb_it, "/rgbd/rgb", queue_size_);
00061 sub_depth_.subscribe(depth_it, "/rgbd/depth", queue_size_);
00062 sub_info_.subscribe(nh_, "/rgbd/info", queue_size_);
00063
00064
00065 sync_.reset(new RGBDSynchronizer3(
00066 RGBDSyncPolicy3(queue_size_), sub_rgb_, sub_depth_, sub_info_));
00067
00068 sync_->registerCallback(boost::bind(&VisualOdometry::RGBDCallback, this, _1, _2, _3));
00069 }
00070
00071 VisualOdometry::~VisualOdometry()
00072 {
00073 ROS_INFO("Destroying RGBD Visual Odometry");
00074 }
00075
00076 void VisualOdometry::initParams()
00077 {
00078 if (!nh_private_.getParam ("publish_tf", publish_tf_))
00079 publish_tf_ = true;
00080 if (!nh_private_.getParam ("publish_path", publish_path_))
00081 publish_path_ = true;
00082 if (!nh_private_.getParam ("publish_odom", publish_odom_))
00083 publish_odom_ = true;
00084 if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
00085 fixed_frame_ = "/odom";
00086 if (!nh_private_.getParam ("base_frame", base_frame_))
00087 base_frame_ = "/camera_link";
00088 if (!nh_private_.getParam ("queue_size", queue_size_))
00089 queue_size_ = 5;
00090
00091
00092
00093 if (!nh_private_.getParam ("feature/publish_cloud", publish_cloud_))
00094 publish_cloud_ = false;
00095 if (!nh_private_.getParam ("feature/detector_type", detector_type_))
00096 detector_type_ = "GFT";
00097
00098 resetDetector();
00099
00100 int smooth;
00101 double max_range, max_stdev;
00102
00103 if (!nh_private_.getParam ("feature/smooth", smooth))
00104 smooth = 0;
00105 if (!nh_private_.getParam ("feature/max_range", max_range))
00106 max_range = 5.5;
00107 if (!nh_private_.getParam ("feature/max_stdev", max_stdev))
00108 max_stdev = 0.03;
00109
00110 feature_detector_->setSmooth(smooth);
00111 feature_detector_->setMaxRange(max_range);
00112 feature_detector_->setMaxStDev(max_stdev);
00113
00114
00115
00116 if (!nh_private_.getParam ("reg/reg_type", reg_type_))
00117 reg_type_ = "ICPProbModel";
00118
00119 if (reg_type_ == "ICP")
00120 motion_estimation_ = new MotionEstimationICP(nh_, nh_private_);
00121 else if (reg_type_ == "ICPProbModel")
00122 motion_estimation_ = new MotionEstimationICPProbModel(nh_, nh_private_);
00123 else
00124 ROS_FATAL("%s is not a valid registration type!", reg_type_.c_str());
00125 }
00126
00127 void VisualOdometry::resetDetector()
00128 {
00129 gft_config_server_.reset();
00130 star_config_server_.reset();
00131 orb_config_server_.reset();
00132 surf_config_server_.reset();
00133
00134 if (detector_type_ == "ORB")
00135 {
00136 ROS_INFO("Creating ORB detector");
00137 feature_detector_.reset(new OrbDetector());
00138 orb_config_server_.reset(new
00139 OrbDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/ORB")));
00140
00141
00142 OrbDetectorConfigServer::CallbackType f = boost::bind(
00143 &VisualOdometry::orbReconfigCallback, this, _1, _2);
00144 orb_config_server_->setCallback(f);
00145 }
00146 else if (detector_type_ == "SURF")
00147 {
00148 ROS_INFO("Creating SURF detector");
00149 feature_detector_.reset(new SurfDetector());
00150 surf_config_server_.reset(new
00151 SurfDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/SURF")));
00152
00153
00154 SurfDetectorConfigServer::CallbackType f = boost::bind(
00155 &VisualOdometry::surfReconfigCallback, this, _1, _2);
00156 surf_config_server_->setCallback(f);
00157 }
00158 else if (detector_type_ == "GFT")
00159 {
00160 ROS_INFO("Creating GFT detector");
00161 feature_detector_.reset(new GftDetector());
00162 gft_config_server_.reset(new
00163 GftDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/GFT")));
00164
00165
00166 GftDetectorConfigServer::CallbackType f = boost::bind(
00167 &VisualOdometry::gftReconfigCallback, this, _1, _2);
00168 gft_config_server_->setCallback(f);
00169 }
00170 else if (detector_type_ == "STAR")
00171 {
00172 ROS_INFO("Creating STAR detector");
00173 feature_detector_.reset(new StarDetector());
00174 star_config_server_.reset(new
00175 StarDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/STAR")));
00176
00177
00178 StarDetectorConfigServer::CallbackType f = boost::bind(
00179 &VisualOdometry::starReconfigCallback, this, _1, _2);
00180 star_config_server_->setCallback(f);
00181 }
00182 else
00183 {
00184 ROS_FATAL("%s is not a valid detector type! Using GFT", detector_type_.c_str());
00185 feature_detector_.reset(new GftDetector());
00186 gft_config_server_.reset(new
00187 GftDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/GFT")));
00188
00189
00190 GftDetectorConfigServer::CallbackType f = boost::bind(
00191 &VisualOdometry::gftReconfigCallback, this, _1, _2);
00192 gft_config_server_->setCallback(f);
00193 }
00194 }
00195
00196 void VisualOdometry::RGBDCallback(
00197 const ImageMsg::ConstPtr& rgb_msg,
00198 const ImageMsg::ConstPtr& depth_msg,
00199 const CameraInfoMsg::ConstPtr& info_msg)
00200 {
00201 ros::WallTime start = ros::WallTime::now();
00202
00203
00204
00205 if (!initialized_)
00206 {
00207 initialized_ = getBaseToCameraTf(rgb_msg->header);
00208 init_time_ = rgb_msg->header.stamp;
00209 if (!initialized_) return;
00210
00211 motion_estimation_->setBaseToCameraTf(b2c_);
00212 }
00213
00214
00215
00216 ros::WallTime start_frame = ros::WallTime::now();
00217 RGBDFrame frame(rgb_msg, depth_msg, info_msg);
00218 ros::WallTime end_frame = ros::WallTime::now();
00219
00220
00221
00222 ros::WallTime start_features = ros::WallTime::now();
00223 feature_detector_->findFeatures(frame);
00224 ros::WallTime end_features = ros::WallTime::now();
00225
00226
00227
00228 ros::WallTime start_reg = ros::WallTime::now();
00229 tf::Transform motion = motion_estimation_->getMotionEstimation(frame);
00230 f2b_ = motion * f2b_;
00231 ros::WallTime end_reg = ros::WallTime::now();
00232
00233
00234
00235 if (publish_tf_) publishTf(rgb_msg->header);
00236 if (publish_odom_) publishOdom(rgb_msg->header);
00237 if (publish_path_) publishPath(rgb_msg->header);
00238
00239 if (publish_cloud_) publishFeatureCloud(frame);
00240
00241
00242
00243 ros::WallTime end = ros::WallTime::now();
00244
00245 int n_features = frame.keypoints.size();
00246 int n_valid_features = frame.n_valid_keypoints;
00247 int n_model_pts = motion_estimation_->getModelSize();
00248
00249 double d_frame = 1000.0 * (end_frame - start_frame ).toSec();
00250 double d_features = 1000.0 * (end_features - start_features).toSec();
00251 double d_reg = 1000.0 * (end_reg - start_reg ).toSec();
00252 double d_total = 1000.0 * (end - start ).toSec();
00253
00254 printf("[VO %d] Fr: %2.1f %s[%d][%d]: %3.1f %s[%d] %4.1f TOTAL %4.1f\n",
00255 frame_count_,
00256 d_frame,
00257 detector_type_.c_str(), n_features, n_valid_features, d_features,
00258 reg_type_.c_str(), n_model_pts, d_reg,
00259 d_total);
00260
00261 frame_count_++;
00262 }
00263
00264 void VisualOdometry::publishTf(const std_msgs::Header& header)
00265 {
00266 tf::StampedTransform transform_msg(
00267 f2b_, header.stamp, fixed_frame_, base_frame_);
00268 tf_broadcaster_.sendTransform (transform_msg);
00269 }
00270
00271 void VisualOdometry::publishOdom(const std_msgs::Header& header)
00272 {
00273 OdomMsg odom;
00274 odom.header.stamp = header.stamp;
00275 odom.header.frame_id = fixed_frame_;
00276 tf::poseTFToMsg(f2b_, odom.pose.pose);
00277 odom_publisher_.publish(odom);
00278 }
00279
00280 void VisualOdometry::publishPath(const std_msgs::Header& header)
00281 {
00282 path_msg_.header.stamp = header.stamp;
00283 path_msg_.header.frame_id = fixed_frame_;
00284
00285 geometry_msgs::PoseStamped pose_stamped;
00286 pose_stamped.header.stamp = header.stamp;
00287 pose_stamped.header.frame_id = fixed_frame_;
00288 tf::poseTFToMsg(f2b_, pose_stamped.pose);
00289
00290 path_msg_.poses.push_back(pose_stamped);
00291 path_pub_.publish(path_msg_);
00292 }
00293
00294 void VisualOdometry::publishFeatureCloud(RGBDFrame& frame)
00295 {
00296 PointCloudFeature cloud;
00297 cloud.header = frame.header;
00298 frame.constructFeaturePointCloud(cloud);
00299 cloud_publisher_.publish(cloud);
00300 }
00301
00302 bool VisualOdometry::getBaseToCameraTf(const std_msgs::Header& header)
00303 {
00304 tf::StampedTransform tf_m;
00305
00306 try
00307 {
00308 tf_listener_.waitForTransform(
00309 base_frame_, header.frame_id, header.stamp, ros::Duration(1.0));
00310 tf_listener_.lookupTransform (
00311 base_frame_, header.frame_id, header.stamp, tf_m);
00312 }
00313 catch (tf::TransformException& ex)
00314 {
00315 ROS_WARN("Base to camera transform unavailable %s", ex.what());
00316 return false;
00317 }
00318
00319 b2c_ = tf_m;
00320
00321 return true;
00322 }
00323
00324 void VisualOdometry::gftReconfigCallback(GftDetectorConfig& config, uint32_t level)
00325 {
00326 GftDetectorPtr gft_detector =
00327 boost::static_pointer_cast<GftDetector>(feature_detector_);
00328
00329 gft_detector->setNFeatures(config.n_features);
00330 gft_detector->setMinDistance(config.min_distance);
00331 }
00332
00333 void VisualOdometry::starReconfigCallback(StarDetectorConfig& config, uint32_t level)
00334 {
00335 StarDetectorPtr star_detector =
00336 boost::static_pointer_cast<StarDetector>(feature_detector_);
00337
00338 star_detector->setThreshold(config.threshold);
00339 star_detector->setMinDistance(config.min_distance);
00340 }
00341
00342 void VisualOdometry::surfReconfigCallback(SurfDetectorConfig& config, uint32_t level)
00343 {
00344 SurfDetectorPtr surf_detector =
00345 boost::static_pointer_cast<SurfDetector>(feature_detector_);
00346
00347 surf_detector->setThreshold(config.threshold);
00348 }
00349
00350 void VisualOdometry::orbReconfigCallback(OrbDetectorConfig& config, uint32_t level)
00351 {
00352 OrbDetectorPtr orb_detector =
00353 boost::static_pointer_cast<OrbDetector>(feature_detector_);
00354
00355 orb_detector->setThreshold(config.threshold);
00356 orb_detector->setNFeatures(config.n_features);
00357 }
00358
00359 }