9 #include <mrpt/core/lock_helper.h>
10 #include <mrpt/math/TTwist2D.h>
11 #include <mrpt/obs/CObservationOdometry.h>
12 #include <mrpt/obs/CSinCosLookUpTableFor2DScans.h>
13 #include <mrpt/poses/CPose3D.h>
14 #include <mrpt/poses/CPose3DQuat.h>
15 #include <mrpt/system/filesystem.h>
16 #include <mrpt/tfest.h>
17 #include <mrpt/tfest/TMatchingPair.h>
18 #include <mrpt/version.h>
21 using namespace mvsim;
27 ASSERT_(initialized_);
29 const double t0 = mrpt::Clock::nowDouble();
32 if (!simul_start_wallclock_time_.has_value()) simul_start_wallclock_time_ = t0 - dt;
34 timlogger_.registerUserMeasure(
"run_simulation.dt", dt);
36 const double simulTimestep = get_simul_timestep();
40 ASSERT_(simulTimestep > 0);
43 const double end_time = get_simul_time() + dt;
45 const double timetol = 1e-4;
46 while (get_simul_time() < (end_time - timetol))
50 const double remainingTime = end_time - get_simul_time();
51 if (remainingTime <= 0)
break;
53 internal_one_timestep(remainingTime >= simulTimestep ? simulTimestep : remainingTime);
57 if (simulator_must_close())
break;
60 const double t1 = mrpt::Clock::toDouble(mrpt::Clock::now());
62 timlogger_.registerUserMeasure(
"run_simulation.cpu_dt", t1 - t0);
68 if (simulator_must_close())
return;
70 std::lock_guard<std::mutex> lck(simulationStepRunningMtx_);
72 timer_iteration_.Tic();
76 context.
b2_world = box2d_world_.get();
80 auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
84 mrpt::system::CTimeLoggerEntry tle(timlogger_,
"timestep.1.prestep");
85 for (
auto& e : simulableObjects_)
86 if (e.second) e.second->simul_pre_timestep(context);
90 mrpt::system::CTimeLoggerEntry tle(timlogger_,
"timestep.2.terrain_elevation");
91 internal_simul_pre_step_terrain_elevation();
96 mrpt::system::CTimeLoggerEntry tle(timlogger_,
"timestep.3.dynamics_integrator");
98 box2d_world_->Step(dt, b2dVelIters_, b2dPosIters_);
101 auto lckSimTim = mrpt::lockHelper(simul_time_mtx_);
110 mrpt::system::CTimeLoggerEntry tle(timlogger_,
"timestep.4.save_dynstate");
112 const auto lckPhys = mrpt::lockHelper(physical_objects_mtx());
113 const auto lckCopy = mrpt::lockHelper(copy_of_objects_dynstate_mtx_);
115 for (
auto& e : simulableObjects_)
117 if (!e.second)
continue;
119 e.second->simul_post_timestep(context);
122 copy_of_objects_dynstate_pose_[e.first] = e.second->getPose();
123 copy_of_objects_dynstate_twist_[e.first] = e.second->getTwist();
125 if (e.second->hadCollision()) copy_of_objects_had_collision_.insert(e.first);
129 const auto lckCollis = mrpt::lockHelper(reset_collision_flags_mtx_);
130 for (
const auto& sId : reset_collision_flags_)
132 if (
auto itV = simulableObjects_.find(sId); itV != simulableObjects_.end())
133 itV->second->resetCollisionFlag();
135 reset_collision_flags_.clear();
137 lckListObjs.unlock();
140 mrpt::system::CTimeLoggerEntry tle4(timlogger_,
"timestep.5.wait_3D_sensors");
141 if (pending_running_sensors_on_3D_scene())
145 for (
int i = 0; i < 20000 && pending_running_sensors_on_3D_scene(); i++)
146 std::this_thread::sleep_for(std::chrono::milliseconds(1));
148 if (pending_running_sensors_on_3D_scene())
151 "Timeout waiting for async sensors to be simulated in opengl "
153 timlogger_.registerUserMeasure(
"timestep.timeout_3D_sensors", 1.0);
159 mrpt::system::CTimeLoggerEntry tle5(timlogger_,
"timestep.6.post_rawlog");
161 internalPostSimulStepForRawlog();
162 internalPostSimulStepForTrajectory();
166 const double ts = timer_iteration_.Tac();
167 timlogger_.registerUserMeasure(
"timestep", ts);
168 if (ts > dt) timlogger_.registerUserMeasure(
"timestep.too_slow_alert", ts);
173 ASSERT_GE_(simulTimestep_, .0);
174 static bool firstTimeCheck =
true;
176 auto lambdaMinimumSensorPeriod = [
this]() -> std::optional<double>
178 std::optional<double> ret;
179 for (
const auto& veh : vehicles_)
181 if (!veh.second)
continue;
182 for (
const auto&
s : veh.second->getSensors())
184 const double T =
s->sensor_period();
186 mrpt::keep_min(*ret, T);
194 if (simulTimestep_ == 0)
198 simulTimestep_ = 5e-3;
200 auto sensorMinPeriod = lambdaMinimumSensorPeriod();
201 if (sensorMinPeriod) mrpt::keep_min(simulTimestep_, *sensorMinPeriod);
204 "Physics simulation timestep automatically determined as: %.02f ms",
205 1e3 * simulTimestep_);
207 firstTimeCheck =
false;
209 else if (firstTimeCheck)
211 firstTimeCheck =
false;
212 auto sensorMinPeriod = lambdaMinimumSensorPeriod();
213 if (sensorMinPeriod && simulTimestep_ > *sensorMinPeriod)
216 "Physics simulation timestep (%.02f ms) should be >= than the "
217 "minimum sensor period (%.02f ms) to avoid missing sensor "
219 1e3 * simulTimestep_, 1e3 * *sensorMinPeriod);
223 return simulTimestep_;
228 if (save_to_rawlog_.empty())
return;
230 ASSERT_GT_(rawlog_odometry_rate_, 0.0);
232 const double now = get_simul_time();
233 const double T_odom = 1.0 / rawlog_odometry_rate_;
235 if (rawlog_last_odom_time_.has_value() && (now - *rawlog_last_odom_time_) < T_odom)
240 rawlog_last_odom_time_ = now;
243 for (
const auto& veh : vehicles_)
245 auto obs = mrpt::obs::CObservationOdometry::Create();
246 obs->timestamp = get_simul_timestamp();
247 obs->sensorLabel =
"odom";
248 obs->odometry = veh.second->getCPose2D();
250 obs->hasVelocities =
true;
251 obs->velocityLocal = veh.second->getVelocityLocal();
255 internalOnObservation(*veh.second, obs);
261 using namespace std::string_literals;
263 if (save_ground_truth_trajectory_.empty())
return;
265 ASSERT_GT_(ground_truth_rate_, 0.0);
267 const double now = get_simul_time();
268 const double T_odom = 1.0 / ground_truth_rate_;
270 if (gt_last_time_.has_value() && (now - *gt_last_time_) < T_odom)
279 auto lck = mrpt::lockHelper(gt_io_mtx_);
280 if (gt_io_per_veh_.empty())
282 for (
const auto& [vehName, veh] : vehicles_)
284 const std::string
fileName = mrpt::system::fileNameChangeExtension(
285 save_ground_truth_trajectory_, vehName +
".txt"s);
287 MRPT_LOG_INFO_STREAM(
"Creating ground truth file: " <<
fileName);
289 gt_io_per_veh_[vehName] = std::fstream(
fileName, std::ios::out);
293 const double t = mrpt::Clock::toDouble(get_simul_timestamp());
295 for (
const auto& [vehName, veh] : vehicles_)
297 const auto p = mrpt::poses::CPose3DQuat(veh->getCPose3D());
302 gt_io_per_veh_.at(vehName) << mrpt::format(
303 "%f %f %f %f %f %f %f %f\n",
t, p.x(), p.y(), p.z(), p.quat().x(), p.quat().y(),
304 p.quat().z(), p.quat().w());
313 const double gravity = get_gravity();
315 mrpt::tfest::TMatchingPairList corrs;
316 mrpt::poses::CPose3D optimalTf;
318 std::map<const VehicleBase*, std::vector<float>> wheel_heights_per_vehicle;
321 for (
auto& [name, veh] : lstVehs)
323 const size_t nWheels = veh->getNumWheels();
333 mrpt::math::TPoint3D dir_down;
334 for (
int iter = 0; iter < 2; iter++)
336 const mrpt::math::TPose3D& cur_pose = veh->getPose();
339 const mrpt::poses::CPose3D cur_cpose(cur_pose);
341 mrpt::math::TPose3D new_pose = cur_pose;
344 bool all_equal =
true;
345 std::optional<float> equal_zs;
348 auto& wheelHeights = wheel_heights_per_vehicle[veh.get()];
349 wheelHeights.resize(nWheels);
351 for (
size_t iW = 0; iW < nWheels; iW++)
353 const Wheel& wheel = veh->getWheelInfo(iW);
356 mrpt::tfest::TMatchingPair corr;
359 corr.local = mrpt::math::TPoint3D(wheel.
x, wheel.
y, 0);
362 const mrpt::math::TPoint3D gPt = cur_cpose.composePoint({wheel.
x, wheel.
y, 0.0});
364 const mrpt::math::TPoint3D gPtWheelsAxis =
365 gPt + mrpt::math::TPoint3D(.0, .0, 0.5 * wheel.
diameter);
368 const float z = this->getHighestElevationUnder(gPtWheelsAxis);
370 wheelHeights[iW] = z;
372 if (!equal_zs) equal_zs = z;
373 if (std::abs(*equal_zs - z) > 1e-4) all_equal =
false;
376 corr.global = mrpt::math::TPoint3D(gPt.x, gPt.y, z);
378 corrs.push_back(corr);
381 if (all_equal && equal_zs.has_value())
384 new_pose.z = *equal_zs;
388 else if (corrs.size() >= 3)
392 mrpt::poses::CPose3DQuat tmpl;
394 mrpt::tfest::se3_l2(corrs, tmpl, transf_scale,
true );
396 optimalTf = mrpt::poses::CPose3D(tmpl);
398 new_pose.z = optimalTf.z();
399 new_pose.yaw = optimalTf.yaw();
400 new_pose.pitch = optimalTf.pitch();
401 new_pose.roll = optimalTf.roll();
404 veh->setPose(new_pose);
410 mrpt::poses::CPose3D rot_only;
411 rot_only.setRotationMatrix(optimalTf.getRotationMatrix());
412 rot_only.inverseComposePoint(.0, .0, -1.0, dir_down.x, dir_down.y, dir_down.z);
419 const double chassis_weight = veh->getChassisMass() * gravity;
420 const mrpt::math::TPoint2D chassis_com = veh->getChassisCenterOfMass();
422 {dir_down.x * chassis_weight, dir_down.y * chassis_weight}, chassis_com);
425 for (
size_t iW = 0; iW < nWheels; iW++)
427 const Wheel& wheel = veh->getWheelInfo(iW);
428 const double wheel_weight = wheel.
mass * gravity;
430 {dir_down.x * wheel_weight, dir_down.y * wheel_weight}, {wheel.
x, wheel.
y});
441 const size_t nObjs = lstVehs.size() + lstBlocks.size();
442 obstacles_for_each_obj_.resize(nObjs);
444 for (
auto& [name, veh] : lstVehs)
446 auto& e = obstacles_for_each_obj_.at(objIdx);
447 if (!e.has_value()) e.emplace();
450 ipv.
pose = veh->getCPose3D();
452 ipv.
contour = veh->getChassisShape();
455 const auto& tw = veh->getTwist();
456 ipv.
speed = mrpt::math::TVector2D(tw.vx, tw.vy).norm();
458 if (
auto it = wheel_heights_per_vehicle.find(veh.get());
459 it != wheel_heights_per_vehicle.end())
467 #if 0 // Do not process blocks for now...
468 for (
const auto& [name, block] : lstBlocks)
470 auto& e = obstacles_for_each_obj_.at(objIdx);
474 if (block->isStatic())
continue;
475 const auto tw = block->getTwist();
476 if (std::abs(tw.vx) < 1e-4 && std::abs(tw.vy) < 1e-4 && std::abs(tw.omega) < 1e-4)
continue;
478 if (!e.has_value()) e.emplace();
481 ipv.
pose = block->getCPose3D();
483 ipv.
contour = block->blockShape();
489 for (
auto& e : obstacles_for_each_obj_)
491 if (!e.has_value())
continue;
497 float avrg_wheels_z = .0f;
503 for (
size_t k = 0; k < ipv.
contour.size(); k++)
505 const auto ptLocal = mrpt::math::TPoint3D(
507 const auto ptGlobal = ipv.
pose.composePoint(ptLocal);
508 const float z = getHighestElevationUnder(ptGlobal);
528 for (
size_t k = 0; k < ipv.
contour.size(); k++)
530 const double obsW = 0.01 + ipv.
speed * 0.07;
540 fixtureDef.
shape = &sqrPoly;
548 const bool is_collision =
565 ASSERT_(poly !=
nullptr);
567 const auto ptLocal = mrpt::math::TPoint3D(
569 auto ptGlobal = ipv.
pose.composePoint(ptLocal);
570 ptGlobal.x = mrpt::round(ptGlobal.x / obsW) * obsW;
571 ptGlobal.y = mrpt::round(ptGlobal.y / obsW) * obsW;
573 const float lx = ptGlobal.x - ipv.
pose.x();
574 const float ly = ptGlobal.y - ipv.
pose.y();
584 if (!lut2d_objects_is_up_to_date_) internal_update_lut_cache();
586 return lut2d_objects_;
591 constexpr
float LUT_GRID_SIZE = 4.0;
593 c.
x =
static_cast<int32_t
>(p.x / LUT_GRID_SIZE);
594 c.
y =
static_cast<int32_t
>(p.y / LUT_GRID_SIZE);
600 lut2d_objects_is_up_to_date_ =
true;
602 lut2d_objects_.clear();
603 for (
const auto& [name, obj] : blocks_)
605 std::set<lut_2d_coordinates_t, LutIndexHash> affected_coords;
606 const auto p = obj->getCPose3D();
607 for (
const auto& vertex : obj->blockShape())
609 const auto pt = p.composePoint({vertex.x, vertex.y, .0});
610 const auto c = xy_to_lut_coords(mrpt::math::TPoint2Df(pt.x, pt.y));
611 affected_coords.insert(c);
613 for (
const auto& c : affected_coords) lut2d_objects_[c].push_back(obj);