LaserScanner.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2020 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include <mrpt/core/lock_helper.h>
12 #include <mrpt/random.h>
14 #include <mvsim/VehicleBase.h>
15 #include <mvsim/World.h>
17 
18 #include "xml_utils.h"
19 
20 using namespace mvsim;
21 using namespace rapidxml;
22 
23 int z_order_cnt = 0;
24 
26  VehicleBase& parent, const rapidxml::xml_node<char>* root)
27  : SensorBase(parent),
28  m_z_order(++z_order_cnt),
29  m_rangeStdNoise(0.01),
30  m_angleStdNoise(mrpt::DEG2RAD(0.01)),
31  m_see_fixtures(true)
32 {
33  this->loadConfigFrom(root);
34 }
35 
38 {
39  m_gui_uptodate = false;
40 
41  // Attribs:
43  attribs["name"] = TParamEntry("%s", &this->m_name);
44 
45  parse_xmlnode_attribs(*root, attribs, {}, "[LaserScanner]");
46 
47  const std::map<std::string, std::string> varValues = {
48  {"NAME", m_name}, {"PARENT_NAME", m_vehicle.getName()}};
49 
50  // Other scalar params:
51  int nRays = 181;
52  double fov_deg = 180;
53  m_scan_model.sensorPose.z() = 0.05;
54 
56  params["fov_degrees"] = TParamEntry("%lf", &fov_deg);
57  params["nrays"] = TParamEntry("%i", &nRays);
58  params["pose"] = TParamEntry("%pose2d_ptr3d", &m_scan_model.sensorPose);
59  params["height"] = TParamEntry("%lf", &m_scan_model.sensorPose.z());
60  params["range_std_noise"] = TParamEntry("%lf", &m_rangeStdNoise);
61  params["angle_std_noise_deg"] = TParamEntry("%lf_deg", &m_angleStdNoise);
62  params["sensor_period"] = TParamEntry("%lf", &this->m_sensor_period);
63  params["bodies_visible"] = TParamEntry("%bool", &this->m_see_fixtures);
64 
65  params["viz_pointSize"] = TParamEntry("%f", &this->m_viz_pointSize);
66  params["viz_visiblePlane"] =
67  TParamEntry("%bool", &this->m_viz_visiblePlane);
68  params["viz_visiblePoints"] =
69  TParamEntry("%bool", &this->m_viz_visiblePoints);
70 
71  // Parse XML params:
72  parse_xmlnode_children_as_param(*root, params, varValues);
73 
74  // Parse common sensor XML params:
75  this->parseSensorPublish(root->first_node("publish"), varValues);
76 
77  // Pass params to the scan2D obj:
78  m_scan_model.aperture = mrpt::DEG2RAD(fov_deg);
79  m_scan_model.resizeScan(nRays);
80 
81  // Assign a sensible default name/sensor label if none is provided:
82  if (m_name.empty())
83  {
84  const size_t nextIdx = m_vehicle.getSensors().size() + 1;
85  m_name = mrpt::format("laser%u", static_cast<unsigned int>(nextIdx));
86  }
87 }
88 
90  mrpt::opengl::COpenGLScene& scene, bool childrenOnly)
91 {
92  auto lck = mrpt::lockHelper(m_gui_mtx);
93 
94  // 1st time?
95  if (!m_gl_scan)
96  {
97  m_gl_scan = mrpt::opengl::CPlanarLaserScan::Create();
98  m_gl_scan->enablePoints(m_viz_visiblePoints);
99  m_gl_scan->setPointSize(m_viz_pointSize);
100  m_gl_scan->enableSurface(m_viz_visiblePlane);
101  // m_gl_scan->setSurfaceColor(0.0f, 0.0f, 1.0f, 0.4f);
102 
103  m_gl_scan->setLocalRepresentativePoint({0, 0, 0.10f});
104  scene.insert(m_gl_scan);
105  }
106 
107  if (!m_gui_uptodate)
108  {
109  {
110  std::lock_guard<std::mutex> csl(m_last_scan_cs);
111  if (m_last_scan2gui)
112  {
113  m_gl_scan->setScan(*m_last_scan2gui);
114  m_last_scan2gui.reset();
115  }
116  }
117  m_gui_uptodate = true;
118  }
119 
120  const double z_incrs = 10e-3; // for m_z_order
121  const double z_offset = 1e-2;
124  p.x(), p.y(), z_offset + z_incrs * m_z_order, p.phi(), 0.0, 0.0));
125 }
126 
128  [maybe_unused]] const TSimulContext& context)
129 {
130 }
131 
132 // Simulate sensor AFTER timestep, with the updated vehicle dynamical state:
134 {
135  auto lck = mrpt::lockHelper(m_gui_mtx);
136 
138 
141 
142  // Limit sensor rate:
143  if (context.simul_time < m_sensor_last_timestamp + m_sensor_period) return;
144 
146 
147  // Create an array of scans, each reflecting ranges to one kind of world
148  // objects.
149  // Finally, we'll take the shortest range in each direction:
150  std::list<CObservation2DRangeScan> lstScans;
151 
152  const size_t nRays = m_scan_model.getScanSize();
153  const double maxRange = m_scan_model.maxRange;
154 
155  // Get pose of the robot:
156  const mrpt::poses::CPose2D& vehPose = m_vehicle.getCPose2D();
157 
158  // grid maps:
159  // -------------
160  m_world->getTimeLogger().enter("LaserScanner.scan.1.gridmap");
161 
163 
164  for (const auto& element : elements)
165  {
166  // If not a grid map, ignore:
167  const OccupancyGridMap* grid =
168  dynamic_cast<const OccupancyGridMap*>(element.get());
169  if (!grid) continue;
170  const COccupancyGridMap2D& occGrid = grid->getOccGrid();
171 
172  // Create new scan:
173  lstScans.emplace_back(m_scan_model);
174  CObservation2DRangeScan& scan = lstScans.back();
175 
176  // Ray tracing over the gridmap:
177  occGrid.laserScanSimulator(
178  scan, vehPose, 0.5f, m_scan_model.getScanSize(), m_rangeStdNoise, 1,
180  }
181  m_world->getTimeLogger().leave("LaserScanner.scan.1.gridmap");
182 
183  // ray trace on Box2D polygons:
184  // ------------------------------
185  m_world->getTimeLogger().enter("LaserScanner.scan.2.polygons");
186  {
187  // Create new scan:
188  lstScans.push_back(CObservation2DRangeScan(m_scan_model));
189  CObservation2DRangeScan& scan = lstScans.back();
190 
191  // Avoid the lidar seeing the vehicle owns shape:
192  std::map<b2Fixture*, void*> orgUserData;
193 
194  auto makeFixtureInvisible = [&](b2Fixture* f) {
195  if (!f) return;
196  orgUserData[f] = f->GetUserData();
197  f->SetUserData(INVISIBLE_FIXTURE_USER_DATA);
198  };
199  auto undoInvisibleFixtures = [&]() {
200  for (auto& kv : orgUserData) kv.first->SetUserData(kv.second);
201  };
202 
203  makeFixtureInvisible(m_vehicle.get_fixture_chassis());
204  for (auto& f : m_vehicle.get_fixture_wheels()) makeFixtureInvisible(f);
205 
206  // Do Box2D raycasting stuff:
207  // ------------------------------
208  // This callback finds the closest hit. Polygon 0 is filtered.
209  class RayCastClosestCallback : public b2RayCastCallback
210  {
211  public:
212  RayCastClosestCallback() : m_see_fixtures(true), m_hit(false) {}
213  float32 ReportFixture(
214  b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal,
215  float32 fraction) override
216  {
217  if (!m_see_fixtures ||
219  {
220  // By returning -1, we instruct the calling code to ignore
221  // this fixture and
222  // continue the ray-cast to the next fixture.
223  return -1.0f;
224  }
225 
226  m_hit = true;
227  m_point = point;
228  m_normal = normal;
229  // By returning the current fraction, we instruct the calling
230  // code to clip the ray and
231  // continue the ray-cast to the next fixture. WARNING: do not
232  // assume that fixtures
233  // are reported in order. However, by clipping, we can always
234  // get the closest fixture.
235  return fraction;
236  }
237 
238  bool m_see_fixtures;
239  bool m_hit;
240  b2Vec2 m_point;
241  b2Vec2 m_normal;
242  };
243 
244  const mrpt::poses::CPose2D sensorPose =
245  vehPose + mrpt::poses::CPose2D(scan.sensorPose);
246  const b2Vec2 sensorPt = b2Vec2(sensorPose.x(), sensorPose.y());
247 
248  RayCastClosestCallback callback;
249  callback.m_see_fixtures = m_see_fixtures;
250 
251  // Scan size:
252  ASSERT_(nRays >= 2);
253  scan.resizeScanAndAssign(nRays, maxRange, false);
254  double A =
255  sensorPose.phi() + (scan.rightToLeft ? -0.5 : +0.5) * scan.aperture;
256  const double AA =
257  (scan.rightToLeft ? 1.0 : -1.0) * (scan.aperture / (nRays - 1));
258 
259  auto& rnd = mrpt::random::getRandomGenerator();
260 
261  for (size_t i = 0; i < nRays; i++, A += AA)
262  {
263  const b2Vec2 endPt = b2Vec2(
264  sensorPt.x + cos(A) * maxRange, sensorPt.y + sin(A) * maxRange);
265 
266  callback.m_hit = false;
267  m_world->getBox2DWorld()->RayCast(&callback, sensorPt, endPt);
268  scan.setScanRangeValidity(i, callback.m_hit);
269 
270  float range = scan.getScanRange(i);
271  if (callback.m_hit)
272  {
273  // Hit:
274  range = std::sqrt(
275  mrpt::square(callback.m_point.x - sensorPt.x) +
276  mrpt::square(callback.m_point.y - sensorPt.y));
277  range += rnd.drawGaussian1D_normalized() * m_rangeStdNoise;
278  }
279  else
280  {
281  // Miss:
282  range = maxRange;
283  }
284  scan.setScanRange(i, range);
285  } // end for (raycast scan)
286 
287  undoInvisibleFixtures();
288  }
289  m_world->getTimeLogger().leave("LaserScanner.scan.2.polygons");
290 
291  // Summarize all scans in one single scan:
292  // ----------------------------------------
293  m_world->getTimeLogger().enter("LaserScanner.scan.3.merge");
294 
295  auto lastScan = CObservation2DRangeScan::Create(m_scan_model);
296 
297  lastScan->timestamp = mrpt::system::now();
298  lastScan->sensorLabel = m_name;
299 
300  lastScan->resizeScanAndAssign(nRays, maxRange, false);
301 
302  for (const auto& scan : lstScans)
303  {
304  for (size_t i = 0; i < nRays; i++)
305  {
306  if (scan.getScanRangeValidity(i))
307  {
308  lastScan->setScanRange(
309  i,
310  std::min(lastScan->getScanRange(i), scan.getScanRange(i)));
311  lastScan->setScanRangeValidity(i, true);
312  }
313  }
314  }
315  m_world->getTimeLogger().leave("LaserScanner.scan.3.merge");
316 
317  {
318  std::lock_guard<std::mutex> csl(m_last_scan_cs);
319  m_last_scan = std::move(lastScan);
321  }
322 
324 
325  m_gui_uptodate = false;
326 }
void resizeScan(const size_t len)
GLenum GLenum range
const mrpt::maps::COccupancyGridMap2D & getOccGrid() const
std::map< std::string, TParamEntry > TParameterDefinitions
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:179
b2Fixture * get_fixture_chassis()
Definition: VehicleBase.h:139
mrpt::poses::CPose2D getCPose2D() const
Alternative to getPose()
Definition: Simulable.cpp:93
mrpt::system::TTimeStamp now()
double simul_time
Current time in the simulated world.
Definition: basic_types.h:60
std::unique_ptr< b2World > & getBox2DWorld()
Definition: World.h:189
LaserScanner(VehicleBase &parent, const rapidxml::xml_node< char > *root)
void parse_xmlnode_attribs(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:140
bool parseSensorPublish(const rapidxml::xml_node< char > *node, const std::map< std::string, std::string > &varValues)
Definition: SensorBase.cpp:88
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
A 2D column vector.
Definition: b2Math.h:52
void insert(const CRenderizablePtr &newObject, const std::string &viewportName=std::string("main"))
const TListSensors & getSensors() const
Definition: VehicleBase.h:104
const GLint * attribs
std::string m_name
sensor label/name
Definition: LaserScanner.h:43
const std::string & getName() const
Definition: Simulable.h:95
mrpt::poses::CPose3D sensorPose
VehicleBase & m_vehicle
The vehicle this sensor is attached to.
Definition: SensorBase.h:43
void reportNewObservation(const std::shared_ptr< mrpt::obs::CObservation > &obs, const TSimulContext &context)
Definition: SensorBase.cpp:106
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
mrpt::obs::CObservation2DRangeScan m_scan_model
Definition: LaserScanner.h:56
double DEG2RAD(const double x)
const double & phi() const
GLfloat GLfloat p
mrpt::obs::CObservation2DRangeScan::Ptr m_last_scan
Definition: LaserScanner.h:60
virtual void simul_pre_timestep(const TSimulContext &context) override
virtual void internalGuiUpdate(mrpt::opengl::COpenGLScene &scene, bool childrenOnly) override
#define INVISIBLE_FIXTURE_USER_DATA
Used to signal a Box2D fixture as "invisible" to sensors.
Definition: basic_types.h:22
virtual void simul_post_timestep(const TSimulContext &context)
Definition: Simulable.cpp:38
virtual void simul_post_timestep(const TSimulContext &context) override
float32 y
Definition: b2Math.h:139
void laserScanSimulator(mrpt::obs::CObservation2DRangeScan &inout_Scan, const mrpt::poses::CPose2D &robotPose, float threshold=0.6f, size_t N=361, float noiseStd=0, unsigned int decimation=1, float angleNoiseStd=mrpt::utils::DEG2RAD(0)) const
const WorldElementList & getListOfWorldElements() const
Definition: World.h:199
mrpt::system::CTimeLogger & getTimeLogger()
Definition: World.h:210
std::vector< b2Fixture * > & get_fixture_wheels()
Definition: VehicleBase.h:140
mrpt::obs::CObservation2DRangeScan::Ptr m_last_scan2gui
Definition: LaserScanner.h:61
int m_z_order
to help rendering multiple scans
Definition: LaserScanner.h:41
#define ASSERT_(f)
GLfloat * params
double m_sensor_period
Definition: SensorBase.h:37
int z_order_cnt
std::list< WorldElementBase::Ptr > WorldElementList
Definition: World.h:176
float32 x
Definition: b2Math.h:139
virtual void loadConfigFrom(const rapidxml::xml_node< char > *root) override
double m_sensor_last_timestamp
Definition: SensorBase.h:46
mrpt::opengl::CPlanarLaserScan::Ptr m_gl_scan
Definition: LaserScanner.h:67
std::mutex m_last_scan_cs
Definition: LaserScanner.h:58
float float32
Definition: b2Settings.h:35
GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble f
std::mutex m_gui_mtx
Definition: LaserScanner.h:66
void * GetUserData() const
Definition: b2Fixture.h:263


mvsim
Author(s):
autogenerated on Fri May 7 2021 03:05:51