00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00040 #define BOOST_NO_HASH
00041 #include <flirtlib_ros/flirtlib.h>
00042
00043 #include <flirtlib_ros/conversions.h>
00044 #include <occupancy_grid_utils/file.h>
00045 #include <occupancy_grid_utils/ray_tracer.h>
00046 #include <visualization_msgs/Marker.h>
00047 #include <mongo_ros/message_collection.h>
00048 #include <ros/ros.h>
00049 #include <tf/transform_listener.h>
00050 #include <boost/thread.hpp>
00051 #include <boost/foreach.hpp>
00052 #include <boost/lexical_cast.hpp>
00053 #include <boost/optional.hpp>
00054
00055 namespace flirtlib_ros
00056 {
00057
00058 namespace sm=sensor_msgs;
00059 namespace gu=occupancy_grid_utils;
00060 namespace vm=visualization_msgs;
00061 namespace gm=geometry_msgs;
00062 namespace mr=mongo_ros;
00063 namespace nm=nav_msgs;
00064
00065 using std::string;
00066 using std::vector;
00067 using boost::shared_ptr;
00068
00069 typedef boost::mutex::scoped_lock Lock;
00070 typedef vector<InterestPoint*> InterestPointVec;
00071 typedef std::pair<InterestPoint*, InterestPoint*> Correspondence;
00072 typedef vector<Correspondence> Correspondences;
00073 typedef vector<RefScan> RefScans;
00074
00075
00076
00077
00078
00079
00080 class Node
00081 {
00082 public:
00083
00084 Node ();
00085 void mainLoop (const ros::TimerEvent& e);
00086 void scanCB (sm::LaserScan::ConstPtr scan);
00087
00088 private:
00089
00090 RefScans generateRefScans () const;
00091 void initializeRefScans();
00092 gm::Pose getPose();
00093
00094
00095 boost::mutex mutex_;
00096 ros::NodeHandle nh_;
00097
00098
00099 const string scan_db_;
00100 const unsigned num_matches_required_;
00101
00102
00103 sm::LaserScan::ConstPtr scan_;
00104 RefScans ref_scans_;
00105 boost::optional<gm::Pose> last_pose_;
00106
00108 shared_ptr<SimpleMinMaxPeakFinder> peak_finder_;
00109 shared_ptr<HistogramDistance<double> > histogram_dist_;
00110 shared_ptr<Detector> detector_;
00111 shared_ptr<DescriptorGenerator> descriptor_;
00112 shared_ptr<RansacFeatureSetMatcher> ransac_;
00113
00114
00115 tf::TransformListener tf_;
00116 ros::Subscriber scan_sub_;
00117 ros::Publisher marker_pub_;
00118 ros::Timer exec_timer_;
00119
00120 };
00121
00122
00123
00124
00125
00126
00127 template <class T>
00128 T getPrivateParam (const string& name)
00129 {
00130 ros::NodeHandle nh("~");
00131 T val;
00132 const bool found = nh.getParam(name, val);
00133 ROS_ASSERT_MSG (found, "Could not find parameter %s", name.c_str());
00134 ROS_DEBUG_STREAM_NAMED ("init", "Initialized " << name << " to " << val);
00135 return val;
00136 }
00137
00138 template <class T>
00139 T getPrivateParam (const string& name, const T& default_val)
00140 {
00141 ros::NodeHandle nh("~");
00142 T val;
00143 nh.param(name, val, default_val);
00144 ROS_DEBUG_STREAM_NAMED ("init", "Initialized " << name << " to " << val <<
00145 "(default was " << default_val << ")");
00146 return val;
00147 }
00148
00149 sm::LaserScan getScannerParams ()
00150 {
00151 sm::LaserScan params;
00152 const double PI = 3.14159265;
00153 params.angle_min = -PI/2;
00154 params.angle_max = PI/2;
00155 params.angle_increment = PI/180;
00156 params.range_max = 10;
00157
00158 return params;
00159 }
00160
00161 SimpleMinMaxPeakFinder* createPeakFinder ()
00162 {
00163 return new SimpleMinMaxPeakFinder(0.34, 0.001);
00164 }
00165
00166 Detector* createDetector (SimpleMinMaxPeakFinder* peak_finder)
00167 {
00168 const double scale = 5.0;
00169 const double dmst = 2.0;
00170 const double base_sigma = 0.2;
00171 const double sigma_step = 1.4;
00172 CurvatureDetector* det = new CurvatureDetector(peak_finder, scale, base_sigma,
00173 sigma_step, dmst);
00174 det->setUseMaxRange(false);
00175 return det;
00176 }
00177
00178 DescriptorGenerator* createDescriptor (HistogramDistance<double>* dist)
00179 {
00180 const double min_rho = 0.02;
00181 const double max_rho = 0.5;
00182 const double bin_rho = 4;
00183 const double bin_phi = 12;
00184 BetaGridGenerator* gen = new BetaGridGenerator(min_rho, max_rho, bin_rho,
00185 bin_phi);
00186 gen->setDistanceFunction(dist);
00187 return gen;
00188 }
00189
00190 Node::Node () :
00191 scan_db_(getPrivateParam<string>("scan_db", "")),
00192 num_matches_required_(getPrivateParam<int>("num_matches_required", 10)),
00193
00194 peak_finder_(createPeakFinder()),
00195 histogram_dist_(new SymmetricChi2Distance<double>()),
00196 detector_(createDetector(peak_finder_.get())),
00197 descriptor_(createDescriptor(histogram_dist_.get())),
00198 ransac_(new RansacFeatureSetMatcher(0.0599, 0.95, 0.4, 0.4,
00199 0.0384, false)),
00200
00201 scan_sub_(nh_.subscribe("scan0", 10, &Node::scanCB, this)),
00202 marker_pub_(nh_.advertise<vm::Marker>("visualization_marker", 10)),
00203 exec_timer_(nh_.createTimer(ros::Duration(0.2), &Node::mainLoop, this))
00204 {
00205 initializeRefScans();
00206 }
00207
00208
00209
00210
00211
00212
00213 RefScans Node::generateRefScans() const
00214 {
00215
00216 const string map_file = getPrivateParam<string>("map_file");
00217 const double resolution = getPrivateParam<double>("resolution");
00218 const sm::LaserScan scanner_params = getScannerParams();
00219 const double ref_pos_resolution = getPrivateParam<double>("ref_pos_resolution");
00220 const double ref_angle_resolution = getPrivateParam<double>("ref_angle_resolution");
00221 const double x0 = getPrivateParam<double>("x0", -1e9);
00222 const double x1 = getPrivateParam<double>("x1", 1e9);
00223 const double y0 = getPrivateParam<double>("y0", -1e9);
00224 const double y1 = getPrivateParam<double>("y1", 1e9);
00225
00226
00227 nm::OccupancyGrid::ConstPtr grid = gu::loadGrid(map_file, resolution);
00228 const unsigned skip = ref_pos_resolution/grid->info.resolution;
00229
00230 RefScans ref_scans;
00231
00232 for (unsigned x=0; x<grid->info.width; x+=skip)
00233 {
00234 for (unsigned y=0; y<grid->info.height; y+=skip)
00235 {
00236 for (double theta=0; theta<6.28; theta+=ref_angle_resolution)
00237 {
00238
00239 gm::Pose pose;
00240 const gu::Cell c(x,y);
00241 pose.position = gu::cellCenter(grid->info, c);
00242 pose.orientation = tf::createQuaternionMsgFromYaw(theta);
00243 if ((pose.position.x > x1) || (pose.position.x < x0) ||
00244 (pose.position.y > y1) || (pose.position.y < y0))
00245 continue;
00246 if (grid->data[cellIndex(grid->info, c)]!=gu::UNOCCUPIED)
00247 continue;
00248 ROS_INFO_THROTTLE (0.3, "Pos: %.2f, %.2f", pose.position.x,
00249 pose.position.y);
00250
00251
00252 sm::LaserScan::Ptr scan = gu::simulateRangeScan(*grid, pose, scanner_params, true);
00253
00254
00255 shared_ptr<LaserReading> reading = fromRos(*scan);
00256 InterestPointVec pts;
00257 detector_->detect(*reading, pts);
00258 BOOST_FOREACH (InterestPoint* p, pts)
00259 {
00260 p->setDescriptor(descriptor_->describe(*p, *reading));
00261 }
00262
00263
00264 ref_scans.push_back(RefScan(scan, pose, pts));
00265 }
00266 }
00267 }
00268 ROS_INFO_STREAM ("Generated " << ref_scans.size() << " reference scans");
00269 return ref_scans;
00270 }
00271
00272
00273 void Node::initializeRefScans ()
00274 {
00275 if (scan_db_.size()>0)
00276 {
00277 mr::MessageCollection<RefScanRos> coll(scan_db_, "scans");
00278 if (coll.count() == 0)
00279 {
00280 ROS_INFO ("Didn't find any messages in %s/scans, so generating features.",
00281 scan_db_.c_str());
00282 ref_scans_ = generateRefScans();
00283 ROS_INFO ("Saving scans");
00284 BOOST_FOREACH (const RefScan& scan, ref_scans_)
00285 coll.insert(toRos(scan), mr::Metadata("x", scan.pose.position.x,
00286 "y", scan.pose.position.y));
00287 }
00288 else
00289 {
00290 ROS_INFO ("Loading scans from %s/scans", scan_db_.c_str());
00291 ROS_ASSERT(ref_scans_.size()==0);
00292 BOOST_FOREACH (const RefScanRos::ConstPtr m, coll.queryResults(mr::Query(), false))
00293 ref_scans_.push_back(fromRos(*m));
00294 }
00295 }
00296 else
00297 {
00298 ROS_INFO ("No db name provided; generating features from scratch.");
00299 ref_scans_ = generateRefScans();
00300 }
00301 }
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 void Node::scanCB (sm::LaserScan::ConstPtr scan)
00316 {
00317 Lock l(mutex_);
00318 scan_ = scan;
00319 }
00320
00321
00322
00323
00324
00325
00326
00327 gm::Pose Node::getPose ()
00328 {
00329 tf::StampedTransform trans;
00330 tf_.lookupTransform("/map", "pose0", ros::Time(), trans);
00331 gm::Pose pose;
00332 tf::poseTFToMsg(trans, pose);
00333 return pose;
00334 }
00335
00336 bool closeTo (const gm::Pose& p1, const gm::Pose& p2)
00337 {
00338 const double TOL=1e-3;
00339 const double dx = p1.position.x - p2.position.x;
00340 const double dy = p1.position.y - p2.position.y;
00341 const double dt = tf::getYaw(p1.orientation) - tf::getYaw(p2.orientation);
00342 return fabs(dx)<TOL && fabs(dy)<TOL && fabs(dt)<TOL;
00343 }
00344
00345 void Node::mainLoop (const ros::TimerEvent& e)
00346 {
00347 try
00348 {
00349
00350 const gm::Pose current_pose = getPose();
00351 const double theta = tf::getYaw(current_pose.orientation);
00352 const double x=current_pose.position.x;
00353 const double y=current_pose.position.y;
00354
00355 Lock l(mutex_);
00356 if (last_pose_ && closeTo(*last_pose_, current_pose))
00357 return;
00358 if (!scan_)
00359 return;
00360
00361 ROS_INFO("Matching scan at %.2f, %.2f, %.2f", x, y, theta);
00362
00363 InterestPointVec pts;
00364 shared_ptr<LaserReading> reading = fromRos(*scan_);
00365 detector_->detect(*reading, pts);
00366 BOOST_FOREACH (InterestPoint* p, pts)
00367 p->setDescriptor(descriptor_->describe(*p, *reading));
00368 marker_pub_.publish(interestPointMarkers(pts, current_pose, 0));
00369
00370
00371 vector<gm::Pose> match_poses;
00372 BOOST_FOREACH (const RefScan& ref_scan, ref_scans_)
00373 {
00374 Correspondences matches;
00375 OrientedPoint2D transform;
00376 ransac_->matchSets(ref_scan.raw_pts, pts, transform, matches);
00377 if (matches.size() > num_matches_required_)
00378 {
00379 ROS_INFO ("Found %zu matches with ref scan at %.2f, %.2f, %.2f. "
00380 "Current pose is %.2f, %.2f, %.2f.", matches.size(),
00381 ref_scan.pose.position.x, ref_scan.pose.position.y,
00382 tf::getYaw(ref_scan.pose.orientation), x, y, theta);
00383 match_poses.push_back(ref_scan.pose);
00384 }
00385 }
00386 ROS_INFO ("Done matching");
00387
00388 BOOST_FOREACH (const vm::Marker& m, poseMarkers(match_poses))
00389 marker_pub_.publish(m);
00390 last_pose_ = current_pose;
00391 }
00392
00393 catch (tf::TransformException& e)
00394 {
00395 ROS_INFO ("Skipping because of tf exception");
00396 }
00397 }
00398
00399
00400
00401
00402 }
00403
00404 int main (int argc, char** argv)
00405 {
00406 ros::init(argc, argv, "place_rec_test");
00407 flirtlib_ros::Node node;
00408 ros::spin();
00409 return 0;
00410 }