00001
00002
00003
00004
00005
00006
00007
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
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
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
00060 parse_xmlnode_children_as_param(*root, params);
00061
00062
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
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
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;
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
00112 void LaserScanner::simul_post_timestep(const TSimulContext& context)
00113 {
00114
00115 if (context.simul_time < m_sensor_last_timestamp + m_sensor_period) return;
00116
00117 m_sensor_last_timestamp = context.simul_time;
00118
00119
00120
00121
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
00128 const mrpt::poses::CPose2D& vehPose = m_vehicle.getCPose2D();
00129
00130
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
00140 const OccupancyGridMap* grid =
00141 dynamic_cast<const OccupancyGridMap*>(*it);
00142 if (!grid) continue;
00143 const COccupancyGridMap2D& occGrid = grid->getOccGrid();
00144
00145
00146 lstScans.push_back(CObservation2DRangeScan(m_scan_model));
00147 CObservation2DRangeScan& scan = lstScans.back();
00148
00149
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
00157
00158 m_world->getTimeLogger().enter("LaserScanner.scan.2.polygons");
00159 {
00160
00161 lstScans.push_back(CObservation2DRangeScan(m_scan_model));
00162 CObservation2DRangeScan& scan = lstScans.back();
00163
00164
00165
00166
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
00179
00180
00181 return -1.0f;
00182 }
00183
00184 m_hit = true;
00185 m_point = point;
00186 m_normal = normal;
00187
00188
00189
00190
00191
00192
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
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
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
00249 range = maxRange;
00250 }
00251 #if MRPT_VERSION >= 0x150
00252 scan.setScanRange(i, range);
00253 #else
00254 scan.scan[i] = range;
00255 #endif
00256 }
00257 }
00258 m_world->getTimeLogger().leave("LaserScanner.scan.2.polygons");
00259
00260
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;
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 }