VehicleBase.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/core/lock_helper.h>
11 #include <mrpt/math/TPose2D.h>
12 #include <mrpt/opengl/CPolyhedron.h>
13 #include <mrpt/poses/CPose2D.h>
14 #include <mrpt/system/filesystem.h>
15 #include <mvsim/FrictionModels/DefaultFriction.h> // For use as default model
17 #include <mvsim/VehicleBase.h>
21 #include <mvsim/World.h>
22 
23 #include <map>
24 #include <rapidxml.hpp>
25 #include <rapidxml_print.hpp>
26 #include <rapidxml_utils.hpp>
27 #include <sstream> // std::stringstream
28 #include <string>
29 
30 #include "JointXMLnode.h"
31 #include "XMLClassesRegistry.h"
32 #include "parse_utils.h"
33 #include "xml_utils.h"
34 
35 using namespace mvsim;
36 using namespace std;
37 
38 static XmlClassesRegistry veh_classes_registry("vehicle:class");
39 
41 
42 // Explicit registration calls seem to be one (the unique?) way to assure
43 // registration takes place:
45 {
46  static bool done = false;
47  if (done)
48  return;
49  else
50  done = true;
51 
54  "differential_3_wheels", DynamicsDifferential_3_wheels)
56  "differential_4_wheels", DynamicsDifferential_4_wheels)
59  "ackermann_drivetrain", DynamicsAckermannDrivetrain)
60 }
61 
62 constexpr char VehicleBase::DL_TIMESTAMP[];
63 constexpr char VehicleBase::LOGGER_POSE[];
64 constexpr char VehicleBase::LOGGER_WHEEL[];
65 
66 constexpr char VehicleBase::PL_Q_X[];
67 constexpr char VehicleBase::PL_Q_Y[];
68 constexpr char VehicleBase::PL_Q_Z[];
69 constexpr char VehicleBase::PL_Q_YAW[];
70 constexpr char VehicleBase::PL_Q_PITCH[];
71 constexpr char VehicleBase::PL_Q_ROLL[];
72 constexpr char VehicleBase::PL_DQ_X[];
73 constexpr char VehicleBase::PL_DQ_Y[];
74 constexpr char VehicleBase::PL_DQ_Z[];
75 
76 constexpr char VehicleBase::WL_TORQUE[];
77 constexpr char VehicleBase::WL_WEIGHT[];
78 constexpr char VehicleBase::WL_VEL_X[];
79 constexpr char VehicleBase::WL_VEL_Y[];
80 constexpr char VehicleBase::WL_FRIC_X[];
81 constexpr char VehicleBase::WL_FRIC_Y[];
82 
83 // Protected ctor:
84 VehicleBase::VehicleBase(World* parent, size_t nWheels)
85  : VisualObject(parent), Simulable(parent), fixture_wheels_(nWheels, nullptr)
86 {
87  // Create wheels:
88  for (size_t i = 0; i < nWheels; i++) wheels_info_.emplace_back(parent);
89 
90  // Default shape:
91  chassis_poly_.emplace_back(-0.4, -0.5);
92  chassis_poly_.emplace_back(-0.4, 0.5);
93  chassis_poly_.emplace_back(0.4, 0.5);
94  chassis_poly_.emplace_back(0.6, 0.3);
95  chassis_poly_.emplace_back(0.6, -0.3);
96  chassis_poly_.emplace_back(0.4, -0.5);
98 }
99 
103  const rapidxml::xml_node<char>* xml_node)
104 {
105  // Sanity checks:
106  if (!xml_node)
107  throw runtime_error(
108  "[VehicleBase::register_vehicle_class] XML node is nullptr");
109  if (0 != strcmp(xml_node->name(), "vehicle:class"))
110  throw runtime_error(mrpt::format(
111  "[VehicleBase::register_vehicle_class] XML element is '%s' "
112  "('vehicle:class' expected)",
113  xml_node->name()));
114 
115  // rapidxml doesn't allow making copied of objects.
116  // So: convert to txt; then re-parse.
117  std::stringstream ss;
118  ss << *xml_node;
119 
120  veh_classes_registry.add(ss.str());
121 }
122 
126  World* parent, const rapidxml::xml_node<char>* root)
127 {
129 
130  using namespace std;
131  using namespace rapidxml;
132 
133  if (!root)
134  throw runtime_error("[VehicleBase::factory] XML node is nullptr");
135  if (0 != strcmp(root->name(), "vehicle"))
136  throw runtime_error(mrpt::format(
137  "[VehicleBase::factory] XML root element is '%s' ('vehicle' "
138  "expected)",
139  root->name()));
140 
141  // "class": When a vehicle has a 'class="XXX"' attribute, look for each
142  // parameter
143  // in the set of "root" + "class_root" XML nodes:
144  // --------------------------------------------------------------------------------
145  JointXMLnode<> nodes;
146 
147  std::vector<XML_Doc_Data::Ptr> scopedLifeDocs;
148 
149  // Solve includes:
150  for (auto n = root->first_node(); n; n = n->next_sibling())
151  {
152  if (strcmp(n->name(), "include") != 0) continue;
153 
154  auto fileAttrb = n->first_attribute("file");
155  ASSERTMSG_(
156  fileAttrb,
157  "XML tag '<include />' must have a 'file=\"xxx\"' attribute)");
158 
159  const std::string relFile = mvsim::parse(fileAttrb->value(), {});
160  const auto absFile = parent->local_to_abs_path(relFile);
161  parent->logStr(
162  mrpt::system::LVL_DEBUG,
163  mrpt::format("XML parser: including file: '%s'", absFile.c_str()));
164 
165  std::map<std::string, std::string> vars;
166  for (auto attr = n->first_attribute(); attr;
167  attr = attr->next_attribute())
168  {
169  if (strcmp(attr->name(), "file") == 0) continue;
170  vars[attr->name()] = attr->value();
171  }
172 
173  // Delay the replacement of this variable (used in Sensors) until "veh"
174  // is constructed and we actually have a name:
175  std::set<std::string> varsRetain = {
176  "NAME" /*sensor name*/, "PARENT_NAME" /*vehicle name*/};
177 
178  const auto [xml, nRoot] = readXmlAndGetRoot(absFile, vars, varsRetain);
179 
180  // the XML document object must exist during this whole function scope
181  scopedLifeDocs.emplace_back(xml);
182 
183  // recursive parse:
184  const auto newBasePath =
185  mrpt::system::trim(mrpt::system::extractFileDirectory(absFile));
186 
187  nodes.add(nRoot->parent());
188  }
189 
190  // ---
191  {
192  // Always search in root. Also in the class root, if any:
193  nodes.add(root);
194 
195  const xml_attribute<>* veh_class = root->first_attribute("class");
196  if (veh_class)
197  {
198  const string sClassName = veh_class->value();
199  const rapidxml::xml_node<char>* class_root =
200  veh_classes_registry.get(sClassName);
201  if (!class_root)
202  throw runtime_error(mrpt::format(
203  "[VehicleBase::factory] Vehicle class '%s' undefined",
204  sClassName.c_str()));
205 
206  nodes.add(class_root);
207  }
208  }
209 
210  // Class factory according to: <dynamics class="XXX">
211  // -------------------------------------------------
212  const xml_node<>* dyn_node = nodes.first_node("dynamics");
213  if (!dyn_node)
214  throw runtime_error(
215  "[VehicleBase::factory] Missing XML node <dynamics>");
216 
217  const xml_attribute<>* dyn_class = dyn_node->first_attribute("class");
218  if (!dyn_class || !dyn_class->value())
219  throw runtime_error(
220  "[VehicleBase::factory] Missing mandatory attribute 'class' in "
221  "node <dynamics>");
222 
223  VehicleBase::Ptr veh =
225  if (!veh)
226  throw runtime_error(mrpt::format(
227  "[VehicleBase::factory] Unknown vehicle dynamics class '%s'",
228  dyn_class->value()));
229 
230  // Initialize here all common params shared by any polymorphic class:
231  // -------------------------------------------------
232  // attrib: name
233  {
234  const xml_attribute<>* attrib_name = root->first_attribute("name");
235  if (attrib_name && attrib_name->value())
236  {
237  veh->name_ = attrib_name->value();
238  }
239  else
240  {
241  // Default name:
242  static int cnt = 0;
243  veh->name_ = mrpt::format("veh%i", ++cnt);
244  }
245  }
246 
247  // Common setup for simulable objects:
248  // -----------------------------------------------------------
249  veh->parseSimulable(nodes);
250 
251  // Custom visualization 3D model:
252  // -----------------------------------------------------------
253  veh->parseVisual(nodes);
254 
255  // Initialize class-specific params (mass, chassis shape, etc.)
256  // ---------------------------------------------------------------
257  veh->dynamics_load_params_from_xml(dyn_node);
258 
259  // Auto shape node from visual?
260  if (const rapidxml::xml_node<char>* xml_chassis =
261  dyn_node->first_node("chassis");
262  xml_chassis)
263  {
264  if (const rapidxml::xml_node<char>* sfv =
265  xml_chassis->first_node("shape_from_visual");
266  sfv)
267  {
268  const auto& bbVis = veh->collisionShape();
269  if (!bbVis.has_value())
270  {
271  THROW_EXCEPTION(
272  "Error: Tag <shape_from_visual/> found but no "
273  "<visual> entry seems to have been found while parsing "
274  "<vehicle>");
275  }
276  const auto& bb = bbVis.value();
277  if (bb.volume() == 0)
278  {
279  THROW_EXCEPTION(
280  "Error: Tag <shape_from_visual/> found but bounding box of "
281  "visual object seems incorrect, while parsing <vehicle>");
282  }
283 
284  // Set contour polygon:
285  veh->chassis_poly_ = bb.getContour();
286  veh->chassis_z_min_ = bb.zMin();
287  veh->chassis_z_max_ = bb.zMax();
288  }
289  else
290  {
291  // Update collision shape from shape loaded from XML:
292  Shape2p5 cs;
293  cs.setShapeManual(
294  veh->chassis_poly_, veh->chassis_z_min_, veh->chassis_z_max_);
295 
296  veh->setCollisionShape(cs);
297  }
298  }
299 
300  veh->updateMaxRadiusFromPoly();
301 
302  // <Optional> Log path. If not specified, app folder will be used
303  // -----------------------------------------------------------
304  {
305  const xml_node<>* log_path_node = nodes.first_node("log_path");
306  if (log_path_node)
307  {
308  // Parse:
309  veh->log_path_ = log_path_node->value();
310  }
311  }
312 
313  veh->initLoggers();
314 
315  // Register bodies, fixtures, etc. in Box2D simulator:
316  // ----------------------------------------------------
317  veh->create_multibody_system(*parent->getBox2DWorld());
318 
319  if (veh->b2dBody_)
320  {
321  // Init pos:
322  const auto q = veh->getPose();
323  const auto dq = veh->getTwist();
324 
325  veh->b2dBody_->SetTransform(b2Vec2(q.x, q.y), q.yaw);
326  // Init vel:
327  veh->b2dBody_->SetLinearVelocity(b2Vec2(dq.vx, dq.vy));
328  veh->b2dBody_->SetAngularVelocity(dq.omega);
329  }
330 
331  // Friction model:
332  // Parse <friction> node, or assume default linear model:
333  // -----------------------------------------------------------
334  {
335  const xml_node<>* frict_node = nodes.first_node("friction");
336  if (!frict_node)
337  {
338  // Default:
339  veh->friction_ =
340  std::make_shared<DefaultFriction>(*veh, nullptr /*default */);
341  }
342  else
343  {
344  // Parse:
345  veh->friction_ = std::shared_ptr<FrictionBase>(
346  FrictionBase::factory(*veh, frict_node));
347  ASSERT_(veh->friction_);
348  }
349  }
350 
351  // Sensors: <sensor class='XXX'> entries
352  // -------------------------------------------------
353  for (const auto& xmlNode : nodes)
354  {
355  if (!strcmp(xmlNode->name(), "sensor"))
356  {
357  SensorBase::Ptr se = SensorBase::factory(*veh, xmlNode);
358  veh->sensors_.push_back(SensorBase::Ptr(se));
359  }
360  }
361 
362  return veh;
363 }
364 
366  World* parent, const std::string& xml_text)
367 {
368  // Parse the string as if it was an XML file:
369  std::stringstream s;
370  s.str(xml_text);
371 
372  char* input_str = const_cast<char*>(xml_text.c_str());
374  try
375  {
376  xml.parse<0>(input_str);
377  }
378  catch (rapidxml::parse_error& e)
379  {
380  unsigned int line =
381  static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
382  throw std::runtime_error(mrpt::format(
383  "[VehicleBase::factory] XML parse error (Line %u): %s",
384  static_cast<unsigned>(line), e.what()));
385  }
386  return VehicleBase::factory(parent, xml.first_node());
387 }
388 
390 {
392  for (auto& s : sensors_) s->simul_pre_timestep(context);
393 
394  // Update wheels position (they may turn, etc. as in an Ackermann
395  // configuration)
396  for (size_t i = 0; i < fixture_wheels_.size(); i++)
397  {
398  b2PolygonShape* wheelShape =
399  dynamic_cast<b2PolygonShape*>(fixture_wheels_[i]->GetShape());
400  wheelShape->SetAsBox(
401  wheels_info_[i].diameter * 0.5, wheels_info_[i].width * 0.5,
402  b2Vec2(wheels_info_[i].x, wheels_info_[i].y), wheels_info_[i].yaw);
403  }
404 
405  // Apply motor forces/torques:
406  const std::vector<double> wheelTorque = invoke_motor_controllers(context);
407 
408  // Apply friction model at each wheel:
409  const size_t nW = getNumWheels();
410  ASSERT_EQUAL_(wheelTorque.size(), nW);
411 
412  // Part of the vehicle weight on each wheel:
413  const double gravity = parent()->get_gravity();
414  MRPT_TODO("Use chassis cog point");
415  const double massPerWheel = getChassisMass() / nW;
416  const double weightPerWheel = massPerWheel * gravity;
417 
418  const std::vector<mrpt::math::TVector2D> wheelLocalVels =
420 
421  ASSERT_EQUAL_(wheelLocalVels.size(), nW);
422 
423  // For visualization only
424  std::vector<mrpt::math::TSegment3D> forceVectors, torqueVectors;
425 
426  for (size_t i = 0; i < nW; i++)
427  {
428  // prepare data:
429  Wheel& w = getWheelInfo(i);
430 
431  FrictionBase::TFrictionInput fi(context, w);
432  fi.motorTorque = -wheelTorque[i]; // "-" => Forwards is negative
433  fi.weight = weightPerWheel;
434  fi.wheelCogLocalVel = wheelLocalVels[i];
435 
436  friction_->setLogger(
437  getLoggerPtr(LOGGER_WHEEL + std::to_string(i + 1)));
438 
439  // eval friction (in the frame of the vehicle):
440  const mrpt::math::TPoint2D F_r = friction_->evaluate_friction(fi);
441 
442  // Apply force:
443  // Force vector -> world coords
444  const b2Vec2 wForce = b2dBody_->GetWorldVector(b2Vec2(F_r.x, F_r.y));
445  // Application point -> world coords
446  const b2Vec2 wPt = b2dBody_->GetWorldPoint(b2Vec2(w.x, w.y));
447 
448  // printf("w%i: Lx=%6.3f Ly=%6.3f | Gx=%11.9f
449  // Gy=%11.9f\n",(int)i,net_force_.x,net_force_.y,wForce.x,wForce.y);
450 
451  b2dBody_->ApplyForce(wForce, wPt, true /*wake up*/);
452 
453  // log
454  {
455  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
456  DL_TIMESTAMP, context.simul_time);
457  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
458  WL_TORQUE, fi.motorTorque);
459  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
460  WL_WEIGHT, fi.weight);
461  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
462  WL_VEL_X, fi.wheelCogLocalVel.x);
463  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
464  WL_VEL_Y, fi.wheelCogLocalVel.y);
465  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
466  WL_FRIC_X, F_r.x);
467  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->updateColumn(
468  WL_FRIC_Y, F_r.y);
469  }
470 
471  // save it for optional rendering:
473  {
474  // [meters/N]
475  const double forceScale = world_->guiOptions_.force_scale;
476 
477  const mrpt::math::TPoint3D pt1(
478  wPt.x, wPt.y, chassis_z_max_ * 1.1 + getPose().z);
479  const mrpt::math::TPoint3D pt2 =
480  pt1 + mrpt::math::TPoint3D(wForce.x, wForce.y, 0) * forceScale;
481 
482  forceVectors.emplace_back(pt1, pt2);
483 
484  const mrpt::math::TPoint3D pt2_t =
485  pt1 + mrpt::math::TPoint3D(0, 0, wheelTorque[i]) * forceScale;
486 
487  torqueVectors.emplace_back(pt1, pt2_t);
488  }
489  }
490 
491  // Save forces for optional rendering:
493  {
494  std::lock_guard<std::mutex> csl(forceSegmentsForRenderingMtx_);
495  forceSegmentsForRendering_ = forceVectors;
496  torqueSegmentsForRendering_ = torqueVectors;
497  }
498 }
499 
503 {
505 
506  // Common part (update q_, dq_)
508  for (auto& s : sensors_) s->simul_post_timestep(context);
509 
510  // Integrate wheels' rotation:
511  const size_t nW = getNumWheels();
512 
513  for (size_t i = 0; i < nW; i++)
514  {
515  // prepare data:
516  Wheel& w = getWheelInfo(i);
517 
518  // Explicit Euler:
519  w.setPhi(w.getPhi() + w.getW() * context.dt);
520 
521  // Wrap wheel spin position (angle), so it doesn't
522  // become excessively large (it's actually unbound, but we don't want to
523  // lose 'double' accuracy):
524  const double cur_abs_phi = std::abs(w.getPhi());
525  if (cur_abs_phi > 1e4)
526  w.setPhi(
527  ::fmod(cur_abs_phi, 2 * M_PI) *
528  (w.getPhi() < 0.0 ? -1.0 : 1.0));
529  }
530 
531  const auto q = getPose();
532  const auto dq = getTwist();
533 
534  loggers_[LOGGER_POSE]->updateColumn(DL_TIMESTAMP, context.simul_time);
535  loggers_[LOGGER_POSE]->updateColumn(PL_Q_X, q.x);
536  loggers_[LOGGER_POSE]->updateColumn(PL_Q_Y, q.y);
537  loggers_[LOGGER_POSE]->updateColumn(PL_Q_Z, q.z);
538  loggers_[LOGGER_POSE]->updateColumn(PL_Q_YAW, q.yaw);
539  loggers_[LOGGER_POSE]->updateColumn(PL_Q_PITCH, q.pitch);
540  loggers_[LOGGER_POSE]->updateColumn(PL_Q_ROLL, q.roll);
541  loggers_[LOGGER_POSE]->updateColumn(PL_DQ_X, dq.vx);
542  loggers_[LOGGER_POSE]->updateColumn(PL_DQ_Y, dq.vy);
543  loggers_[LOGGER_POSE]->updateColumn(PL_DQ_Z, dq.omega);
544 
545  {
546  writeLogStrings();
547  }
548 }
549 
551 std::vector<mrpt::math::TVector2D> VehicleBase::getWheelsVelocityLocal(
552  const mrpt::math::TTwist2D& veh_vel_local) const
553 {
554  // Each wheel velocity is:
555  // v_w = v_veh + \omega \times wheel_pos
556  // =>
557  // v_w = v_veh + ( -w*y, w*x )
558 
559  const double w = veh_vel_local.omega; // vehicle w
560 
561  const size_t nW = this->getNumWheels();
562  std::vector<mrpt::math::TVector2D> vels(nW);
563 
564  for (size_t i = 0; i < nW; i++)
565  {
566  const Wheel& wheel = getWheelInfo(i);
567 
568  vels[i].x = veh_vel_local.vx - w * wheel.y;
569  vels[i].y = veh_vel_local.vy + w * wheel.x;
570  }
571  return vels;
572 }
573 
575  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& viz,
576  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& physical)
577 {
578  for (auto& s : sensors_) s->guiUpdate(viz, physical);
579 }
580 
582  [[maybe_unused]] mrpt::opengl::COpenGLScene& scene)
583 {
585  {
586  std::lock_guard<std::mutex> csl(forceSegmentsForRenderingMtx_);
587  glForces_->clear();
589  glForces_->setVisibility(true);
590 
591  glMotorTorques_->clear();
593  glMotorTorques_->setVisibility(true);
594  }
595  else
596  {
597  glForces_->setVisibility(false);
598  glMotorTorques_->setVisibility(false);
599  }
600 }
601 
603 {
604  using namespace mrpt::math;
605 
606  maxRadius_ = 0.001f;
607  for (const auto& pt : chassis_poly_)
608  {
609  const float n = pt.norm();
610  mrpt::keep_max(maxRadius_, n);
611  }
612 }
613 
616 {
617  // Define the dynamic body. We set its position and call the body factory.
618  b2BodyDef bodyDef;
619  bodyDef.type = b2_dynamicBody;
620 
621  b2dBody_ = world.CreateBody(&bodyDef);
622 
623  // Define shape of chassis:
624  // ------------------------------
625  {
626  // Convert shape into Box2D format:
627  const size_t nPts = chassis_poly_.size();
628  ASSERT_(nPts >= 3);
629  ASSERT_LE_(nPts, (size_t)b2_maxPolygonVertices);
630  std::vector<b2Vec2> pts(nPts);
631  for (size_t i = 0; i < nPts; i++)
632  pts[i] = b2Vec2(chassis_poly_[i].x, chassis_poly_[i].y);
633 
634  b2PolygonShape chassisPoly;
635  chassisPoly.Set(&pts[0], nPts);
636 
637  // FIXED value by design in b2Box: The "skin" depth of the body
638  chassisPoly.m_radius = 2.5e-3; // b2_polygonRadius;
639 
640  // Define the dynamic body fixture.
641  b2FixtureDef fixtureDef;
642  fixtureDef.shape = &chassisPoly;
643  fixtureDef.restitution = 0.01;
644 
645  // Set the box density to be non-zero, so it will be dynamic.
646  b2MassData mass;
647  // Mass with density=1 => compute area
648  chassisPoly.ComputeMass(&mass, 1);
649  fixtureDef.density = chassis_mass_ / mass.mass;
650 
651  // Override the default friction.
652  fixtureDef.friction = 0;
653 
654  // Add the shape to the body.
655  fixture_chassis_ = b2dBody_->CreateFixture(&fixtureDef);
656 
657  // Compute center of mass:
658  b2MassData vehMass;
659  fixture_chassis_->GetMassData(&vehMass);
660  chassis_com_.x = vehMass.center.x;
661  chassis_com_.y = vehMass.center.y;
662  }
663 
664  // Define shape of wheels:
665  // ------------------------------
666  ASSERT_EQUAL_(fixture_wheels_.size(), wheels_info_.size());
667 
668  for (size_t i = 0; i < wheels_info_.size(); i++)
669  {
670  b2PolygonShape wheelShape;
671  wheelShape.SetAsBox(
672  wheels_info_[i].diameter * 0.5, wheels_info_[i].width * 0.5,
673  b2Vec2(wheels_info_[i].x, wheels_info_[i].y), wheels_info_[i].yaw);
674 
675  // Define the dynamic body fixture.
676  b2FixtureDef fixtureDef;
677  fixtureDef.shape = &wheelShape;
678  fixtureDef.restitution = 0.05;
679 
680  // Set the box density to be non-zero, so it will be dynamic.
681  b2MassData mass;
682  wheelShape.ComputeMass(
683  &mass, 1); // Mass with density=1 => compute area
684  fixtureDef.density = wheels_info_[i].mass / mass.mass;
685 
686  // Override the default friction.
687  fixtureDef.friction = 0.5f;
688 
689  fixture_wheels_[i] = b2dBody_->CreateFixture(&fixtureDef);
690  }
691 }
692 
694  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& viz,
695  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& physical,
696  bool childrenOnly)
697 {
698  // 1st time call?? -> Create objects
699  // ----------------------------------
700  const size_t nWs = this->getNumWheels();
701  if (!glChassis_ && viz && physical)
702  {
703  glChassis_ = mrpt::opengl::CSetOfObjects::Create();
704  glChassis_->setName("vehicle_chassis_"s + name_);
705 
706  // Wheels shape:
707  glWheels_.resize(nWs);
708  for (size_t i = 0; i < nWs; i++)
709  {
710  glWheels_[i] = mrpt::opengl::CSetOfObjects::Create();
711  this->getWheelInfo(i).getAs3DObject(*glWheels_[i]);
712  glChassis_->insert(glWheels_[i]);
713  }
714 
715  if (!childrenOnly)
716  {
717  // Robot shape:
718  auto gl_poly = mrpt::opengl::CPolyhedron::CreateCustomPrism(
720  gl_poly->setLocation(0, 0, chassis_z_min_);
721  gl_poly->setColor_u8(chassis_color_);
722  glChassis_->insert(gl_poly);
723  }
724 
725  viz->get().insert(glChassis_);
726  physical->get().insert(glChassis_);
727  }
728 
729  // Update them:
730  // ----------------------------------
731  // If "viz" does not have a value, it's because we are already inside a
732  // setPose() change event, so my caller already holds the mutex and we don't
733  // need/can't acquire it again:
734  const auto objectPose = viz.has_value() ? getPose() : getPoseNoLock();
735 
736  if (glChassis_)
737  {
738  glChassis_->setPose(objectPose);
739  for (size_t i = 0; i < nWs; i++)
740  {
741  const Wheel& w = getWheelInfo(i);
742  glWheels_[i]->setPose(mrpt::math::TPose3D(
743  w.x, w.y, 0.5 * w.diameter, w.yaw, w.getPhi(), 0.0));
744 
745  if (!w.linked_yaw_object_name.empty())
746  {
747  auto glLinked = VisualObject::glCustomVisual_->getByName(
749  if (!glLinked)
750  {
751  THROW_EXCEPTION_FMT(
752  "Wheel #%zu has linked_yaw_object_name='%s' but parent "
753  "vehicle '%s' does not have any custom visual group "
754  "with that name.",
755  i, w.linked_yaw_object_name.c_str(), name_.c_str());
756  }
757  auto p = glLinked->getPose();
758  p.yaw = w.yaw + w.linked_yaw_offset;
759  glLinked->setPose(p);
760  }
761  }
762  }
763 
764  // Init on first use:
765  if (!glForces_ && viz)
766  {
767  // Visualization of forces:
768  glForces_ = mrpt::opengl::CSetOfLines::Create();
769  glForces_->setLineWidth(3.0);
770  glForces_->setColor_u8(0xff, 0xff, 0xff);
771  viz->get().insert(glForces_); // forces are in global coords
772  }
773  if (!glMotorTorques_ && viz)
774  {
775  // Visualization of forces:
776  glMotorTorques_ = mrpt::opengl::CSetOfLines::Create();
777  glMotorTorques_->setLineWidth(3.0);
778  glMotorTorques_->setColor_u8(0xff, 0x00, 0x00);
779  viz->get().insert(glMotorTorques_); // torques are in global coords
780  }
781 
782  // Other common stuff:
783  if (viz && physical)
784  {
785  internal_internalGuiUpdate_sensors(viz->get(), physical->get());
787  }
788 }
789 
791 {
792  loggers_[LOGGER_POSE] = std::make_shared<CSVLogger>();
793  // loggers_[LOGGER_POSE]->addColumn(DL_TIMESTAMP);
794  // loggers_[LOGGER_POSE]->addColumn(PL_Q_X);
795  // loggers_[LOGGER_POSE]->addColumn(PL_Q_Y);
796  // loggers_[LOGGER_POSE]->addColumn(PL_Q_Z);
797  // loggers_[LOGGER_POSE]->addColumn(PL_Q_YAW);
798  // loggers_[LOGGER_POSE]->addColumn(PL_Q_PITCH);
799  // loggers_[LOGGER_POSE]->addColumn(PL_Q_ROLL);
800  // loggers_[LOGGER_POSE]->addColumn(PL_DQ_X);
801  // loggers_[LOGGER_POSE]->addColumn(PL_DQ_Y);
802  // loggers_[LOGGER_POSE]->addColumn(PL_DQ_Z);
803  loggers_[LOGGER_POSE]->setFilepath(
804  log_path_ + "mvsim_" + name_ + LOGGER_POSE + ".log");
805 
806  for (size_t i = 0; i < getNumWheels(); i++)
807  {
808  loggers_[LOGGER_WHEEL + std::to_string(i + 1)] =
809  std::make_shared<CSVLogger>();
810  // loggers_[LOGGER_WHEEL + std::to_string(i +
811  // 1)]->addColumn(DL_TIMESTAMP);
812  // loggers_[LOGGER_WHEEL + std::to_string(i +
813  // 1)]->addColumn(WL_TORQUE);
814  // loggers_[LOGGER_WHEEL + std::to_string(i +
815  // 1)]->addColumn(WL_WEIGHT);
816  // loggers_[LOGGER_WHEEL + std::to_string(i +
817  // 1)]->addColumn(WL_VEL_X);
818  // loggers_[LOGGER_WHEEL + std::to_string(i +
819  // 1)]->addColumn(WL_VEL_Y);
820  // loggers_[LOGGER_WHEEL + std::to_string(i +
821  // 1)]->addColumn(WL_FRIC_X);
822  // loggers_[LOGGER_WHEEL + std::to_string(i +
823  // 1)]->addColumn(WL_FRIC_Y);
824  loggers_[LOGGER_WHEEL + std::to_string(i + 1)]->setFilepath(
825  log_path_ + "mvsim_" + name_ + LOGGER_WHEEL +
826  std::to_string(i + 1) + ".log");
827  }
828 }
829 
831 {
832  std::map<std::string, std::shared_ptr<CSVLogger>>::iterator it;
833  for (it = loggers_.begin(); it != loggers_.end(); ++it)
834  {
835  it->second->writeRow();
836  }
837 }
838 
840  const mrpt::math::TVector2D& force, const mrpt::math::TPoint2D& applyPoint)
841 {
842  ASSERT_(b2dBody_);
843  const b2Vec2 wPt = b2dBody_->GetWorldPoint(b2Vec2(
844  applyPoint.x, applyPoint.y)); // Application point -> world coords
845  b2dBody_->ApplyForce(b2Vec2(force.x, force.y), wPt, true /*wake up*/);
846 }
847 
849 {
850  // register myself, and my children objects:
852  for (auto& sensor : sensors_) sensor->registerOnServer(c);
853 }
854 
856 {
857  if (glChassis_) glChassis_->setVisibility(visible);
858  for (auto& glW : glWheels_)
859  {
860  if (glW) glW->setVisibility(visible);
861  }
862 }
const b2Shape * shape
Definition: b2_fixture.h:76
mrpt::opengl::CSetOfObjects::Ptr glChassis_
Definition: VehicleBase.h:226
double force_scale
In meters/Newton.
Definition: World.h:457
void internal_internalGuiUpdate_forces(mrpt::opengl::COpenGLScene &scene)
This file contains rapidxml parser and DOM implementation.
float mass
The mass of the shape, usually in kilograms.
Definition: b2_shape.h:36
void updateMaxRadiusFromPoly()
float density
The density, usually in kg/m^2.
Definition: b2_fixture.h:92
const Wheel & getWheelInfo(const size_t idx) const
Definition: VehicleBase.h:81
virtual const char * what() const
Definition: rapidxml.hpp:85
static constexpr char PL_DQ_Y[]
Definition: VehicleBase.h:247
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
mrpt::math::TPose3D getPose() const
Definition: Simulable.cpp:438
virtual double getChassisMass() const
Definition: VehicleBase.h:73
std::vector< mrpt::math::TSegment3D > forceSegmentsForRendering_
Definition: VehicleBase.h:231
static constexpr char PL_Q_ROLL[]
Definition: VehicleBase.h:245
virtual void invoke_motor_controllers_post_step([[maybe_unused]] const TSimulContext &context)
Definition: VehicleBase.h:171
float x
Definition: b2_math.h:128
float y
Definition: b2_math.h:128
std::string log_path_
Definition: VehicleBase.h:149
mrpt::math::TPose3D getPoseNoLock() const
No thread-safe version. Used internally only.
Definition: Simulable.cpp:446
virtual void simul_post_timestep(const TSimulContext &context) override
XmlRpcServer s
std::map< std::string, std::shared_ptr< CSVLogger > > loggers_
Definition: VehicleBase.h:148
double simul_time
Current time in the simulated world.
Definition: basic_types.h:59
#define REGISTER_VEHICLE_DYNAMICS(TEXTUAL_NAME, CLASS_NAME)
Definition: VehicleBase.h:265
virtual void initLoggers()
mrpt::math::TTwist2D getTwist() const
Definition: Simulable.cpp:448
const rapidxml::xml_node< Ch > * first_node(const char *name) const
Definition: JointXMLnode.h:30
static constexpr char LOGGER_WHEEL[]
Definition: VehicleBase.h:238
static constexpr char WL_VEL_X[]
Definition: VehicleBase.h:252
void parse(Ch *text)
Definition: rapidxml.hpp:1381
mrpt::opengl::CSetOfLines::Ptr glForces_
Definition: VehicleBase.h:228
std::unique_ptr< b2World > & getBox2DWorld()
Definition: World.h:297
void getAs3DObject(mrpt::opengl::CSetOfObjects &obj)
Definition: Wheel.cpp:27
std::shared_ptr< CSVLogger > getLoggerPtr(std::string logger_name)
Definition: VehicleBase.h:100
static constexpr char PL_Q_X[]
Definition: VehicleBase.h:240
#define M_PI
static constexpr char WL_TORQUE[]
Definition: VehicleBase.h:250
mrpt::opengl::CSetOfLines::Ptr glMotorTorques_
Definition: VehicleBase.h:229
b2Vec2 center
The position of the shape&#39;s centroid relative to the shape&#39;s origin.
Definition: b2_shape.h:39
Ch * name() const
Definition: rapidxml.hpp:673
std::deque< Wheel > wheels_info_
Definition: VehicleBase.h:209
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
static constexpr char PL_Q_Z[]
Definition: VehicleBase.h:242
static constexpr char PL_Q_PITCH[]
Definition: VehicleBase.h:244
void ComputeMass(b2MassData *massData, float density) const override
double getW() const
Spinning velocity (rad/s) wrt shaft.
Definition: Wheel.h:92
void SetAsBox(float hx, float hy)
std::shared_ptr< mrpt::opengl::CSetOfObjects > glCustomVisual_
Definition: VisualObject.h:75
mrpt::math::TPoint2D chassis_com_
Definition: VehicleBase.h:201
double x
Definition: Wheel.h:38
TClassFactory_vehicleDynamics classFactory_vehicleDynamics
Definition: VehicleBase.cpp:40
static constexpr char PL_Q_YAW[]
Definition: VehicleBase.h:243
void setShapeManual(const mrpt::math::TPolygon2D &contour, const float zMin, const float zMax)
Definition: Shape2p5.cpp:219
static constexpr char PL_DQ_Z[]
Definition: VehicleBase.h:248
b2BodyType type
Definition: b2_body.h:74
double dt
timestep
Definition: basic_types.h:60
std::vector< mrpt::math::TSegment3D > torqueSegmentsForRendering_
Definition: VehicleBase.h:232
float m_radius
Definition: b2_shape.h:102
VehicleBase(World *parent, size_t nWheels)
Definition: VehicleBase.cpp:84
void GetMassData(b2MassData *massData) const
Definition: b2_fixture.h:354
void setPhi(double val)
Orientation (rad) wrt vehicle local frame.
Definition: Wheel.h:88
double y
Definition: Wheel.h:38
std::shared_ptr< SensorBase > Ptr
Definition: SensorBase.h:29
std::mutex forceSegmentsForRenderingMtx_
Definition: VehicleBase.h:233
virtual void create_multibody_system(b2World &world)
std::shared_ptr< VehicleBase > Ptr
Definition: VehicleBase.h:44
std::string parse(const std::string &input, const std::map< std::string, std::string > &variableNamesValues={})
double diameter
Definition: Wheel.h:44
void chassisAndWheelsVisible(bool visible)
std::vector< mrpt::opengl::CSetOfObjects::Ptr > glWheels_
Definition: VehicleBase.h:227
std::string name_
Definition: Simulable.h:120
std::tuple< XML_Doc_Data::Ptr, rapidxml::xml_node<> * > readXmlAndGetRoot(const std::string &pathToFile, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain={})
Definition: xml_utils.cpp:333
static constexpr char PL_Q_Y[]
Definition: VehicleBase.h:241
static constexpr char WL_FRIC_X[]
Definition: VehicleBase.h:254
static constexpr char PL_DQ_X[]
Definition: VehicleBase.h:246
mrpt::math::TPolygon2D chassis_poly_
Definition: VehicleBase.h:189
b2Fixture * fixture_chassis_
Created at.
Definition: VehicleBase.h:212
static void register_vehicle_class(const rapidxml::xml_node< char > *xml_node)
virtual void registerOnServer(mvsim::Client &c)
Definition: Simulable.cpp:406
void registerOnServer(mvsim::Client &c) override
double yaw
Definition: Wheel.h:38
virtual void simul_post_timestep(const TSimulContext &context)
Definition: Simulable.cpp:59
double getPhi() const
Orientation (rad) wrt vehicle local frame.
Definition: Wheel.h:84
virtual void simul_pre_timestep(const TSimulContext &context)
Definition: Simulable.cpp:30
Ch * value() const
Definition: rapidxml.hpp:692
virtual void internalGuiUpdate(const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &viz, const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &physical, bool childrenOnly) override
void Set(const b2Vec2 *points, int32 count)
virtual std::vector< double > invoke_motor_controllers(const TSimulContext &context)=0
virtual void writeLogStrings()
double linked_yaw_offset
Definition: Wheel.h:59
std::string linked_yaw_object_name
Definition: Wheel.h:58
mrpt::math::TTwist2D getVelocityLocal() const
Definition: Simulable.cpp:116
TListSensors sensors_
Sensors aboard.
Definition: VehicleBase.h:185
Ch * where() const
Definition: rapidxml.hpp:94
std::string local_to_abs_path(const std::string &in_path) const
Definition: World.cpp:252
TGUI_Options guiOptions_
Definition: World.h:495
Ptr create(const std::string &class_name, ARG1 a1) const
Definition: ClassFactory.h:40
mrpt::img::TColor chassis_color_
Definition: VehicleBase.h:197
static constexpr char WL_VEL_Y[]
Definition: VehicleBase.h:253
#define b2_maxPolygonVertices
Definition: b2_settings.h:53
static constexpr char LOGGER_POSE[]
Definition: VehicleBase.h:237
static FrictionBase::Ptr factory(VehicleBase &parent, const rapidxml::xml_node< char > *xml_node)
FrictionBasePtr friction_
Definition: VehicleBase.h:183
This file contains rapidxml printer implementation.
void internal_internalGuiUpdate_sensors(const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &viz, const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &physical)
const rapidxml::xml_node< char > * get(const std::string &xml_node_class) const
void add(const std::string &input_xml_node_class)
static constexpr char WL_WEIGHT[]
Definition: VehicleBase.h:251
void register_all_veh_dynamics()
Definition: VehicleBase.cpp:44
static XmlClassesRegistry veh_classes_registry("vehicle:class")
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004
std::vector< mrpt::math::TVector2D > getWheelsVelocityLocal(const mrpt::math::TTwist2D &veh_vel_local) const
static SensorBase::Ptr factory(Simulable &parent, const rapidxml::xml_node< char > *xml_node)
Definition: SensorBase.cpp:92
static constexpr char WL_FRIC_Y[]
Definition: VehicleBase.h:255
float restitution
The restitution (elasticity) usually in the range [0,1].
Definition: b2_fixture.h:85
This holds the mass data computed for a shape.
Definition: b2_shape.h:33
virtual void apply_force(const mrpt::math::TVector2D &force, const mrpt::math::TPoint2D &applyPoint=mrpt::math::TPoint2D(0, 0)) override
void add(const rapidxml::xml_node< Ch > *node)
Definition: JointXMLnode.h:28
mrpt::math::TVector2D wheelCogLocalVel
Definition: FrictionBase.h:49
size_t getNumWheels() const
Definition: VehicleBase.h:80
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
b2Body * CreateBody(const b2BodyDef *def)
Definition: b2_world.cpp:115
std::vector< b2Fixture * > fixture_wheels_
Definition: VehicleBase.h:216
float friction
The friction coefficient, usually in the range [0,1].
Definition: b2_fixture.h:82
double get_gravity() const
Definition: World.h:136
static constexpr char DL_TIMESTAMP[]
Definition: VehicleBase.h:236
virtual void simul_pre_timestep(const TSimulContext &context) override


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