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
00031
00032
00033 #include <ros/ros.h>
00034
00035 #include <sensor_msgs/LaserScan.h>
00036 #include <sensor_msgs/PointCloud.h>
00037 #include <nav_msgs/Odometry.h>
00038
00039 #include <tf/transform_listener.h>
00040
00041 #include <mrpt_bridge/pose.h>
00042 #include <mrpt_bridge/laser_scan.h>
00043 #include <mrpt_bridge/point_cloud.h>
00044
00045 #include <map>
00046
00047 #include <mrpt/version.h>
00048 #include <mrpt/obs/CSensoryFrame.h>
00049 #include <mrpt/maps/CSimplePointsMap.h>
00050 #include <mrpt/maps/COccupancyGridMap2D.h>
00051 #include <mrpt/obs/CObservation2DRangeScan.h>
00052 using namespace mrpt::maps;
00053 using namespace mrpt::obs;
00054
00055 #include <mrpt/system/string_utils.h>
00056 #include <mrpt/gui/CDisplayWindow3D.h>
00057 #include <mrpt/opengl/CGridPlaneXY.h>
00058 #include <mrpt/opengl/CPointCloud.h>
00059 #include <mrpt/opengl/stock_objects.h>
00060
00061 #include <mrpt/version.h>
00062 #if MRPT_VERSION>=0x199
00063 #include <mrpt/system/CTimeLogger.h>
00064 #include <mrpt/config/CConfigFile.h>
00065 using namespace mrpt::system;
00066 using namespace mrpt::config;
00067 using namespace mrpt::img;
00068 #else
00069 #include <mrpt/utils/CTimeLogger.h>
00070 #include <mrpt/utils/CConfigFile.h>
00071 using namespace mrpt::utils;
00072 #endif
00073
00074
00075
00076 class LocalObstaclesNode
00077 {
00078 private:
00079 struct TAuxInitializer
00080 {
00081 TAuxInitializer(int argc, char** argv)
00082 {
00083 ros::init(argc, argv, "mrpt_local_obstacles_node");
00084 }
00085 };
00086
00087 CTimeLogger m_profiler;
00088
00089 TAuxInitializer m_auxinit;
00090 ros::NodeHandle m_nh;
00091 ros::NodeHandle m_localn;
00092 bool m_show_gui;
00093 std::string m_frameid_reference;
00094 std::string m_frameid_robot;
00095 std::string
00096 m_topic_local_map_pointcloud;
00097 std::string m_source_topics_2dscan;
00098 double m_time_window;
00099
00100 double m_publish_period;
00101
00102
00103 ros::Timer m_timer_publish;
00104
00105
00106 struct TInfoPerTimeStep
00107 {
00108 CObservation::Ptr observation;
00109 mrpt::poses::CPose3D robot_pose;
00110 };
00111 typedef std::multimap<double, TInfoPerTimeStep> TListObservations;
00112 TListObservations m_hist_obs;
00113
00114 boost::mutex m_hist_obs_mtx;
00115
00117 CSimplePointsMap m_localmap_pts;
00118
00119
00120 mrpt::gui::CDisplayWindow3D::Ptr m_gui_win;
00121
00124 ros::Publisher m_pub_local_map_pointcloud;
00125 std::vector<ros::Subscriber>
00126 m_subs_2dlaser;
00127 tf::TransformListener m_tf_listener;
00128
00136 template <typename CALLBACK_METHOD_TYPE>
00137 size_t subscribeToMultipleTopics(
00138 const std::string& lstTopics, std::vector<ros::Subscriber>& subs,
00139 CALLBACK_METHOD_TYPE cb)
00140 {
00141 std::vector<std::string> lstSources;
00142 mrpt::system::tokenize(lstTopics, " ,\t\n", lstSources);
00143 subs.resize(lstSources.size());
00144 for (size_t i = 0; i < lstSources.size(); i++)
00145 subs[i] = m_nh.subscribe(lstSources[i], 1, cb, this);
00146 return lstSources.size();
00147 }
00148
00151 void onNewSensor_Laser2D(const sensor_msgs::LaserScanConstPtr& scan)
00152 {
00153 CTimeLoggerEntry tle(m_profiler, "onNewSensor_Laser2D");
00154
00155
00156 tf::StampedTransform sensorOnRobot;
00157 try
00158 {
00159 CTimeLoggerEntry tle2(
00160 m_profiler, "onNewSensor_Laser2D.lookupTransform_sensor");
00161 m_tf_listener.lookupTransform(
00162 m_frameid_robot, scan->header.frame_id, ros::Time(0),
00163 sensorOnRobot);
00164 }
00165 catch (tf::TransformException& ex)
00166 {
00167 ROS_ERROR("%s", ex.what());
00168 return;
00169 }
00170
00171
00172 mrpt::poses::CPose3D sensorOnRobot_mrpt;
00173 mrpt_bridge::convert(sensorOnRobot, sensorOnRobot_mrpt);
00174
00175
00176 auto obsScan = CObservation2DRangeScan::Create();
00177 mrpt_bridge::convert(*scan, sensorOnRobot_mrpt, *obsScan);
00178
00179 ROS_DEBUG(
00180 "[onNewSensor_Laser2D] %u rays, sensor pose on robot %s",
00181 static_cast<unsigned int>(obsScan->scan.size()),
00182 sensorOnRobot_mrpt.asString().c_str());
00183
00184
00185 const double timestamp = scan->header.stamp.toSec();
00186
00187
00188
00189 mrpt::poses::CPose3D robotPose;
00190 try
00191 {
00192 CTimeLoggerEntry tle3(
00193 m_profiler, "onNewSensor_Laser2D.lookupTransform_robot");
00194 tf::StampedTransform tx;
00195
00196 try
00197 {
00198 m_tf_listener.lookupTransform(
00199 m_frameid_reference, m_frameid_robot, scan->header.stamp,
00200 tx);
00201 }
00202 catch (tf::ExtrapolationException&)
00203 {
00204
00205
00206 m_tf_listener.lookupTransform(
00207 m_frameid_reference, m_frameid_robot, ros::Time(0), tx);
00208 }
00209 mrpt_bridge::convert(tx, robotPose);
00210 ROS_DEBUG(
00211 "[onNewSensor_Laser2D] robot pose %s",
00212 robotPose.asString().c_str());
00213 }
00214 catch (tf::TransformException& ex)
00215 {
00216 ROS_ERROR("%s", ex.what());
00217 return;
00218 }
00219
00220
00221 TInfoPerTimeStep ipt;
00222 ipt.observation = obsScan;
00223 ipt.robot_pose = robotPose;
00224
00225 m_hist_obs_mtx.lock();
00226 m_hist_obs.insert(
00227 m_hist_obs.end(), TListObservations::value_type(timestamp, ipt));
00228 m_hist_obs_mtx.unlock();
00229
00230 }
00231
00233 void onDoPublish(const ros::TimerEvent&)
00234 {
00235 CTimeLoggerEntry tle(m_profiler, "onDoPublish");
00236
00237
00238 TListObservations obs;
00239 {
00240 CTimeLoggerEntry tle(
00241 m_profiler, "onDoPublish.removingOld");
00242 m_hist_obs_mtx.lock();
00243
00244
00245 if (!m_hist_obs.empty())
00246 {
00247 const double last_time = m_hist_obs.rbegin()->first;
00248 TListObservations::iterator it_first_valid =
00249 m_hist_obs.lower_bound(last_time - m_time_window);
00250 const size_t nToRemove =
00251 std::distance(m_hist_obs.begin(), it_first_valid);
00252 ROS_DEBUG(
00253 "[onDoPublish] Removing %u old entries, last_time=%lf",
00254 static_cast<unsigned int>(nToRemove), last_time);
00255 m_hist_obs.erase(m_hist_obs.begin(), it_first_valid);
00256 }
00257
00258 obs = m_hist_obs;
00259 m_hist_obs_mtx.unlock();
00260 }
00261
00262 ROS_DEBUG(
00263 "Building local map with %u observations.",
00264 static_cast<unsigned int>(obs.size()));
00265 if (obs.empty()) return;
00266
00267
00268
00269 m_localmap_pts.clear();
00270 mrpt::poses::CPose3D curRobotPose;
00271 {
00272 CTimeLoggerEntry tle2(
00273 m_profiler, "onDoPublish.buildLocalMap");
00274
00275
00276
00277
00278 try
00279 {
00280 tf::StampedTransform tx;
00281 m_tf_listener.lookupTransform(
00282 m_frameid_reference, m_frameid_robot, ros::Time(0), tx);
00283 mrpt_bridge::convert(tx, curRobotPose);
00284 ROS_DEBUG(
00285 "[onDoPublish] Building local map relative to latest robot "
00286 "pose: %s",
00287 curRobotPose.asString().c_str());
00288 }
00289 catch (tf::TransformException& ex)
00290 {
00291 ROS_ERROR("%s", ex.what());
00292 return;
00293 }
00294
00295
00296
00297 for (TListObservations::const_iterator it = obs.begin();
00298 it != obs.end(); ++it)
00299 {
00300 const TInfoPerTimeStep& ipt = it->second;
00301
00302
00303 mrpt::poses::CPose3D relPose(mrpt::poses::UNINITIALIZED_POSE);
00304 relPose.inverseComposeFrom(ipt.robot_pose, curRobotPose);
00305
00306
00307 m_localmap_pts.insertObservationPtr(ipt.observation, &relPose);
00308
00309 }
00310 }
00311
00312
00313 if (m_pub_local_map_pointcloud.getNumSubscribers() > 0)
00314 {
00315 sensor_msgs::PointCloudPtr msg_pts =
00316 sensor_msgs::PointCloudPtr(new sensor_msgs::PointCloud);
00317 msg_pts->header.frame_id = m_frameid_robot;
00318 msg_pts->header.stamp = ros::Time(obs.rbegin()->first);
00319 mrpt_bridge::point_cloud::mrpt2ros(
00320 m_localmap_pts, msg_pts->header, *msg_pts);
00321 m_pub_local_map_pointcloud.publish(msg_pts);
00322 }
00323
00324
00325 if (m_show_gui)
00326 {
00327 if (!m_gui_win)
00328 {
00329 m_gui_win = mrpt::gui::CDisplayWindow3D::Create(
00330 "LocalObstaclesNode", 800, 600);
00331 mrpt::opengl::COpenGLScene::Ptr& scene =
00332 m_gui_win->get3DSceneAndLock();
00333 scene->insert(mrpt::opengl::CGridPlaneXY::Create());
00334 scene->insert(
00335 mrpt::opengl::stock_objects::CornerXYZSimple(1.0, 4.0));
00336
00337 auto gl_obs = mrpt::opengl::CSetOfObjects::Create();
00338 gl_obs->setName("obstacles");
00339 scene->insert(gl_obs);
00340
00341 auto gl_pts = mrpt::opengl::CPointCloud::Create();
00342 gl_pts->setName("points");
00343 gl_pts->setPointSize(2.0);
00344 gl_pts->setColor_u8(TColor(0x0000ff));
00345 scene->insert(gl_pts);
00346
00347 m_gui_win->unlockAccess3DScene();
00348 }
00349
00350 auto& scene = m_gui_win->get3DSceneAndLock();
00351 auto gl_obs = mrpt::ptr_cast<mrpt::opengl::CSetOfObjects>::from(
00352 scene->getByName("obstacles"));
00353 ROS_ASSERT(!!gl_obs);
00354 gl_obs->clear();
00355
00356 auto gl_pts = mrpt::ptr_cast<mrpt::opengl::CPointCloud>::from(
00357 scene->getByName("points"));
00358
00359 for (const auto &o :obs)
00360 {
00361 const TInfoPerTimeStep& ipt = o.second;
00362
00363 mrpt::poses::CPose3D relPose(mrpt::poses::UNINITIALIZED_POSE);
00364 relPose.inverseComposeFrom(ipt.robot_pose, curRobotPose);
00365
00366 mrpt::opengl::CSetOfObjects::Ptr gl_axis =
00367 mrpt::opengl::stock_objects::CornerXYZSimple(0.9, 2.0);
00368 gl_axis->setPose(relPose);
00369 gl_obs->insert(gl_axis);
00370 }
00371
00372 gl_pts->loadFromPointsMap(&m_localmap_pts);
00373
00374 m_gui_win->unlockAccess3DScene();
00375 m_gui_win->repaint();
00376 }
00377
00378 }
00379
00380 public:
00382 LocalObstaclesNode(int argc, char** argv)
00383 : m_auxinit(argc, argv),
00384 m_nh(),
00385 m_localn("~"),
00386 m_show_gui(true),
00387 m_frameid_reference("odom"),
00388 m_frameid_robot("base_link"),
00389 m_topic_local_map_pointcloud("local_map_pointcloud"),
00390 m_source_topics_2dscan("scan,laser1"),
00391 m_time_window(0.2),
00392 m_publish_period(0.05)
00393 {
00394
00395 m_localn.param("show_gui", m_show_gui, m_show_gui);
00396 m_localn.param(
00397 "frameid_reference", m_frameid_reference, m_frameid_reference);
00398 m_localn.param("frameid_robot", m_frameid_robot, m_frameid_robot);
00399 m_localn.param(
00400 "topic_local_map_pointcloud", m_topic_local_map_pointcloud,
00401 m_topic_local_map_pointcloud);
00402 m_localn.param(
00403 "source_topics_2dscan", m_source_topics_2dscan,
00404 m_source_topics_2dscan);
00405 m_localn.param("time_window", m_time_window, m_time_window);
00406 m_localn.param("publish_period", m_publish_period, m_publish_period);
00407
00408 ROS_ASSERT(m_time_window > m_publish_period);
00409 ROS_ASSERT(m_publish_period > 0);
00410
00411
00412 m_pub_local_map_pointcloud = m_nh.advertise<sensor_msgs::PointCloud>(
00413 m_topic_local_map_pointcloud, 10);
00414
00415
00416
00417 size_t nSubsTotal = 0;
00418 nSubsTotal += this->subscribeToMultipleTopics(
00419 m_source_topics_2dscan, m_subs_2dlaser,
00420 &LocalObstaclesNode::onNewSensor_Laser2D);
00421
00422 ROS_INFO(
00423 "Total number of sensor subscriptions: %u\n",
00424 static_cast<unsigned int>(nSubsTotal));
00425 ROS_ASSERT_MSG(
00426 nSubsTotal > 0,
00427 "*Error* It is mandatory to set at least one source topic for "
00428 "sensory information!");
00429
00430
00431 m_localmap_pts.insertionOptions.minDistBetweenLaserPoints = 0;
00432 m_localmap_pts.insertionOptions.also_interpolate = false;
00433
00434
00435 m_timer_publish = m_nh.createTimer(
00436 ros::Duration(m_publish_period), &LocalObstaclesNode::onDoPublish,
00437 this);
00438
00439 }
00440
00441 };
00442
00443 int main(int argc, char** argv)
00444 {
00445 LocalObstaclesNode the_node(argc, argv);
00446 ros::spin();
00447 return 0;
00448 }