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


mvsim
Author(s):
autogenerated on Wed May 28 2025 02:13:08