VehicleAckermann_Drivetrain.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/opengl/COpenGLScene.h>
12 #include <mvsim/World.h>
13 
14 #include <cmath>
15 #include <rapidxml.hpp>
16 
17 #include "xml_utils.h"
18 
19 using namespace mvsim;
20 using namespace std;
21 
22 // Ctor:
24  : VehicleBase(parent, 4 /*num wheels*/), max_steer_ang_(mrpt::DEG2RAD(30))
25 {
26  using namespace mrpt::math;
27 
28  chassis_mass_ = 500.0;
29  chassis_z_min_ = 0.20;
30  chassis_z_max_ = 1.40;
31  chassis_color_ = mrpt::img::TColor(0xe8, 0x30, 0x00);
32 
33  // Default shape:
34  chassis_poly_.clear();
35  chassis_poly_.emplace_back(-0.8, -1.0);
36  chassis_poly_.emplace_back(-0.8, 1.0);
37  chassis_poly_.emplace_back(1.5, 0.9);
38  chassis_poly_.emplace_back(1.8, 0.8);
39  chassis_poly_.emplace_back(1.8, -0.8);
40  chassis_poly_.emplace_back(1.5, -0.9);
42 
43  fixture_chassis_ = nullptr;
44  for (int i = 0; i < 4; i++) fixture_wheels_[i] = nullptr;
45 
46  // Default values:
47  // rear-left:
48  wheels_info_[WHEEL_RL].x = 0;
49  wheels_info_[WHEEL_RL].y = 0.9;
50  // rear-right:
51  wheels_info_[WHEEL_RR].x = 0;
52  wheels_info_[WHEEL_RR].y = -0.9;
53  // Front-left:
54  wheels_info_[WHEEL_FL].x = 1.3;
55  wheels_info_[WHEEL_FL].y = 0.9;
56  // Front-right:
57  wheels_info_[WHEEL_FR].x = 1.3;
58  wheels_info_[WHEEL_FR].y = -0.9;
59 
60  FrontRearSplit_ = 0.5;
61  FrontLRSplit_ = 0.5;
62  RearLRSplit_ = 0.5;
63 
64  FrontRearBias_ = 1.5;
65  FrontLRBias_ = 1.5;
66  RearLRBias_ = 1.5;
67 
69 }
70 
73  const rapidxml::xml_node<char>* xml_node)
74 {
75  // <chassis ...> </chassis>
76  const rapidxml::xml_node<char>* xml_chassis =
77  xml_node->first_node("chassis");
78  if (xml_chassis)
79  {
80  // Attribs:
81  TParameterDefinitions attribs;
82  attribs["mass"] = TParamEntry("%lf", &this->chassis_mass_);
83  attribs["zmin"] = TParamEntry("%lf", &this->chassis_z_min_);
84  attribs["zmax"] = TParamEntry("%lf", &this->chassis_z_max_);
85  attribs["color"] = TParamEntry("%color", &this->chassis_color_);
86 
88  *xml_chassis, attribs, {},
89  "[DynamicsAckermannDrivetrain::dynamics_load_params_from_xml]");
90 
91  // Shape node (optional, fallback to default shape if none found)
92  const rapidxml::xml_node<char>* xml_shape =
93  xml_chassis->first_node("shape");
94  if (xml_shape)
96  *xml_shape, chassis_poly_,
97  "[DynamicsAckermannDrivetrain::dynamics_load_params_from_xml]");
98  }
99 
100  //<rl_wheel pos="0 1" mass="6.0" width="0.30" diameter="0.62" />
101  //<rr_wheel pos="0 -1" mass="6.0" width="0.30" diameter="0.62" />
102  //<fl_wheel mass="6.0" width="0.30" diameter="0.62" />
103  //<fr_wheel mass="6.0" width="0.30" diameter="0.62" />
104  const char* w_names[4] = {
105  "rl_wheel", // 0
106  "rr_wheel", // 1
107  "fl_wheel", // 2
108  "fr_wheel" // 3
109  };
110  // Load common params:
111  for (size_t i = 0; i < 4; i++)
112  {
113  if (auto xml_wheel = xml_node->first_node(w_names[i]); xml_wheel)
114  {
115  wheels_info_[i].loadFromXML(xml_wheel);
116  }
117  else
118  {
119  world_->logFmt(
120  mrpt::system::LVL_WARN,
121  "No XML entry '%s' found: using defaults for wheel #%u",
122  w_names[i], static_cast<unsigned int>(i));
123  }
124  }
125 
126  //<f_wheels_x>1.3</f_wheels_x>
127  //<f_wheels_d>2.0</f_wheels_d>
128  // Load front ackermann wheels and other params:
129  {
130  double front_x = 1.3;
131  double front_d = 2.0;
132  TParameterDefinitions ack_ps;
133  // Front wheels:
134  ack_ps["f_wheels_x"] = TParamEntry("%lf", &front_x);
135  ack_ps["f_wheels_d"] = TParamEntry("%lf", &front_d);
136  // other params:
137  ack_ps["max_steer_ang_deg"] = TParamEntry("%lf_deg", &max_steer_ang_);
138 
140  *xml_node, ack_ps, {},
141  "[DynamicsAckermannDrivetrain::dynamics_load_params_from_xml]");
142 
143  // Front-left:
144  wheels_info_[WHEEL_FL].x = front_x;
145  wheels_info_[WHEEL_FL].y = 0.5 * front_d;
146  // Front-right:
147  wheels_info_[WHEEL_FR].x = front_x;
148  wheels_info_[WHEEL_FR].y = -0.5 * front_d;
149  }
150 
151  // Drivetrain parameters
152  // Watch the order of DifferentialType enum!
153  const char* drive_names[6] = {"open_front", "open_rear", "open_4wd",
154  "torsen_front", "torsen_rear", "torsen_4wd"};
155 
156  const rapidxml::xml_node<char>* xml_drive =
157  xml_node->first_node("drivetrain");
158  if (xml_drive)
159  {
160  TParameterDefinitions attribs;
161  std::string diff_type;
162  attribs["type"] = TParamEntry("%s", &diff_type);
163 
165  *xml_drive, attribs, {},
166  "[DynamicsAckermannDrivetrain::dynamics_load_params_from_xml]");
167 
168  for (size_t i = 0; i < DifferentialType::DIFF_MAX; ++i)
169  {
170  if (diff_type == drive_names[i])
171  {
173  break;
174  }
175  }
176 
177  TParameterDefinitions drive_params;
178  drive_params["front_rear_split"] = TParamEntry("%lf", &FrontRearSplit_);
179  drive_params["front_rear_bias"] = TParamEntry("%lf", &FrontRearBias_);
180  drive_params["front_left_right_split"] =
181  TParamEntry("%lf", &FrontLRSplit_);
182  drive_params["front_left_right_bias"] =
183  TParamEntry("%lf", &FrontLRBias_);
184  drive_params["rear_left_right_split"] =
185  TParamEntry("%lf", &RearLRSplit_);
186  drive_params["rear_left_right_bias"] = TParamEntry("%lf", &RearLRBias_);
187 
189  *xml_drive, drive_params, {},
190  "[DynamicsAckermannDrivetrain::dynamics_load_params_from_xml]");
191  }
192 
193  // Vehicle controller:
194  // -------------------------------------------------
195  {
196  const rapidxml::xml_node<char>* xml_control =
197  xml_node->first_node("controller");
198  if (xml_control)
199  {
200  rapidxml::xml_attribute<char>* control_class =
201  xml_control->first_attribute("class");
202  if (!control_class || !control_class->value())
203  throw runtime_error(
204  "[DynamicsAckermannDrivetrain] Missing 'class' attribute "
205  "in <controller> XML node");
206 
207  const std::string sCtrlClass = std::string(control_class->value());
208  if (sCtrlClass == ControllerRawForces::class_name())
209  controller_ = std::make_shared<ControllerRawForces>(*this);
210  else if (sCtrlClass == ControllerTwistFrontSteerPID::class_name())
211  controller_ =
212  std::make_shared<ControllerTwistFrontSteerPID>(*this);
213  else if (sCtrlClass == ControllerFrontSteerPID::class_name())
214  controller_ = std::make_shared<ControllerFrontSteerPID>(*this);
215  else
216  THROW_EXCEPTION_FMT(
217  "[DynamicsAckermannDrivetrain] Unknown 'class'='%s' in "
218  "<controller> XML node",
219  sCtrlClass.c_str());
220 
221  controller_->load_config(*xml_control);
222  }
223  }
224 
225  // Default controller:
226  if (!controller_)
227  controller_ = std::make_shared<ControllerRawForces>(*this);
228 }
229 
230 // See docs in base class:
232  const TSimulContext& context)
233 {
234  // Longitudinal forces at each wheel:
235  std::vector<double> out_torque_per_wheel;
236  out_torque_per_wheel.assign(4, 0.0);
237  double torque_split_per_wheel[4] = {0.0};
238 
239  if (controller_)
240  {
241  // Invoke controller:
242  TControllerInput ci;
243  ci.context = context;
245  controller_->control_step(ci, co);
246  // Take its output:
247 
248  switch (diff_type_)
249  {
250  case DIFF_OPEN_FRONT:
251  {
252  torque_split_per_wheel[WHEEL_FL] = FrontLRSplit_;
253  torque_split_per_wheel[WHEEL_FR] = 1. - FrontLRSplit_;
254  torque_split_per_wheel[WHEEL_RL] = 0.0;
255  torque_split_per_wheel[WHEEL_RR] = 0.0;
256  }
257  break;
258  case DIFF_OPEN_REAR:
259  {
260  torque_split_per_wheel[WHEEL_FL] = 0.0;
261  torque_split_per_wheel[WHEEL_FR] = 0.0;
262  torque_split_per_wheel[WHEEL_RL] = RearLRSplit_;
263  torque_split_per_wheel[WHEEL_RR] = 1. - RearLRSplit_;
264  }
265  break;
266  case DIFF_OPEN_4WD:
267  {
268  const double front = FrontRearSplit_;
269  const double rear = 1. - FrontRearSplit_;
270 
271  torque_split_per_wheel[WHEEL_FL] = front * FrontLRSplit_;
272  torque_split_per_wheel[WHEEL_FR] = front * (1. - FrontLRSplit_);
273  torque_split_per_wheel[WHEEL_RL] = rear * RearLRSplit_;
274  torque_split_per_wheel[WHEEL_RR] = rear * (1. - RearLRSplit_);
275  }
276  break;
277  case DIFF_TORSEN_FRONT:
278  {
279  // no torque to rear
280  torque_split_per_wheel[WHEEL_RL] = 0.0;
281  torque_split_per_wheel[WHEEL_RR] = 0.0;
282 
284  wheels_info_[WHEEL_FL].getW(),
286  torque_split_per_wheel[WHEEL_FL],
287  torque_split_per_wheel[WHEEL_FR]);
288  }
289  break;
290  case DIFF_TORSEN_REAR:
291  {
292  // no torque to front
293  torque_split_per_wheel[WHEEL_FL] = 0.0;
294  torque_split_per_wheel[WHEEL_FR] = 0.0;
295 
297  wheels_info_[WHEEL_RL].getW(),
299  torque_split_per_wheel[WHEEL_RL],
300  torque_split_per_wheel[WHEEL_RR]);
301  }
302  break;
303  case DIFF_TORSEN_4WD:
304  {
305  // here we have magic
306  const double w_FL = wheels_info_[WHEEL_FL].getW();
307  const double w_FR = wheels_info_[WHEEL_FR].getW();
308  const double w_RL = wheels_info_[WHEEL_RL].getW();
309  const double w_RR = wheels_info_[WHEEL_RR].getW();
310  const double w_F = w_FL + w_FR;
311  const double w_R = w_RL + w_RR;
312 
313  double t_F = 0.0;
314  double t_R = 0.0;
315  // front-rear
317  w_F, w_R, FrontRearBias_, FrontRearSplit_, t_F, t_R);
318 
319  double t_FL = 0.0;
320  double t_FR = 0.0;
321  // front left-right
323  w_FL, w_FR, FrontLRBias_, FrontLRSplit_, t_FL, t_FR);
324 
325  double t_RL = 0.0;
326  double t_RR = 0.0;
327  // rear left-right
329  w_RL, w_RR, RearLRBias_, RearLRSplit_, t_RL, t_RR);
330 
331  torque_split_per_wheel[WHEEL_FL] = t_F * t_FL;
332  torque_split_per_wheel[WHEEL_FR] = t_F * t_FR;
333  torque_split_per_wheel[WHEEL_RL] = t_R * t_RL;
334  torque_split_per_wheel[WHEEL_RR] = t_R * t_RR;
335  }
336  break;
337  default:
338  {
339  // fatal - unknown diff!
340  ASSERTMSG_(
341  0,
342  "DynamicsAckermannDrivetrain::invoke_motor_controllers: \
343  Unknown differential type!");
344  }
345  break;
346  }
347 
348  ASSERT_(out_torque_per_wheel.size() == 4);
349  for (int i = 0; i < 4; i++)
350  {
351  out_torque_per_wheel[i] =
352  co.drive_torque * torque_split_per_wheel[i];
353  }
354 
355  // Kinematically-driven steering wheels:
356  // Ackermann formulas for inner&outer weels turning angles wrt the
357  // equivalent (central) one:
360  wheels_info_[WHEEL_FR].yaw);
361  }
362  return out_torque_per_wheel;
363 }
364 
366  const double desired_equiv_steer_ang, double& out_fl_ang,
367  double& out_fr_ang) const
368 {
369  // EQ1: cot(d)+0.5*w/l = cot(do)
370  // EQ2: cot(di)=cot(do)-w/l
371  const double w = wheels_info_[WHEEL_FL].y - wheels_info_[WHEEL_FR].y;
372  const double l = wheels_info_[WHEEL_FL].x - wheels_info_[WHEEL_RL].x;
373  ASSERT_(l > 0);
374  const double w_l = w / l;
375  const double delta =
376  b2Clamp(std::abs(desired_equiv_steer_ang), 0.0, max_steer_ang_);
377 
378  const bool delta_neg = (desired_equiv_steer_ang < 0);
379  ASSERT_LT_(delta, 0.5 * M_PI - 0.01);
380  const double cot_do = 1.0 / tan(delta) + 0.5 * w_l;
381  const double cot_di = cot_do - w_l;
382  // delta>0: do->right, di->left wheel
383  // delta<0: do->left , di->right wheel
384  (delta_neg ? out_fr_ang : out_fl_ang) =
385  atan(1.0 / cot_di) * (delta_neg ? -1.0 : 1.0);
386  (delta_neg ? out_fl_ang : out_fr_ang) =
387  atan(1.0 / cot_do) * (delta_neg ? -1.0 : 1.0);
388 }
389 
391  const double w1, const double w2, const double diffBias,
392  const double splitRatio, double& t1, double& t2)
393 {
394  if (mrpt::signWithZero(w1) == 0.0 || mrpt::signWithZero(w2) == 0.0)
395  {
396  t1 = splitRatio;
397  t2 = 1.0 - splitRatio;
398  return;
399  }
400 
401  const double w1Abs = std::abs(w1);
402  const double w2Abs = std::abs(w2);
403  const double omegaMax = std::max(w1Abs, w2Abs);
404  const double omegaMin = std::min(w1Abs, w2Abs);
405 
406  const double delta = omegaMax - diffBias * omegaMin;
407 
408  const double deltaTorque = (delta > 0) ? delta / omegaMax : 0.0f;
409  const double f1 = (w1Abs - w2Abs > 0) ? splitRatio * (1.0f - deltaTorque)
410  : splitRatio * (1.0f + deltaTorque);
411  const double f2 = (w1Abs - w2Abs > 0)
412  ? (1.0f - splitRatio) * (1.0f + deltaTorque)
413  : (1.0f - splitRatio) * (1.0f - deltaTorque);
414  const double denom = 1.0f / (f1 + f2);
415 
416  t1 = f1 * denom;
417  t2 = f2 * denom;
418 }
419 
420 // See docs in base class:
422  const
423 {
424  mrpt::math::TTwist2D odo_vel;
425  // Equations:
426 
427  // Velocities in local +X at each wheel i={0,1}:
428  // v_i = vx - w_veh * wheel_{i,y} = w_i * R_i
429  // Re-arranging:
430  const double w0 = wheels_info_[WHEEL_RL].getW();
431  const double w1 = wheels_info_[WHEEL_RR].getW();
432  const double R0 = wheels_info_[WHEEL_RL].diameter * 0.5;
433  const double R1 = wheels_info_[WHEEL_RR].diameter * 0.5;
434 
435  const double Ay = wheels_info_[WHEEL_RL].y - wheels_info_[WHEEL_RR].y;
436  ASSERTMSG_(
437  Ay != 0.0,
438  "The two wheels of a differential vehicle cannot be at the same Y "
439  "coordinate!");
440 
441  const double w_veh = (w1 * R1 - w0 * R0) / Ay;
442  const double vx_veh = w0 * R0 + w_veh * wheels_info_[WHEEL_RL].y;
443 
444  odo_vel.vx = vx_veh;
445  odo_vel.vy = 0.0;
446  odo_vel.omega = w_veh;
447 
448  return odo_vel;
449 }
This file contains rapidxml parser and DOM implementation.
void updateMaxRadiusFromPoly()
DifferentialType diff_type_
min turning radius
std::map< std::string, TParamEntry > TParameterDefinitions
ControllerBase::Ptr controller_
The installed controller.
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
virtual mrpt::math::TTwist2D getVelocityLocalOdoEstimate() const override
void parse_xmlnode_attribs(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:184
#define M_PI
virtual void dynamics_load_params_from_xml(const rapidxml::xml_node< char > *xml_node) override
std::deque< Wheel > wheels_info_
Definition: VehicleBase.h:209
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
INLINE Rall1d< T, V, S > atan(const Rall1d< T, V, S > &x)
void computeDiffTorqueSplit(const double w1, const double w2, const double diffBias, const double defaultSplitRatio, double &t1, double &t2)
mrpt::math::TPolygon2D chassis_poly_
Definition: VehicleBase.h:189
b2Fixture * fixture_chassis_
Created at.
Definition: VehicleBase.h:212
T b2Clamp(T a, T low, T high)
Definition: b2_math.h:648
Ch * value() const
Definition: rapidxml.hpp:692
double steer_ang
Equivalent Ackermann steering angle.
mrpt::img::TColor chassis_color_
Definition: VehicleBase.h:197
static int min(int n1, int n2)
Definition: wl_init.c:42
#define DEG2RAD(x)
void parse_xmlnode_shape(const rapidxml::xml_node< char > &xml_node, mrpt::math::TPolygon2D &out_poly, const char *functionNameContext="")
Definition: xml_utils.cpp:271
virtual std::vector< double > invoke_motor_controllers(const TSimulContext &context) override
INLINE Rall1d< T, V, S > tan(const Rall1d< T, V, S > &arg)
void computeFrontWheelAngles(const double desired_equiv_steer_ang, double &out_fl_ang, double &out_fr_ang) const
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
std::vector< b2Fixture * > fixture_wheels_
Definition: VehicleBase.h:216


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