World.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 #include <mrpt/core/lock_helper.h>
10 #include <mrpt/math/TTwist2D.h>
11 #include <mrpt/system/filesystem.h> // filePathSeparatorsToNative()
12 #include <mrpt/version.h>
13 #include <mvsim/World.h>
14 
15 #include <algorithm> // count()
16 #include <iostream>
17 #include <map>
18 #include <stdexcept>
19 
20 using namespace mvsim;
21 using namespace std;
22 
23 // Default ctor: inits empty world.
24 World::World() : mrpt::system::COutputLogger("mvsim::World")
25 {
26  this->clear_all();
27 }
28 
29 // Dtor.
31 {
32  if (gui_thread_.joinable())
33  {
34  MRPT_LOG_DEBUG("Dtor: Waiting for GUI thread to quit...");
36  gui_thread_.join();
37  MRPT_LOG_DEBUG("Dtor: GUI thread shut down successful.");
38  }
39  else
40  {
41  MRPT_LOG_DEBUG("Dtor: GUI thread already shut down.");
42  }
43 
44  this->clear_all();
45  box2d_world_.reset();
46 }
47 
48 // Resets the entire simulation environment to an empty world.
50 {
51  auto lck = mrpt::lockHelper(world_cs_);
52 
53  // Reset params:
55 
56  // (B2D) World contents:
57  // ---------------------------------------------
58  box2d_world_ = std::make_unique<b2World>(b2Vec2_zero);
59 
60  // Define the ground body.
61  b2BodyDef groundBodyDef;
62  b2_ground_body_ = box2d_world_->CreateBody(&groundBodyDef);
63 
64  // Clear lists of objs:
65  // ---------------------------------------------
66  vehicles_.clear();
67  worldElements_.clear();
68  blocks_.clear();
69 }
70 
72 {
73  ASSERT_(!initialized_);
74  ASSERT_(worldVisual_);
75 
76 #if MRPT_VERSION >= 0x270
77  worldVisual_->getViewport()->lightParameters().ambient =
79 #else
80  worldVisual_->getViewport()->lightParameters().ambient = {
83 #endif
84  // Physical world light = visual world lights:
85  worldPhysical_.getViewport()->lightParameters() =
86  worldVisual_->getViewport()->lightParameters();
87 
88  // Create group for sensor viz:
89  {
90  auto glVizSensors = mrpt::opengl::CSetOfObjects::Create();
91  glVizSensors->setName("group_sensors_viz");
92  glVizSensors->setVisibility(guiOptions_.show_sensor_points);
93  worldVisual_->insert(glVizSensors);
94  }
95 
96  getTimeLogger().setMinLoggingLevel(this->getMinLoggingLevel());
97  remoteResources_.setMinLoggingLevel(this->getMinLoggingLevel());
98 
99  initialized_ = true;
100 }
101 
103 void World::run_simulation(double dt)
104 {
105  ASSERT_(initialized_);
106 
107  const double t0 = mrpt::Clock::nowDouble();
108 
109  // Define start of simulation time:
110  if (!simul_start_wallclock_time_.has_value())
111  simul_start_wallclock_time_ = t0 - dt;
112 
113  timlogger_.registerUserMeasure("run_simulation.dt", dt);
114 
115  const double simulTimestep = get_simul_timestep();
116 
117  // sanity checks:
118  ASSERT_(dt > 0);
119  ASSERT_(simulTimestep > 0);
120 
121  // Run in time steps:
122  const double end_time = get_simul_time() + dt;
123  // tolerance for rounding errors summing time steps
124  const double timetol = 1e-4;
125  while (get_simul_time() < (end_time - timetol))
126  {
127  // Timestep: always "simul_step" for the sake of repeatibility,
128  // except if requested to run a shorter step:
129  const double remainingTime = end_time - get_simul_time();
130  if (remainingTime <= 0) break;
131 
133  remainingTime >= simulTimestep ? simulTimestep : remainingTime);
134 
135  // IMPORTANT: This must be inside the loop to allow breaking if we are
136  // closing the app and simulatedTime is not ticking anymore.
137  if (simulator_must_close()) break;
138  }
139 
140  const double t1 = mrpt::Clock::toDouble(mrpt::Clock::now());
141 
142  timlogger_.registerUserMeasure("run_simulation.cpu_dt", t1 - t0);
143 }
144 
147 {
148  if (simulator_must_close()) return;
149 
150  std::lock_guard<std::mutex> lck(simulationStepRunningMtx_);
151 
152  timer_iteration_.Tic();
153 
154  TSimulContext context;
155  context.world = this;
156  context.b2_world = box2d_world_.get();
157  context.simul_time = get_simul_time();
158  context.dt = dt;
159 
160  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
161 
162  // 1) Pre-step
163  {
164  mrpt::system::CTimeLoggerEntry tle(timlogger_, "timestep.0.prestep");
165 
166  for (auto& e : simulableObjects_)
167  if (e.second) e.second->simul_pre_timestep(context);
168  }
169 
170  // 2) Run dynamics
171  {
172  mrpt::system::CTimeLoggerEntry tle(
173  timlogger_, "timestep.1.dynamics_integrator");
174 
176 
177  // Move time forward:
178  auto lckSimTim = mrpt::lockHelper(simul_time_mtx_);
179  simulTime_ += dt;
180  }
181 
182  // 3) Save dynamical state and post-step processing:
183  // This also makes a copy of all objects dynamical state, so service calls
184  // can be answered straight away without waiting for the main simulation
185  // mutex:
186  {
187  mrpt::system::CTimeLoggerEntry tle(
188  timlogger_, "timestep.3.save_dynstate");
189 
190  const auto lckPhys = mrpt::lockHelper(physical_objects_mtx());
191  const auto lckCopy = mrpt::lockHelper(copy_of_objects_dynstate_mtx_);
192 
193  for (auto& e : simulableObjects_)
194  {
195  if (!e.second) continue;
196  // process:
197  e.second->simul_post_timestep(context);
198 
199  // save our own copy of the kinematic state:
200  copy_of_objects_dynstate_pose_[e.first] = e.second->getPose();
201  copy_of_objects_dynstate_twist_[e.first] = e.second->getTwist();
202 
203  if (e.second->hadCollision())
204  copy_of_objects_had_collision_.insert(e.first);
205  }
206  }
207  {
208  const auto lckCollis = mrpt::lockHelper(reset_collision_flags_mtx_);
209  for (const auto& sId : reset_collision_flags_)
210  {
211  if (auto itV = simulableObjects_.find(sId);
212  itV != simulableObjects_.end())
213  itV->second->resetCollisionFlag();
214  }
215  reset_collision_flags_.clear();
216  }
217  lckListObjs.unlock(); // for simulableObjects_
218 
219  // 4) Wait for 3D sensors (OpenGL raytrace) to get executed on its thread:
220  mrpt::system::CTimeLoggerEntry tle4(
221  timlogger_, "timestep.4.wait_3D_sensors");
223  {
224  // Use a huge timeout here to avoid timing out in build farms / cloud
225  // containers:
226  for (int i = 0; i < 20000 && pending_running_sensors_on_3D_scene(); i++)
227  std::this_thread::sleep_for(std::chrono::milliseconds(1));
228 
230  {
231  MRPT_LOG_WARN(
232  "Timeout waiting for async sensors to be simulated in opengl "
233  "thread.");
234  timlogger_.registerUserMeasure("timestep.timeout_3D_sensors", 1.0);
235  }
236  }
237  tle4.stop();
238 
239  const double ts = timer_iteration_.Tac();
240  timlogger_.registerUserMeasure("timestep", ts);
241  if (ts > dt) timlogger_.registerUserMeasure("timestep.too_slow_alert", ts);
242 }
243 
244 std::string World::xmlPathToActualPath(const std::string& modelURI) const
245 {
246  std::string actualFileName = remoteResources_.resolve_path(modelURI);
247  return local_to_abs_path(actualFileName);
248 }
249 
252 std::string World::local_to_abs_path(const std::string& s_in) const
253 {
254  std::string ret;
255  const std::string s = mrpt::system::trim(s_in);
256 
257  // Relative path? It's not if:
258  // "X:\*", "/*"
259  // -------------------
260  bool is_relative = true;
261  if (s.size() > 2 && s[1] == ':' && (s[2] == '/' || s[2] == '\\'))
262  is_relative = false;
263  if (s.size() > 0 && (s[0] == '/' || s[0] == '\\')) is_relative = false;
264  if (is_relative)
265  {
266  ret = basePath_;
267  if (!ret.empty() && ret[ret.size() - 1] != '/' &&
268  ret[ret.size() - 1] != '\\')
269  ret += string("/");
270  ret += s;
271  }
272  else
273  ret = s;
274 
275  return mrpt::system::filePathSeparatorsToNative(ret);
276 }
277 
279 {
280  for (auto& veh : vehicles_)
281  if (veh.second) v(*veh.second);
282 }
283 
285 {
286  for (auto& we : worldElements_)
287  if (we) v(*we);
288 }
289 
291 {
292  for (auto& b : blocks_)
293  if (b.second) v(*b.second);
294 }
295 
297 {
298  //
299  client_.setVerbosityLevel(this->getMinLoggingLevel());
301  client_.connect();
302 
303  // Let objects register topics / services:
304  auto lckListObjs = mrpt::lockHelper(simulableObjectsMtx_);
305 
306  for (auto& o : simulableObjects_)
307  {
308  ASSERT_(o.second);
309  o.second->registerOnServer(client_);
310  }
311  lckListObjs.unlock();
312 
313  // global services:
315 }
316 
317 void World::insertBlock(const Block::Ptr& block)
318 {
319  // Assign each block an "index" number
320  block->setBlockIndex(blocks_.size());
321 
322  // make sure the name is not duplicated:
323  blocks_.insert(BlockList::value_type(block->getName(), block));
324 
325  auto lckListObjs = mrpt::lockHelper(simulableObjectsMtx_);
326 
327  simulableObjects_.insert(
328  simulableObjects_.end(),
329  std::make_pair(
330  block->getName(), std::dynamic_pointer_cast<Simulable>(block)));
331 }
332 
334 {
335  ASSERT_GE_(simulTimestep_, .0);
336  static bool firstTimeCheck = true;
337 
338  auto lambdaMinimumSensorPeriod = [this]() -> std::optional<double> {
339  std::optional<double> ret;
340  for (const auto& veh : vehicles_)
341  {
342  if (!veh.second) continue;
343  for (const auto& s : veh.second->getSensors())
344  {
345  const double T = s->sensor_period();
346  if (ret)
347  mrpt::keep_min(*ret, T);
348  else
349  ret = T;
350  }
351  }
352  return ret;
353  };
354 
355  if (simulTimestep_ == 0)
356  {
357  // `0` means auto-determine as the minimum of 5 ms and the shortest
358  // sensor sample period.
359  simulTimestep_ = 5e-3;
360 
361  auto sensorMinPeriod = lambdaMinimumSensorPeriod();
362  if (sensorMinPeriod) mrpt::keep_min(simulTimestep_, *sensorMinPeriod);
363 
364  MRPT_LOG_INFO_FMT(
365  "Physics simulation timestep automatically determined as: %.02f ms",
366  1e3 * simulTimestep_);
367 
368  firstTimeCheck = false; // no need to check.
369  }
370  else if (firstTimeCheck)
371  {
372  firstTimeCheck = false;
373  auto sensorMinPeriod = lambdaMinimumSensorPeriod();
374  if (sensorMinPeriod && simulTimestep_ > *sensorMinPeriod)
375  {
376  MRPT_LOG_WARN_FMT(
377  "Physics simulation timestep (%.02f ms) should be >= than the "
378  "minimum sensor period (%.02f ms) to avoid missing sensor "
379  "readings!!",
380  1e3 * simulTimestep_, 1e3 * *sensorMinPeriod);
381  }
382  }
383 
384  return simulTimestep_;
385 }
386 
388 {
389  auto lck = mrpt::lockHelper(worldPhysicalMtx_);
390 
391  worldPhysical_.clear();
392  worldVisual_->clear();
393 
395 }
396 
398 {
399  // If we have a GUI, reuse that context:
400  if (!headless()) return false;
401 
402  // otherwise, just the first time:
403  static bool first = true;
404  bool ret = first;
405  first = false;
406  return ret;
407 }
mrpt::system::CTimeLogger timlogger_
Definition: World.h:651
bool sensor_has_to_create_egl_context()
Definition: World.cpp:397
std::unique_ptr< b2World > box2d_world_
Definition: World.h:550
bool simulator_must_close() const
Definition: World.h:242
std::string basePath_
Definition: World.h:439
mrpt::opengl::COpenGLScene worldPhysical_
Definition: World.h:618
int b2dPosIters_
Definition: World.h:416
B2_API const b2Vec2 b2Vec2_zero
Useful constant.
double get_simul_time() const
Definition: World.h:100
SimulableList simulableObjects_
Definition: World.h:564
~World()
Dtor.
Definition: World.cpp:30
std::string serverAddress_
Definition: World.h:418
bool initialized_
Definition: World.h:559
XmlRpcServer s
double simul_time
Current time in the simulated world.
Definition: basic_types.h:59
double simulTimestep_
Definition: World.h:413
void runVisitorOnWorldElements(const world_element_visitor_t &v)
Definition: World.cpp:284
std::mutex simulationStepRunningMtx_
Definition: World.h:570
void force_set_simul_time(double newSimulatedTime)
Normally should not be called by users, for internal use only.
Definition: World.h:107
std::function< void(Block &)> block_visitor_t
Definition: World.h:342
mrpt::system::CTicTac timer_iteration_
Definition: World.h:652
void runVisitorOnBlocks(const block_visitor_t &v)
Definition: World.cpp:290
std::shared_ptr< Block > Ptr
Definition: Block.h:37
std::function< void(VehicleBase &)> vehicle_visitor_t
Definition: World.h:340
void internal_one_timestep(double dt)
Definition: World.cpp:146
VehicleList vehicles_
Definition: World.h:555
void insertBlock(const Block::Ptr &block)
Definition: World.cpp:317
void clear_all()
Definition: World.cpp:49
double dt
timestep
Definition: basic_types.h:60
static void FreeOpenGLResources()
std::string resolve_path(const std::string &uri)
bool headless() const
Definition: World.h:386
auto & getListOfSimulableObjectsMtx()
Definition: World.h:318
double get_simul_timestep() const
Simulation fixed-time interval for numerical integration.
Definition: World.cpp:333
std::recursive_mutex copy_of_objects_dynstate_mtx_
Definition: World.h:625
auto & physical_objects_mtx()
Definition: World.h:384
void free_opengl_resources()
Definition: World.cpp:387
RemoteResourcesManager remoteResources_
Definition: World.h:711
void internal_initialize()
Definition: World.cpp:71
std::function< void(WorldElementBase &)> world_element_visitor_t
Definition: World.h:341
bool pending_running_sensors_on_3D_scene()
Definition: World.h:225
std::thread gui_thread_
Definition: World.h:236
mrpt::system::CTimeLogger & getTimeLogger()
Definition: World.h:320
void internal_advertiseServices()
std::map< std::string, mrpt::math::TPose3D > copy_of_objects_dynstate_pose_
Updated in internal_one_step()
Definition: World.h:622
void runVisitorOnVehicles(const vehicle_visitor_t &v)
Definition: World.cpp:278
int b2dVelIters_
Definition: World.h:416
std::set< std::string > copy_of_objects_had_collision_
Definition: World.h:624
std::mutex reset_collision_flags_mtx_
Definition: World.h:628
std::recursive_mutex worldPhysicalMtx_
Definition: World.h:619
b2Body * b2_ground_body_
Definition: World.h:553
std::string local_to_abs_path(const std::string &in_path) const
Definition: World.cpp:252
std::recursive_mutex world_cs_
Definition: World.h:547
TGUI_Options guiOptions_
Definition: World.h:495
mrpt::opengl::COpenGLScene::Ptr worldVisual_
Definition: World.h:610
LightOptions lightOptions_
Definition: World.h:543
const std::string & serverHostAddress() const
Definition: Client.h:60
std::optional< double > simul_start_wallclock_time_
Definition: World.h:435
World()
Default ctor: inits an empty world.
Definition: World.cpp:24
std::mutex simul_time_mtx_
Definition: World.h:436
void connectToServer()
Definition: World.cpp:296
std::map< std::string, mrpt::math::TTwist2D > copy_of_objects_dynstate_twist_
Definition: World.h:623
BlockList blocks_
Definition: World.h:557
void run_simulation(double dt)
Definition: World.cpp:103
WorldElementList worldElements_
Definition: World.h:556
std::string xmlPathToActualPath(const std::string &modelURI) const
Definition: World.cpp:244
mvsim::Client client_
Definition: World.h:400
std::set< std::string > reset_collision_flags_
Definition: World.h:627
double simulTime_
Definition: World.h:434
std::mutex simulableObjectsMtx_
Definition: World.h:565


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