LaserScanner.cpp
Go to the documentation of this file.
00001 /*+-------------------------------------------------------------------------+
00002   |                       MultiVehicle simulator (libmvsim)                 |
00003   |                                                                         |
00004   | Copyright (C) 2014  Jose Luis Blanco Claraco (University of Almeria)    |
00005   | Copyright (C) 2017  Borys Tymchenko (Odessa Polytechnic University)     |
00006   | Distributed under GNU General Public License version 3                  |
00007   |   See <http://www.gnu.org/licenses/>                                    |
00008   +-------------------------------------------------------------------------+ */
00009 
00010 #include <mvsim/World.h>
00011 #include <mvsim/WorldElements/OccupancyGridMap.h>
00012 #include <mvsim/Sensors/LaserScanner.h>
00013 #include <mvsim/VehicleBase.h>
00014 #include <mrpt/opengl/COpenGLScene.h>
00015 #include <mrpt/random.h>
00016 
00017 #include "xml_utils.h"
00018 
00019 using namespace mvsim;
00020 using namespace rapidxml;
00021 
00022 int z_order_cnt = 0;
00023 
00024 LaserScanner::LaserScanner(
00025         VehicleBase& parent, const rapidxml::xml_node<char>* root)
00026         : SensorBase(parent),
00027           m_z_order(++z_order_cnt),
00028           m_rangeStdNoise(0.01),
00029           m_angleStdNoise(mrpt::utils::DEG2RAD(0.01)),
00030           m_see_fixtures(true)
00031 {
00032         this->loadConfigFrom(root);
00033 }
00034 
00035 LaserScanner::~LaserScanner() {}
00036 void LaserScanner::loadConfigFrom(const rapidxml::xml_node<char>* root)
00037 {
00038         m_gui_uptodate = false;
00039 
00040         // Attribs:
00041         std::map<std::string, TParamEntry> attribs;
00042         attribs["name"] = TParamEntry("%s", &this->m_name);
00043 
00044         parse_xmlnode_attribs(*root, attribs, "[LaserScanner]");
00045 
00046         // Other scalar params:
00047         int nRays = 181;
00048         double fov_deg = 180;
00049 
00050         std::map<std::string, TParamEntry> params;
00051         params["fov_degrees"] = TParamEntry("%lf", &fov_deg);
00052         params["nrays"] = TParamEntry("%i", &nRays);
00053         params["pose"] = TParamEntry("%pose2d_ptr3d", &m_scan_model.sensorPose);
00054         params["range_std_noise"] = TParamEntry("%lf", &m_rangeStdNoise);
00055         params["angle_std_noise_deg"] = TParamEntry("%lf_deg", &m_angleStdNoise);
00056         params["sensor_period"] = TParamEntry("%lf", &this->m_sensor_period);
00057         params["bodies_visible"] = TParamEntry("%bool", &this->m_see_fixtures);
00058 
00059         // Parse XML params:
00060         parse_xmlnode_children_as_param(*root, params);
00061 
00062         // Pass params to the scan2D obj:
00063         m_scan_model.aperture = mrpt::utils::DEG2RAD(fov_deg);
00064 #if MRPT_VERSION >= 0x150
00065         m_scan_model.resizeScan(nRays);
00066 #else
00067         m_scan_model.scan.resize(nRays);
00068         m_scan_model.validRange.resize(nRays);
00069 #endif
00070 
00071         // Assign a sensible default name/sensor label if none is provided:
00072         if (m_name.empty())
00073         {
00074                 const size_t nextIdx = m_vehicle.getSensors().size() + 1;
00075                 m_name = mrpt::format("laser%u", static_cast<unsigned int>(nextIdx));
00076         }
00077 }
00078 
00079 void LaserScanner::gui_update(mrpt::opengl::COpenGLScene& scene)
00080 {
00081         // 1st time?
00082         if (!m_gl_scan)
00083         {
00084                 m_gl_scan = mrpt::make_aligned_shared<mrpt::opengl::CPlanarLaserScan>();
00085                 m_gl_scan->setSurfaceColor(0.0f, 0.0f, 1.0f, 0.05f);
00086                 SCENE_INSERT_Z_ORDER(scene, 2, m_gl_scan);
00087         }
00088 
00089         if (!m_gui_uptodate)
00090         {
00091                 {
00092                         std::lock_guard<std::mutex> csl(m_last_scan_cs);
00093                         if (m_last_scan2gui)
00094                         {
00095                                 m_gl_scan->setScan(*m_last_scan2gui);
00096                                 m_last_scan2gui.reset();
00097                         }
00098                 }
00099                 m_gui_uptodate = true;
00100         }
00101 
00102         const double z_incrs = 10e-3;  // for m_z_order
00103         const double z_offset = 10e-2;
00104         const mrpt::poses::CPose2D& p = m_vehicle.getCPose2D();
00105         m_gl_scan->setPose(
00106                 mrpt::poses::CPose3D(
00107                         p.x(), p.y(), z_offset + z_incrs * m_z_order, p.phi(), 0.0, 0.0));
00108 }
00109 
00110 void LaserScanner::simul_pre_timestep(const TSimulContext& context) {}
00111 // Simulate sensor AFTER timestep, with the updated vehicle dynamical state:
00112 void LaserScanner::simul_post_timestep(const TSimulContext& context)
00113 {
00114         // Limit sensor rate:
00115         if (context.simul_time < m_sensor_last_timestamp + m_sensor_period) return;
00116 
00117         m_sensor_last_timestamp = context.simul_time;
00118 
00119         // Create an array of scans, each reflecting ranges to one kind of world
00120         // objects.
00121         // Finally, we'll take the shortest range in each direction:
00122         std::list<CObservation2DRangeScan> lstScans;
00123 
00124         const size_t nRays = m_scan_model.scan.size();
00125         const double maxRange = m_scan_model.maxRange;
00126 
00127         // Get pose of the robot:
00128         const mrpt::poses::CPose2D& vehPose = m_vehicle.getCPose2D();
00129 
00130         // grid maps:
00131         // -------------
00132         m_world->getTimeLogger().enter("LaserScanner.scan.1.gridmap");
00133 
00134         const World::TListWorldElements& elements =
00135                 m_world->getListOfWorldElements();
00136         for (World::TListWorldElements::const_iterator it = elements.begin();
00137                  it != elements.end(); ++it)
00138         {
00139                 // If not a grid map, ignore:
00140                 const OccupancyGridMap* grid =
00141                         dynamic_cast<const OccupancyGridMap*>(*it);
00142                 if (!grid) continue;
00143                 const COccupancyGridMap2D& occGrid = grid->getOccGrid();
00144 
00145                 // Create new scan:
00146                 lstScans.push_back(CObservation2DRangeScan(m_scan_model));
00147                 CObservation2DRangeScan& scan = lstScans.back();
00148 
00149                 // Ray tracing over the gridmap:
00150                 occGrid.laserScanSimulator(
00151                         scan, vehPose, 0.5f, m_scan_model.scan.size(), m_rangeStdNoise, 1,
00152                         m_angleStdNoise);
00153         }
00154         m_world->getTimeLogger().leave("LaserScanner.scan.1.gridmap");
00155 
00156         // ray trace on Box2D polygons:
00157         // ------------------------------
00158         m_world->getTimeLogger().enter("LaserScanner.scan.2.polygons");
00159         {
00160                 // Create new scan:
00161                 lstScans.push_back(CObservation2DRangeScan(m_scan_model));
00162                 CObservation2DRangeScan& scan = lstScans.back();
00163 
00164                 // Do Box2D raycasting stuff:
00165                 // ------------------------------
00166                 // This callback finds the closest hit. Polygon 0 is filtered.
00167                 class RayCastClosestCallback : public b2RayCastCallback
00168                 {
00169                    public:
00170                         RayCastClosestCallback() : m_see_fixtures(true), m_hit(false) {}
00171                         float32 ReportFixture(
00172                                 b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal,
00173                                 float32 fraction)
00174                         {
00175                                 if (!m_see_fixtures ||
00176                                         fixture->GetUserData() == INVISIBLE_FIXTURE_USER_DATA)
00177                                 {
00178                                         // By returning -1, we instruct the calling code to ignore
00179                                         // this fixture and
00180                                         // continue the ray-cast to the next fixture.
00181                                         return -1.0f;
00182                                 }
00183 
00184                                 m_hit = true;
00185                                 m_point = point;
00186                                 m_normal = normal;
00187                                 // By returning the current fraction, we instruct the calling
00188                                 // code to clip the ray and
00189                                 // continue the ray-cast to the next fixture. WARNING: do not
00190                                 // assume that fixtures
00191                                 // are reported in order. However, by clipping, we can always
00192                                 // get the closest fixture.
00193                                 return fraction;
00194                         }
00195 
00196                         bool m_see_fixtures;
00197                         bool m_hit;
00198                         b2Vec2 m_point;
00199                         b2Vec2 m_normal;
00200                 };
00201 
00202                 const mrpt::poses::CPose2D sensorPose =
00203                         vehPose + mrpt::poses::CPose2D(scan.sensorPose);
00204                 const b2Vec2 sensorPt = b2Vec2(sensorPose.x(), sensorPose.y());
00205 
00206                 RayCastClosestCallback callback;
00207                 callback.m_see_fixtures = m_see_fixtures;
00208 
00209                 // Scan size:
00210                 ASSERT_(nRays >= 2)
00211 #if MRPT_VERSION >= 0x150
00212                 scan.resizeScan(nRays);
00213 #else
00214                 scan.scan.resize(nRays);
00215                 scan.validRange.resize(nRays);
00216 #endif
00217                 double A =
00218                         sensorPose.phi() + (scan.rightToLeft ? -0.5 : +0.5) * scan.aperture;
00219                 const double AA =
00220                         (scan.rightToLeft ? 1.0 : -1.0) * (scan.aperture / (nRays - 1));
00221 
00222                 for (size_t i = 0; i < nRays; i++, A += AA)
00223                 {
00224                         const b2Vec2 endPt = b2Vec2(
00225                                 sensorPt.x + cos(A) * maxRange, sensorPt.y + sin(A) * maxRange);
00226 
00227                         callback.m_hit = false;
00228                         m_world->getBox2DWorld()->RayCast(&callback, sensorPt, endPt);
00229 #if MRPT_VERSION >= 0x150
00230                         scan.setScanRangeValidity(i, callback.m_hit);
00231 #else
00232                         scan.validRange[i] = callback.m_hit ? 1 : 0;
00233 #endif
00234 
00235                         float range = scan.scan[i];
00236                         if (callback.m_hit)
00237                         {
00238                                 // Hit:
00239                                 range = ::hypotf(
00240                                         callback.m_point.x - sensorPt.x,
00241                                         callback.m_point.y - sensorPt.y);
00242                                 range +=
00243                                         mrpt::random::randomGenerator.drawGaussian1D_normalized() *
00244                                         m_rangeStdNoise;
00245                         }
00246                         else
00247                         {
00248                                 // Miss:
00249                                 range = maxRange;
00250                         }
00251 #if MRPT_VERSION >= 0x150
00252                         scan.setScanRange(i, range);
00253 #else
00254                         scan.scan[i] = range;
00255 #endif
00256                 }  // end for (raycast scan)
00257         }
00258         m_world->getTimeLogger().leave("LaserScanner.scan.2.polygons");
00259 
00260         // Summarize all scans in one single scan:
00261         // ----------------------------------------
00262         m_world->getTimeLogger().enter("LaserScanner.scan.3.merge");
00263 
00264         CObservation2DRangeScan* lastScan =
00265                 new CObservation2DRangeScan(m_scan_model);
00266         lastScan->timestamp = mrpt::system::now();
00267         lastScan->sensorLabel = m_name;
00268 
00269 #if MRPT_VERSION >= 0x150
00270         lastScan->resizeScanAndAssign(nRays, maxRange, false);
00271 #else
00272         lastScan->scan.assign(nRays, maxRange);
00273         lastScan->validRange.assign(nRays, 0);
00274 #endif
00275 
00276         for (std::list<CObservation2DRangeScan>::const_iterator it =
00277                          lstScans.begin();
00278                  it != lstScans.end(); ++it)
00279         {
00280                 ASSERT_(it->scan.size() == nRays && it->validRange.size() == nRays)
00281 
00282                 for (size_t i = 0; i < nRays; i++)
00283                 {
00284                         if (it->validRange[i])
00285                         {
00286 #if MRPT_VERSION >= 0x150
00287                                 lastScan->setScanRange(
00288                                         i, std::min(lastScan->scan[i], it->scan[i]));
00289                                 lastScan->setScanRangeValidity(i, true);
00290 #else
00291                                 lastScan->scan[i] = std::min(lastScan->scan[i], it->scan[i]);
00292                                 lastScan->validRange[i] = 1;  // valid
00293 #endif
00294                         }
00295                 }
00296         }
00297         m_world->getTimeLogger().leave("LaserScanner.scan.3.merge");
00298 
00299         {
00300                 std::lock_guard<std::mutex> csl(m_last_scan_cs);
00301                 m_last_scan = CObservation2DRangeScan::Ptr(lastScan);
00302                 m_last_scan2gui = m_last_scan;
00303         }
00304 
00305         m_world->onNewObservation(m_vehicle, m_last_scan.get());
00306 
00307         m_gui_uptodate = false;
00308 }


mvsim
Author(s):
autogenerated on Thu Sep 7 2017 09:27:48