OccupancyGridMap.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2023 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/io/CFileGZInputStream.h>
11 #include <mrpt/maps/CSimplePointsMap.h>
12 #include <mrpt/opengl/COpenGLScene.h>
13 #include <mrpt/poses/CPose2D.h>
14 #include <mrpt/serialization/CArchive.h>
15 #include <mrpt/system/filesystem.h>
16 #include <mrpt/version.h>
17 #include <mvsim/World.h>
19 
20 #include <rapidxml.hpp>
21 
22 #include "xml_utils.h"
23 
24 using namespace rapidxml;
25 using namespace mvsim;
26 using namespace std;
27 
28 OccupancyGridMap::OccupancyGridMap(
29  World* parent, const rapidxml::xml_node<char>* root)
30  : WorldElementBase(parent),
31  gui_uptodate_(false),
32  show_grid_collision_points_(true),
33  restitution_(0.01),
34  lateral_friction_(0.5)
35 {
36  doLoadConfigFrom(root);
37 }
38 
40 
42 {
43  gui_uptodate_ = false;
44 
45  // <file>FILENAME.{png,gridmap}</file>
46  xml_node<>* xml_file = root->first_node("file");
47  if (!xml_file || !xml_file->value())
48  throw std::runtime_error(
49  "Error: <file></file> XML entry not found inside gridmap node!");
50 
51  const string sFile = world_->local_to_abs_path(xml_file->value());
52  const string sFileExt =
53  mrpt::system::extractFileExtension(sFile, true /*ignore gz*/);
54 
55  // ROS YAML map files:
56  if (sFileExt == "yaml")
57  {
58 #if MRPT_VERSION >= 0x250
59  bool ok = grid_.loadFromROSMapServerYAML(sFile);
60  ASSERTMSG_(
61  ok,
62  mrpt::format("Error loading ROS map file: '%s'", sFile.c_str()));
63 #else
64  THROW_EXCEPTION("Loading ROS YAML map files requires MRPT>=2.5.0");
65 #endif
66  }
67  else if (sFileExt == "gridmap")
68  {
69  mrpt::io::CFileGZInputStream fi(sFile);
70  auto f = mrpt::serialization::archiveFrom(fi);
71  f >> grid_;
72  }
73  else
74  // Assume it's an image:
75  {
76  TParameterDefinitions other_params;
77  double xcenterpixel = -1, ycenterpixel = -1;
78  double resolution = 0.10;
79 
80  other_params["resolution"] = TParamEntry("%lf", &resolution);
81  other_params["centerpixel_x"] = TParamEntry("%lf", &xcenterpixel);
82  other_params["centerpixel_y"] = TParamEntry("%lf", &ycenterpixel);
83 
85  *root, other_params, world_->user_defined_variables());
86 
87  if (!grid_.loadFromBitmapFile(
88  sFile, resolution, {xcenterpixel, ycenterpixel}))
89  throw std::runtime_error(mrpt::format(
90  "[OccupancyGridMap] ERROR: File not found '%s'",
91  sFile.c_str()));
92  }
93 
94  {
95  // Other general params:
97  ps["show_collisions"] =
99  ps["restitution"] = TParamEntry("%lf", &restitution_);
100  ps["lateral_friction"] = TParamEntry("%lf", &lateral_friction_);
101 
103  *root, ps, world_->user_defined_variables());
104  }
105 }
106 
108  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& viz,
109  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& physical,
110  [[maybe_unused]] bool childrenOnly)
111 {
112  using namespace mrpt::math;
113 
114  // 1st time call?? -> Create objects
115  if (!gl_grid_ && viz && physical)
116  {
117  gl_grid_ = mrpt::opengl::CSetOfObjects::Create();
118  gl_grid_->setName("OccupancyGridMap");
119  viz->get().insert(gl_grid_);
120  physical->get().insert(gl_grid_);
121  }
122  if (gl_obs_clouds_.size() != obstacles_for_each_obj_.size())
123  {
125  }
126 
127  // 1st call OR gridmap changed?
128  if (!gui_uptodate_)
129  {
130 #if MRPT_VERSION >= 0x240
131  grid_.getVisualizationInto(*gl_grid_);
132 #else
133  grid_.getAs3DObject(gl_grid_);
134 #endif
135  gui_uptodate_ = true;
136  }
137 
138  // Update obstacles:
139  if (viz)
140  {
141  std::lock_guard<std::mutex> csl(gl_obs_clouds_buffer_cs_);
142  for (size_t i = 0; i < gl_obs_clouds_.size(); i++)
143  {
144  mrpt::opengl::CSetOfObjects::Ptr& gl_objs = gl_obs_clouds_[i];
145  if (!gl_objs)
146  {
147  gl_objs = mrpt::opengl::CSetOfObjects::Create();
148  gl_objs->setName(
149  "OccupancyGridMap"s + this->getName() + ".obstacles["s +
150  std::to_string(i) + "]"s);
151  viz->get().insert(gl_objs);
152  }
153 
154  // Now that we are in a safe thread (with the OpenGL scene lock
155  // adquired from the caller)
156  // proceed to replace the old with the new point cloud:
157  gl_objs->clear();
158  if (gl_obs_clouds_buffer_.size() > i)
159  gl_objs->insert(gl_obs_clouds_buffer_[i]);
160  }
161 
162  gl_obs_clouds_buffer_.clear();
163  // end lock
164  }
165 }
166 
168  [[maybe_unused]] const TSimulContext& context)
169 {
170  // Make a list of objects subject to collide with the occupancy grid:
171  // - Vehicles
172  // - Blocks
173  {
174  const World::VehicleList& lstVehs = this->world_->getListOfVehicles();
175  const World::BlockList& lstBlocks = this->world_->getListOfBlocks();
176  const size_t nObjs = lstVehs.size() + lstBlocks.size();
177 
178  obstacles_for_each_obj_.resize(nObjs);
179  size_t obj_idx = 0;
180  for (World::VehicleList::const_iterator itVeh = lstVehs.begin();
181  itVeh != lstVehs.end(); ++itVeh, ++obj_idx)
182  {
185  itVeh->second->getMaxVehicleRadius() * 1.50f;
186  const mrpt::math::TPose3D& pose = itVeh->second->getPose();
187  ipv.pose = mrpt::poses::CPose2D(
188  pose.x, pose.y,
189  0 /* angle=0, no need to rotate everything to later rotate back again! */);
190  }
191  for (const auto& block : lstBlocks)
192  {
195  block.second->getMaxBlockRadius() * 1.50f;
196  const mrpt::math::TPose3D& pose = block.second->getPose();
197  /* angle=0, no need to rotate everything to later rotate back again!
198  */
199  ipv.pose = mrpt::poses::CPose2D(pose.x, pose.y, 0);
200 
201  obj_idx++;
202  }
203  }
204 
205  // For each object, create a ground body with fixtures at each scanned point
206  // around the vehicle, so it can collide with the environment:
207  // Upon first usage, reserve mem:
208  { // lock
209  std::lock_guard<std::mutex> csl(gl_obs_clouds_buffer_cs_);
210  const size_t nObjs = obstacles_for_each_obj_.size();
211  gl_obs_clouds_buffer_.resize(nObjs);
212 
213  for (size_t obj_idx = 0; obj_idx < nObjs; obj_idx++)
214  {
215  // 1) Simulate scan to get obstacles around the vehicle:
217  mrpt::obs::CObservation2DRangeScan::Ptr& scan = ipv.scan;
218  // Upon first time, reserve mem:
219  if (!scan) scan = mrpt::obs::CObservation2DRangeScan::Create();
220 
221  const float veh_max_obstacles_ranges = ipv.max_obstacles_ranges;
222  const float occup_threshold = 0.5f;
223  const size_t nRays = 50;
224 
225  scan->aperture = 2.0 * M_PI; // 360 field of view
226  scan->maxRange = veh_max_obstacles_ranges;
227 
228  grid_.laserScanSimulator(
229  *scan, ipv.pose, occup_threshold, nRays, 0.0f /*noise*/);
230 
231  // Since we'll dilate obstacle points, let's give a bit more space
232  // as compensation:
233  const float range_enlarge = 0.25f * grid_.getResolution();
234  for (size_t k = 0; k < scan->getScanSize(); k++)
235  {
236  scan->setScanRange(k, scan->getScanRange(k) + range_enlarge);
237  }
238  // 2) Create a Box2D "ground body" with square "fixtures" so the
239  // vehicle can collide with the occ. grid:
240 
241  // Create Box2D objects upon first usage:
242  if (!ipv.collide_body)
243  {
244  b2BodyDef bdef;
245  ipv.collide_body = world_->getBox2DWorld()->CreateBody(&bdef);
246  ASSERT_(ipv.collide_body);
247  }
248  // Force move the body to the vehicle origins (to use obstacles in
249  // local coords):
251  b2Vec2(ipv.pose.x(), ipv.pose.y()), .0f);
252 
253  // GL:
254  // 1st usage?
255  mrpt::opengl::CPointCloud::Ptr& gl_pts =
256  gl_obs_clouds_buffer_[obj_idx];
258  {
259  gl_pts = mrpt::opengl::CPointCloud::Create();
260  gl_pts->setPointSize(4.0f);
261  gl_pts->setColor(0, 0, 1);
262 
263  gl_pts->setVisibility(show_grid_collision_points_);
264  gl_pts->setPose(mrpt::poses::CPose2D(ipv.pose));
265  gl_pts->clear();
266  }
267 
268  // Physical properties of each "occupied cell":
269  const float occCellSemiWidth = grid_.getResolution() * 0.4f;
270  b2PolygonShape sqrPoly;
271  sqrPoly.SetAsBox(occCellSemiWidth, occCellSemiWidth);
272  sqrPoly.m_radius = 1e-3; // The "skin" depth of the body
273  b2FixtureDef fixtureDef;
274  fixtureDef.shape = &sqrPoly;
275  fixtureDef.restitution = restitution_;
276  fixtureDef.density = 0; // Fixed (inf. mass)
277  fixtureDef.friction = lateral_friction_; // 0.5f;
278 
279  // Create fixtures at their place (or disable it if no obstacle has
280  // been sensed):
281  const mrpt::obs::CSinCosLookUpTableFor2DScans::TSinCosValues&
282  sincos_tab = sincos_lut_.getSinCosForScan(*scan);
283  ipv.collide_fixtures.resize(nRays);
284  for (size_t k = 0; k < nRays; k++)
285  {
286  if (!ipv.collide_fixtures[k].fixture)
287  ipv.collide_fixtures[k].fixture =
288  ipv.collide_body->CreateFixture(&fixtureDef);
289 
290  if (!scan->getScanRangeValidity(k))
291  {
292  ipv.collide_fixtures[k].fixture->SetSensor(
293  true); // Box2D's way of saying: don't collide with
294  // this!
295  ipv.collide_fixtures[k].fixture->GetUserData().pointer =
297  }
298  else
299  {
300  // Box2D's way of saying: don't collide with this!
301  ipv.collide_fixtures[k].fixture->SetSensor(false);
302  ipv.collide_fixtures[k].fixture->GetUserData().pointer = 0;
303 
304  b2PolygonShape* poly = dynamic_cast<b2PolygonShape*>(
305  ipv.collide_fixtures[k].fixture->GetShape());
306  ASSERT_(poly != nullptr);
307 
308  const float llx =
309  sincos_tab.ccos[k] * scan->getScanRange(k);
310  const float lly =
311  sincos_tab.csin[k] * scan->getScanRange(k);
312 
313  const float ggx = ipv.pose.x() + llx;
314  const float ggy = ipv.pose.y() + lly;
315 
316  const float gx = grid_.idx2x(grid_.x2idx(ggx));
317  const float gy = grid_.idx2y(grid_.y2idx(ggy));
318 
319  const float lx = gx - ipv.pose.x();
320  const float ly = gy - ipv.pose.y();
321 
322  poly->SetAsBox(
323  occCellSemiWidth, occCellSemiWidth, b2Vec2(lx, ly),
324  .0f /*angle*/);
325 
326  if (gl_pts && show_grid_collision_points_)
327  {
328  gl_pts->mrpt::opengl::CPointCloud::insertPoint(
329  lx, ly, .0);
330  }
331  }
332  }
333  } // end for obj_idx
334 
335  } // end lock
336 }
const b2Shape * shape
Definition: b2_fixture.h:76
This file contains rapidxml parser and DOM implementation.
b2Fixture * CreateFixture(const b2FixtureDef *def)
Definition: b2_body.cpp:165
float density
The density, usually in kg/m^2.
Definition: b2_fixture.h:92
mrpt::maps::COccupancyGridMap2D grid_
constexpr uintptr_t INVISIBLE_FIXTURE_USER_DATA
Used to signal a Box2D fixture as "invisible" to sensors.
Definition: basic_types.h:64
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="", mrpt::system::COutputLogger *logger=nullptr)
Definition: xml_utils.cpp:224
f
mrpt::obs::CObservation2DRangeScan::Ptr scan
void SetTransform(const b2Vec2 &position, float angle)
Definition: b2_body.cpp:415
XmlRpcServer s
double lateral_friction_
(Default: 0.5)
std::multimap< std::string, VehicleBase::Ptr > VehicleList
Definition: World.h:281
const VehicleList & getListOfVehicles() const
Definition: World.h:303
std::unique_ptr< b2World > & getBox2DWorld()
Definition: World.h:297
#define M_PI
std::vector< mrpt::opengl::CPointCloud::Ptr > gl_obs_clouds_buffer_
A 2D column vector.
Definition: b2_math.h:41
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
std::multimap< std::string, Block::Ptr > BlockList
Definition: World.h:287
void SetAsBox(float hx, float hy)
mrpt::obs::CSinCosLookUpTableFor2DScans sincos_lut_
virtual void internalGuiUpdate(const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &viz, const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &physical, bool childrenOnly) override
float m_radius
Definition: b2_shape.h:102
std::vector< mrpt::opengl::CSetOfObjects::Ptr > gl_obs_clouds_
ROSCPP_DECL bool ok()
std::vector< TInfoPerCollidableobj > obstacles_for_each_obj_
const std::map< std::string, std::string > & user_defined_variables() const
Definition: World.h:391
std::mutex gl_obs_clouds_buffer_cs_
const std::string & getName() const
Definition: Simulable.h:92
Ch * value() const
Definition: rapidxml.hpp:692
double restitution_
Elastic restitution coef (default: 0.01)
const BlockList & getListOfBlocks() const
Definition: World.h:305
void doLoadConfigFrom(const rapidxml::xml_node< char > *root)
std::string local_to_abs_path(const std::string &in_path) const
Definition: World.cpp:252
float restitution
The restitution (elasticity) usually in the range [0,1].
Definition: b2_fixture.h:85
virtual void simul_pre_timestep(const TSimulContext &context) override
float friction
The friction coefficient, usually in the range [0,1].
Definition: b2_fixture.h:82
mrpt::opengl::CSetOfObjects::Ptr gl_grid_
call of internalGuiUpdate()


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21