00001 #include <ros/ros.h>
00002 #include <image_transport/image_transport.h>
00003 #include <image_transport/subscriber_filter.h>
00004 #include <message_filters/subscriber.h>
00005 #include <message_filters/time_synchronizer.h>
00006 #include <actionlib/client/simple_action_client.h>
00007 #include <tf/transform_listener.h>
00008
00009 #include <pr2_controllers_msgs/PointHeadAction.h>
00010 #include <topic_tools/MuxSelect.h>
00011 #include <geometry_msgs/PoseWithCovarianceStamped.h>
00012 #include <geometry_msgs/PoseArray.h>
00013 #include <kidnapped_robot/RecognizePlace.h>
00014
00015 #include <cv_bridge/CvBridge.h>
00016 #include <image_geometry/stereo_camera_model.h>
00017
00018 #include <kidnapped_robot/place_database.h>
00019 #include <posest/pe3d.h>
00020
00021 #include <boost/thread.hpp>
00022 #include <boost/scoped_ptr.hpp>
00023 #include <cmath>
00024
00025 using pr2_controllers_msgs::PointHeadAction;
00026 typedef actionlib::SimpleActionClient<PointHeadAction> PointHeadClient;
00027
00028 class KidnappedNode
00029 {
00030 ros::NodeHandle nh_;
00031 ros::CallbackQueue stereo_cb_queue_;
00032 ros::NodeHandle stereo_nh_;
00033 boost::scoped_ptr<image_transport::ImageTransport> it_;
00034 boost::scoped_ptr<ros::AsyncSpinner> spinner_;
00035
00036
00037 image_transport::SubscriberFilter l_image_sub_, r_image_sub_;
00038 message_filters::Subscriber<sensor_msgs::CameraInfo> l_info_sub_, r_info_sub_;
00039 message_filters::TimeSynchronizer<sensor_msgs::Image, sensor_msgs::CameraInfo,
00040 sensor_msgs::Image, sensor_msgs::CameraInfo> sync_;
00041 ros::Subscriber localization_sub_;
00042 tf::TransformListener tf_;
00043
00044
00045 image_transport::Publisher image_sample_pub_, image_match_pub_;
00046 geometry_msgs::PoseArray sample_poses_;
00047 ros::Publisher sample_pose_pub_;
00048 ros::Publisher initial_pose_pub_;
00049 ros::Publisher initial_pose_viz_pub_;
00050
00051
00052 ros::ServiceClient mux_client_;
00053 ros::ServiceServer pr_server_;
00054
00055
00056 kidnapped_robot::PlaceDatabase place_db_;
00057 frame_common::FrameProc frame_processor_;
00058 pe::PoseEstimator3d pose_estimator_;
00059 int pr_inliers_;
00060 int num_samples_;
00061 double distance_threshold_;
00062 bool collect_new_data_;
00063
00064
00066 boost::condition_variable sample_cond_;
00067 boost::mutex sample_mutex_;
00068 bool take_sample_;
00069 tf::Stamped<tf::Pose> best_pose_;
00070 int best_inliers_;
00071
00072
00073 sensor_msgs::CvBridge l_bridge_, r_bridge_;
00074 image_geometry::StereoCameraModel cam_model_;
00075
00076 PointHeadClient point_head_client_;
00077 std::string camera_optical_frame_, target_frame_, map_frame_;
00078 double head_target_z_;
00079
00080 public:
00081 KidnappedNode(const std::string& db_file, const std::string& vocab_tree_file,
00082 const std::string& vocab_weights_file, const std::string& calonder_trees_file)
00083 : stereo_nh_("stereo"), sync_(3),
00084 place_db_(db_file, vocab_tree_file, vocab_weights_file),
00085 frame_processor_(10),
00086 pose_estimator_(50000, true, 10.0, 3.0, 3.0),
00087 pr_inliers_(70),
00088 point_head_client_("/head_traj_controller/point_head_action", true)
00089 {
00090
00091 stereo_nh_.setCallbackQueue(&stereo_cb_queue_);
00092 it_.reset(new image_transport::ImageTransport(stereo_nh_));
00093 spinner_.reset(new ros::AsyncSpinner(1, &stereo_cb_queue_));
00094
00095 camera_optical_frame_ = "/wide_stereo_optical_frame";
00096 target_frame_ = "/base_footprint";
00097 map_frame_ = "/map";
00098 head_target_z_ = 1.2;
00099 num_samples_ = 5;
00100 distance_threshold_ = 1.0;
00101
00102 pose_estimator_.windowed = false;
00103
00104
00105
00106
00107 typedef cv::CalonderDescriptorExtractor<float> Calonder;
00108 frame_processor_.setFrameDescriptor(new Calonder(calonder_trees_file));
00109
00110
00111 while (ros::ok() && !point_head_client_.waitForServer(ros::Duration(5.0))) {
00112 ROS_INFO("Waiting for the point_head_action server to come up");
00113 }
00114
00115
00116 ros::NodeHandle mux_nh("mux");
00117 mux_client_ = mux_nh.serviceClient<topic_tools::MuxSelect>("select");
00118
00119 pr_server_ = nh_.advertiseService("recognize_place", &KidnappedNode::localizeCb, this);
00120
00121
00122 localization_sub_ = nh_.subscribe("map_pose", 0, &KidnappedNode::poseCb, this);
00123
00124 sync_.connectInput(l_image_sub_, l_info_sub_, r_image_sub_, r_info_sub_);
00125 sync_.registerCallback( boost::bind(&KidnappedNode::imageCb, this, _1, _2, _3, _4) );
00126
00127
00128 image_sample_pub_ = it_->advertise("/image_samples", 1);
00129 image_match_pub_ = it_->advertise("/pr_matches", 1);
00130 initial_pose_pub_ = nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("/initialpose", 1);
00131 initial_pose_viz_pub_ = nh_.advertise<geometry_msgs::PoseStamped>("place_localized_pose", 1);
00132
00133 sample_pose_pub_ = nh_.advertise<geometry_msgs::PoseArray>("place_poses", 1, true);
00134 loadDbSamplePoses();
00135 publishSamplePoses();
00136 }
00137
00138 void loadDbSamplePoses()
00139 {
00140 sqlite3 *db = place_db_.getSqlite();
00141 static const char SELECT_IDS[] = "SELECT id FROM places";
00142 sqlite3_stmt *stmt = NULL;
00143 assert(sqlite3_prepare_v2(db, SELECT_IDS, sizeof(SELECT_IDS), &stmt, NULL) == SQLITE_OK);
00144
00145 tf::Pose base_in_map;
00146 tf::Transform camera_in_base;
00147 int err;
00148 while ((err = sqlite3_step(stmt)) == SQLITE_ROW) {
00149 int64_t id = sqlite3_column_int64(stmt, 0);
00150 place_db_.getTransforms(id, base_in_map, camera_in_base);
00151 addToSamplePoses(base_in_map, camera_in_base);
00152 }
00153 assert(err == SQLITE_DONE);
00154
00155 err = sqlite3_finalize(stmt);
00156 assert(err == SQLITE_OK);
00157 }
00158
00159 void publishSamplePoses()
00160 {
00161 sample_poses_.header.stamp = ros::Time::now();
00162 sample_poses_.header.frame_id = map_frame_;
00163 sample_pose_pub_.publish(sample_poses_);
00164 }
00165
00166 void subscribeStereo()
00167 {
00168 l_image_sub_.subscribe(*it_, "left/image_rect", 1);
00169 l_info_sub_ .subscribe(stereo_nh_, "left/camera_info", 1);
00170 r_image_sub_.subscribe(*it_, "right/image_rect", 1);
00171 r_info_sub_ .subscribe(stereo_nh_, "right/camera_info", 1);
00172 }
00173
00174 void unsubscribeStereo()
00175 {
00176 l_image_sub_.unsubscribe();
00177 l_info_sub_ .unsubscribe();
00178 r_image_sub_.unsubscribe();
00179 r_info_sub_ .unsubscribe();
00180 }
00181
00183 void lookAt(double x, double y, double z)
00184 {
00185
00186 pr2_controllers_msgs::PointHeadGoal goal;
00187
00188
00189 geometry_msgs::PointStamped point;
00190 point.header.frame_id = target_frame_;
00191 point.point.x = x; point.point.y = y; point.point.z = z;
00192 goal.target = point;
00193
00194
00195 goal.pointing_frame = camera_optical_frame_;
00196 goal.pointing_axis.x = 0.0;
00197 goal.pointing_axis.y = 0.0;
00198 goal.pointing_axis.z = 1.0;
00199
00200
00201
00202
00203
00204 point_head_client_.sendGoal(goal);
00205
00206
00207 point_head_client_.waitForResult();
00208
00209 if (point_head_client_.getState() != actionlib::SimpleClientGoalState::SUCCEEDED)
00210 ROS_WARN("lookAt failed!");
00211 }
00212
00213 void poseCb(const geometry_msgs::PoseWithCovarianceStamped& pose_estimate)
00214 {
00215 ROS_DEBUG("Got a map pose estimate");
00216
00217
00218 const double MAX_VARIANCE = 0.05;
00219 double xx = pose_estimate.pose.covariance[0], yy = pose_estimate.pose.covariance[7];
00220 if (xx > MAX_VARIANCE || yy > MAX_VARIANCE) {
00221 ROS_INFO("Reject map pose estimate due to high variance: xx = %f, yy = %f", xx, yy);
00222 return;
00223 }
00224
00225
00226 cv::Rect_<double> bounding_box;
00227 bounding_box.x = pose_estimate.pose.pose.position.x - distance_threshold_ / 2.0;
00228 bounding_box.y = pose_estimate.pose.pose.position.y - distance_threshold_ / 2.0;
00229 bounding_box.width = bounding_box.height = distance_threshold_;
00230 std::vector<int64_t> ids;
00231 place_db_.findInRegion(bounding_box, ids);
00232 if (ids.size() > 0) {
00233 ROS_INFO("Ignoring pose estimate, already have %d records nearby", (int)ids.size());
00234 #if 0
00235 printf("\tids: ");
00236 for (int i = 0; i < ids.size(); ++i)
00237 printf("%li ", ids[i]);
00238 printf("\n");
00239 #endif
00240 return;
00241 }
00242
00243 collect_new_data_ = true;
00244 collectImages(num_samples_);
00245 }
00246
00248 void collectImages(int n)
00249 {
00250
00251 topic_tools::MuxSelect select_srv;
00252 select_srv.request.topic = "__none";
00253 if (!mux_client_.call(select_srv)) {
00254 ROS_ERROR("Failed to call select service %s on mux. Are you sure that it is up and connected correctly?",
00255 mux_client_.getService().c_str());
00256 return;
00257 }
00258
00259
00260 take_sample_ = false;
00261 subscribeStereo();
00262 spinner_->start();
00263
00264 const double BLIND_ANGLE = M_PI / 8.0;
00265
00266
00267 double step = 2.0 * (M_PI - BLIND_ANGLE) / (n - 1);
00268 double angle = -(M_PI - BLIND_ANGLE);
00269 for (int i = 0; i < n; ++i) {
00270 double x = 3.0 * std::cos(angle);
00271 double y = 3.0 * std::sin(angle);
00272 ROS_DEBUG("Looking at (%f, %f, %f)", x, y, head_target_z_);
00273 lookAt(x, y, head_target_z_);
00274
00275 angle += step;
00276 ros::Duration(1.0).sleep();
00277
00278 boost::unique_lock<boost::mutex> lock(sample_mutex_);
00279 take_sample_ = true;
00280 while (take_sample_) {
00281 sample_cond_.wait(lock);
00282 }
00283 }
00284
00285 spinner_->stop();
00286
00287
00288 lookAt(3.0, 0.0, head_target_z_);
00289
00290
00291 select_srv.request.topic = select_srv.response.prev_topic;
00292 if (!mux_client_.call(select_srv))
00293 ROS_WARN("Failed to reset mux to previous topic");
00294
00295 publishSamplePoses();
00296 }
00297
00298 void imageCb(const sensor_msgs::ImageConstPtr& l_image,
00299 const sensor_msgs::CameraInfoConstPtr& l_cam_info,
00300 const sensor_msgs::ImageConstPtr& r_image,
00301 const sensor_msgs::CameraInfoConstPtr& r_cam_info)
00302 {
00303 if (!take_sample_) {
00304 ROS_DEBUG("Not taking sample, seq = %u", l_cam_info->header.seq);
00305 return;
00306 }
00307 ROS_DEBUG("Taking sample, seq = %u", l_cam_info->header.seq);
00308
00309
00310 cv::Mat left, right;
00311 try {
00312 left = l_bridge_.imgMsgToCv(l_image, "mono8");
00313 right = r_bridge_.imgMsgToCv(r_image, "mono8");
00314 }
00315 catch (sensor_msgs::CvBridgeException& e) {
00316 ROS_ERROR("Conversion error: %s", e.what());
00317 return;
00318 }
00319 cam_model_.fromCameraInfo(l_cam_info, r_cam_info);
00320
00321 frame_common::CamParams cam_params;
00322 cam_params.fx = cam_model_.left().fx();
00323 cam_params.fy = cam_model_.left().fy();
00324 cam_params.cx = cam_model_.left().cx();
00325 cam_params.cy = cam_model_.left().cy();
00326 cam_params.tx = cam_model_.baseline();
00327
00328
00329 frame_common::Frame frame;
00330 frame.setCamParams(cam_params);
00331 frame_processor_.setStereoFrame(frame, left, right);
00332
00333 frame.imgRight = cv::Mat();
00334
00335
00336 if (image_sample_pub_.getNumSubscribers() > 0) {
00337 cv::Mat display;
00338 cv::drawKeypoints(left, frame.kpts, display, CV_RGB(255,0,0));
00339 IplImage display_ipl = display;
00340 image_sample_pub_.publish(l_bridge_.cvToImgMsg(&display_ipl, "bgr8"));
00341 }
00342
00343
00344 try {
00345 tf::StampedTransform camera_in_base;
00346 ros::Time cam_time = cam_model_.left().stamp();
00347 ros::Duration timeout(0.5);
00348 tf_.waitForTransform(target_frame_, cam_model_.tfFrame(), cam_time, timeout);
00349 tf_.lookupTransform(target_frame_, cam_model_.tfFrame(), cam_time, camera_in_base);
00350
00351 if (collect_new_data_) {
00353 tf::StampedTransform base_in_map;
00354 tf_.waitForTransform(map_frame_, target_frame_, cam_time, timeout);
00355 tf_.lookupTransform(map_frame_, target_frame_, cam_time, base_in_map);
00356
00357
00358 int64_t id = place_db_.add(cam_time, base_in_map, camera_in_base, frame);
00359 ROS_INFO("Added place %li with base in map (x,y) = (%f, %f)", id,
00360 base_in_map.getOrigin().x(), base_in_map.getOrigin().y());
00361
00362 addToSamplePoses(base_in_map, camera_in_base);
00363 }
00364 else {
00365
00366 const size_t N = 10;
00367 vt::Matches matches;
00368 place_db_.findMatching(frame, N, matches);
00369
00370 printf("Matches (id, score, inliers):\n");
00371 for (int i = 0; i < (int)matches.size(); ++i) {
00372 int64_t id = matches[i].id;
00373 frame_common::Frame db_frame;
00374 place_db_.getFrame(id, db_frame);
00375 int inliers = pose_estimator_.estimate(db_frame, frame);
00376 printf("\t%li\t%f\t%i\n", id, matches[i].score, inliers);
00377
00378 if (inliers > best_inliers_ && inliers > pr_inliers_) {
00379 tf::Pose db_base_in_map;
00380 tf::Transform db_camera_in_base;
00381 place_db_.getTransforms(id, db_base_in_map, db_camera_in_base);
00382
00383
00384 tf::Transform pe_transform;
00385 Eigen::Matrix3d rot = pose_estimator_.rot;
00386 pe_transform.getBasis().setValue(rot(0,0), rot(0,1), rot(0,2),
00387 rot(1,0), rot(1,1), rot(1,2),
00388 rot(2,0), rot(2,1), rot(2,2));
00389 Eigen::Vector3d trans = pose_estimator_.trans;
00390 pe_transform.getOrigin().setValue(trans[0], trans[1], trans[2]);
00392 tf::Pose map_pose = db_base_in_map * db_camera_in_base * pe_transform * camera_in_base.inverse();
00393 best_pose_.setData(map_pose);
00394
00395 best_pose_.frame_id_ = map_frame_;
00396 best_pose_.stamp_ = cam_time;
00397 best_inliers_ = inliers;
00398
00399
00400 if (image_match_pub_.getNumSubscribers() > 0) {
00401 cv::Mat display, left_db, right_db;
00402 place_db_.getImages(id, left_db, right_db);
00403 cv::drawMatches(left_db, db_frame.kpts, left, frame.kpts, pose_estimator_.inliers, display);
00404 IplImage display_ipl = display;
00405 image_match_pub_.publish(l_bridge_.cvToImgMsg(&display_ipl, "bgr8"));
00406 }
00407 }
00408 }
00409 }
00410 }
00411 catch (tf::TransformException& ex) {
00412 ROS_WARN("TF exception:\n%s", ex.what());
00413 return;
00414 }
00415
00416 {
00417 boost::lock_guard<boost::mutex> lock(sample_mutex_);
00418 take_sample_ = false;
00419 }
00420 sample_cond_.notify_one();
00421 }
00422
00423 void addToSamplePoses(const tf::Transform& base_in_map, const tf::Transform& camera_in_base)
00424 {
00425 tf::Transform camera_in_map = base_in_map * camera_in_base;
00426
00427 double yaw, pitch, roll;
00428 camera_in_map.getBasis().getEulerYPR(yaw, pitch, roll);
00429 yaw += M_PI/2;
00430 camera_in_map.getBasis().setEulerYPR(yaw, pitch, roll);
00431 geometry_msgs::Pose pose;
00432 tf::poseTFToMsg(camera_in_map, pose);
00433 sample_poses_.poses.push_back(pose);
00434 }
00435
00436 bool localizeCb(kidnapped_robot::RecognizePlace::Request& request, kidnapped_robot::RecognizePlace::Response& response)
00437 {
00438 collect_new_data_ = false;
00439 best_inliers_ = 0;
00440 collectImages(num_samples_);
00441
00442 if (best_inliers_ < pr_inliers_) {
00443 ROS_WARN("Localization failed: best match had only %i inliers, need %i", best_inliers_, pr_inliers_);
00444 return false;
00445 }
00446
00447 tf::poseStampedTFToMsg(best_pose_, response.pose);
00448 response.inliers = best_inliers_;
00449
00450
00451 geometry_msgs::PoseWithCovarianceStamped pwcs;
00452 pwcs.header = response.pose.header;
00453 pwcs.pose.pose = response.pose.pose;
00454 pwcs.pose.covariance[0] = pwcs.pose.covariance[7] = 0.1;
00455 pwcs.pose.covariance[1] = pwcs.pose.covariance[6] = 0.01;
00456 pwcs.pose.covariance[27] = 0.1;
00457 initial_pose_pub_.publish(pwcs);
00458
00459 geometry_msgs::PoseStamped ps;
00460 ps.header = pwcs.header;
00461 ps.pose = pwcs.pose.pose;
00462 initial_pose_viz_pub_.publish(ps);
00463
00464 return true;
00465 }
00466 };
00467
00468 int main(int argc, char **argv)
00469 {
00470 ros::init(argc, argv, "kidnapped_localization_node");
00471
00472 if (argc < 5) {
00473 printf("Usage: %s places.db vocab.tree vocab.weights calonder.rtc\n", argv[0]);
00474 return 1;
00475 }
00476
00477 KidnappedNode node(argv[1], argv[2], argv[3], argv[4]);
00478
00479 ros::spin();
00480 }