mrpt_local_obstacles_node.cpp
Go to the documentation of this file.
00001 /***********************************************************************************
00002  * Revised BSD License *
00003  * Copyright (c) 2014-2015, Jose-Luis Blanco <jlblanco@ual.es> *
00004  * All rights reserved. *
00005  *                                                                                 *
00006  * Redistribution and use in source and binary forms, with or without *
00007  * modification, are permitted provided that the following conditions are met: *
00008  *     * Redistributions of source code must retain the above copyright *
00009  *       notice, this list of conditions and the following disclaimer. *
00010  *     * Redistributions in binary form must reproduce the above copyright *
00011  *       notice, this list of conditions and the following disclaimer in the *
00012  *       documentation and/or other materials provided with the distribution. *
00013  *     * Neither the name of the Vienna University of Technology nor the *
00014  *       names of its contributors may be used to endorse or promote products *
00015  *       derived from this software without specific prior written permission. *
00016  *                                                                                 *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  *AND *
00019  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00020  **
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
00022  * DISCLAIMED. IN NO EVENT SHALL Markus Bader BE LIABLE FOR ANY *
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  **
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
00027  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
00028  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029  **
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
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 // The ROS node
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         // Sensor data:
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         // COccupancyGridMap2D m_localmap_grid;
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                 // Get the relative position of the sensor wrt the robot:
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                 // Convert data to MRPT format:
00172                 mrpt::poses::CPose3D sensorOnRobot_mrpt;
00173                 mrpt_bridge::convert(sensorOnRobot, sensorOnRobot_mrpt);
00174                 // In MRPT, CObservation2DRangeScan holds both: sensor data + relative
00175                 // pose:
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                 // Get sensor timestamp:
00185                 const double timestamp = scan->header.stamp.toSec();
00186 
00187                 // Get robot pose at that time in the reference frame, typ: /odom ->
00188                 // /base_link
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                                 // if we need a "too much " recent robot pose,be happy with the
00205                                 // latest one:
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                 // Insert into the observation history:
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         }  // end onNewSensor_Laser2D
00231 
00233         void onDoPublish(const ros::TimerEvent&)
00234         {
00235                 CTimeLoggerEntry tle(m_profiler, "onDoPublish");
00236 
00237                 // Purge old observations & latch a local copy:
00238                 TListObservations obs;
00239                 {
00240                         CTimeLoggerEntry tle(
00241                                 m_profiler, "onDoPublish.removingOld");
00242                         m_hist_obs_mtx.lock();
00243 
00244                         // Purge old obs:
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                         // Local copy in this thread:
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                 // Build local map(s):
00268                 // -----------------------------------------------
00269                 m_localmap_pts.clear();
00270                 mrpt::poses::CPose3D curRobotPose;
00271                 {
00272                         CTimeLoggerEntry tle2(
00273                                 m_profiler, "onDoPublish.buildLocalMap");
00274 
00275                         // Get the latest robot pose in the reference frame (typ: /odom ->
00276                         // /base_link)
00277                         // so we can build the local map RELATIVE to it:
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                         // For each observation: compute relative robot pose & insert obs
00296                         // into map:
00297                         for (TListObservations::const_iterator it = obs.begin();
00298                                  it != obs.end(); ++it)
00299                         {
00300                                 const TInfoPerTimeStep& ipt = it->second;
00301 
00302                                 // Relative pose in the past:
00303                                 mrpt::poses::CPose3D relPose(mrpt::poses::UNINITIALIZED_POSE);
00304                                 relPose.inverseComposeFrom(ipt.robot_pose, curRobotPose);
00305 
00306                                 // Insert obs:
00307                                 m_localmap_pts.insertObservationPtr(ipt.observation, &relPose);
00308 
00309                         }  // end for
00310                 }
00311 
00312                 // Publish them:
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                 // Show gui:
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                                 // Relative pose in the past:
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                         }  // end for
00371 
00372                         gl_pts->loadFromPointsMap(&m_localmap_pts);
00373 
00374                         m_gui_win->unlockAccess3DScene();
00375                         m_gui_win->repaint();
00376                 }
00377 
00378         }  // onDoPublish
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                 // Load params:
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                 // Init ROS publishers:
00412                 m_pub_local_map_pointcloud = m_nh.advertise<sensor_msgs::PointCloud>(
00413                         m_topic_local_map_pointcloud, 10);
00414 
00415                 // Init ROS subs:
00416                 // Subscribe to one or more laser sources:
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                 // Local map params:
00431                 m_localmap_pts.insertionOptions.minDistBetweenLaserPoints = 0;
00432                 m_localmap_pts.insertionOptions.also_interpolate = false;
00433 
00434                 // Init timers:
00435                 m_timer_publish = m_nh.createTimer(
00436                         ros::Duration(m_publish_period), &LocalObstaclesNode::onDoPublish,
00437                         this);
00438 
00439         }  // end ctor
00440 
00441 };  // end class
00442 
00443 int main(int argc, char** argv)
00444 {
00445         LocalObstaclesNode the_node(argc, argv);
00446         ros::spin();
00447         return 0;
00448 }


mrpt_local_obstacles
Author(s): Jose-Luis Blanco-Claraco
autogenerated on Thu Jun 6 2019 21:53:14