Block.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014 Jose Luis Blanco Claraco (University of Almeria) |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under GNU General Public License version 3 |
7  | See <http://www.gnu.org/licenses/> |
8  +-------------------------------------------------------------------------+ */
9 
10 #include <mvsim/World.h>
11 #include <mvsim/Block.h>
12 
13 #include "JointXMLnode.h"
14 #include "XMLClassesRegistry.h"
15 #include "xml_utils.h"
16 
17 #include <rapidxml.hpp>
18 #include <rapidxml_utils.hpp>
19 #include <rapidxml_print.hpp>
20 #include <mrpt/poses/CPose2D.h>
21 #include <mrpt/opengl/CPolyhedron.h>
22 
23 #include <mrpt/version.h>
24 #if MRPT_VERSION < 0x199
25 #include <mrpt/utils/utils_defs.h> // mrpt::format()
26 using namespace mrpt::utils;
27 #else
28 #include <mrpt/math/TPose2D.h>
29 #include <mrpt/core/format.h>
30 #include <mrpt/core/bits_math.h>
31 using namespace mrpt::img;
32 using namespace mrpt;
33 #endif
34 
35 #include <sstream> // std::stringstream
36 #include <map>
37 #include <string>
38 
39 using namespace mvsim;
40 using namespace std;
41 
43 
44 // Protected ctor:
45 Block::Block(World* parent)
46  : VisualObject(parent),
47  m_block_index(0),
48  m_b2d_block_body(NULL),
49  m_q(0, 0, 0, 0, 0, 0),
50  m_dq(0, 0, 0),
51  m_mass(30.0),
52  m_block_z_min(0.0),
53  m_block_z_max(1.0),
54  m_block_color(0x00, 0x00, 0xff),
55  m_block_com(.0, .0),
56  m_lateral_friction(0.5),
57  m_ground_friction(0.5),
58  m_restitution(0.01)
59 {
60  using namespace mrpt::math;
61  // Default shape:
62  m_block_poly.push_back(TPoint2D(-0.5, -0.5));
63  m_block_poly.push_back(TPoint2D(-0.5, 0.5));
64  m_block_poly.push_back(TPoint2D(0.5, 0.5));
65  m_block_poly.push_back(TPoint2D(0.5, -0.5));
66  updateMaxRadiusFromPoly();
67 }
68 
70 {
71  if (m_b2d_block_body)
72  {
73  // Pos:
74  const b2Vec2& pos = m_b2d_block_body->GetPosition();
75  const float32 angle = m_b2d_block_body->GetAngle();
76  m_q.x = pos(0);
77  m_q.y = pos(1);
78  m_q.yaw = angle;
79  // The rest (z,pitch,roll) will be always 0, unless other world-element
80  // modifies them! (e.g. elevation map)
81 
82  // Vel:
83  const b2Vec2& vel = m_b2d_block_body->GetLinearVelocity();
84  const float32 w = m_b2d_block_body->GetAngularVelocity();
85  m_dq.vals[0] = vel(0);
86  m_dq.vals[1] = vel(1);
87  m_dq.vals[2] = w;
88  }
89 }
90 
94 {
95  // Sanity checks:
96  if (!xml_node)
97  throw runtime_error("[Block::register_vehicle_class] XML node is NULL");
98  if (0 != strcmp(xml_node->name(), "block:class"))
99  throw runtime_error(mrpt::format(
100  "[Block::register_block_class] XML element is '%s' "
101  "('block:class' expected)",
102  xml_node->name()));
103 
104  // rapidxml doesn't allow making copied of objects.
105  // So: convert to txt; then re-parse.
106  std::stringstream ss;
107  ss << *xml_node;
108 
109  block_classes_registry.add(ss.str());
110 }
111 
115 {
116  using namespace std;
117  using namespace rapidxml;
118 
119  if (!root) throw runtime_error("[Block::factory] XML node is NULL");
120  if (0 != strcmp(root->name(), "block"))
121  throw runtime_error(mrpt::format(
122  "[Block::factory] XML root element is '%s' ('block' expected)",
123  root->name()));
124 
125  // "class": When there is a 'class="XXX"' attribute, look for each parameter
126  // in the set of "root" + "class_root" XML nodes:
127  // --------------------------------------------------------------------------------
128  JointXMLnode<> block_root_node;
129  const rapidxml::xml_node<char>* class_root = NULL;
130  {
131  block_root_node.add(
132  root); // Always search in root. Also in the class root, if any:
133  const xml_attribute<>* block_class = root->first_attribute("class");
134  if (block_class)
135  {
136  const string sClassName = block_class->value();
137  class_root = block_classes_registry.get(sClassName);
138  if (!class_root)
139  throw runtime_error(mrpt::format(
140  "[Block::factory] Block class '%s' undefined",
141  sClassName.c_str()));
142  block_root_node.add(class_root);
143  }
144  }
145 
146  // Build object (we don't use class factory for blocks)
147  // ----------------------------------------------------
148  Block* block = new Block(parent);
149 
150  // Init params
151  // -------------------------------------------------
152  // attrib: name
153  {
154  const xml_attribute<>* attrib_name = root->first_attribute("name");
155  if (attrib_name && attrib_name->value())
156  {
157  block->m_name = attrib_name->value();
158  }
159  else
160  {
161  // Default name:
162  static int cnt = 0;
163  block->m_name = mrpt::format("block%i", ++cnt);
164  }
165  }
166 
167  // (Mandatory) initial pose:
168  {
169  const xml_node<>* node = block_root_node.first_node("init_pose");
170  if (!node)
171  throw runtime_error(
172  "[Block::factory] Missing XML node <init_pose>");
173 
174  if (3 != ::sscanf(
175  node->value(), "%lf %lf %lf", &block->m_q.x, &block->m_q.y,
176  &block->m_q.yaw))
177  throw runtime_error(
178  "[Block::factory] Error parsing <init_pose>...</init_pose>");
179  block->m_q.yaw *= M_PI / 180.0; // deg->rad
180  }
181 
182  // (Optional) initial vel:
183  {
184  const xml_node<>* node = block_root_node.first_node("init_vel");
185  if (node)
186  {
187  if (3 != ::sscanf(
188  node->value(), "%lf %lf %lf", &block->m_dq.vals[0],
189  &block->m_dq.vals[1], &block->m_dq.vals[2]))
190  throw runtime_error(
191  "[Block::factory] Error parsing <init_vel>...</init_vel>");
192  block->m_dq.vals[2] *= M_PI / 180.0; // deg->rad
193 
194  // Convert twist (velocity) from local -> global coords:
195  const mrpt::poses::CPose2D pose(
196  0, 0, block->m_q.yaw); // Only the rotation
197  pose.composePoint(
198  block->m_dq.vals[0], block->m_dq.vals[1], block->m_dq.vals[0],
199  block->m_dq.vals[1]);
200  }
201  }
202 
203  // Params:
204  std::map<std::string, TParamEntry> params;
205  params["mass"] = TParamEntry("%lf", &block->m_mass);
206  params["zmin"] = TParamEntry("%lf", &block->m_block_z_min);
207  params["zmax"] = TParamEntry("%lf", &block->m_block_z_max);
208  params["ground_friction"] = TParamEntry("%lf", &block->m_ground_friction);
209  params["lateral_friction"] = TParamEntry("%lf", &block->m_lateral_friction);
210  params["restitution"] = TParamEntry("%lf", &block->m_restitution);
211  params["color"] = TParamEntry("%color", &block->m_block_color);
212 
213  parse_xmlnode_children_as_param(*root, params, "[Block::factory]");
214  if (class_root)
216  *class_root, params, "[Block::factory]");
217 
218  // Shape node (optional, fallback to default shape if none found)
219  const rapidxml::xml_node<char>* xml_shape =
220  block_root_node.first_node("shape");
221  if (xml_shape)
222  {
224  *xml_shape, block->m_block_poly, "[Block::factory]");
225  block->updateMaxRadiusFromPoly();
226  }
227 
228  // Register bodies, fixtures, etc. in Box2D simulator:
229  // ----------------------------------------------------
230  b2World* b2world = parent->getBox2DWorld();
231  block->create_multibody_system(b2world);
232 
233  if (block->m_b2d_block_body)
234  {
235  // Init pos:
236  block->m_b2d_block_body->SetTransform(
237  b2Vec2(block->m_q.x, block->m_q.y), block->m_q.yaw);
238  // Init vel:
239  block->m_b2d_block_body->SetLinearVelocity(
240  b2Vec2(block->m_dq.vals[0], block->m_dq.vals[1]));
241  block->m_b2d_block_body->SetAngularVelocity(block->m_dq.vals[2]);
242  }
243 
244  return block;
245 }
246 
247 Block* Block::factory(World* parent, const std::string& xml_text)
248 {
249  // Parse the string as if it was an XML file:
250  std::stringstream s;
251  s.str(xml_text);
252 
253  char* input_str = const_cast<char*>(xml_text.c_str());
255  try
256  {
257  xml.parse<0>(input_str);
258  }
259  catch (rapidxml::parse_error& e)
260  {
261  unsigned int line =
262  static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
263  throw std::runtime_error(mrpt::format(
264  "[Block::factory] XML parse error (Line %u): %s",
265  static_cast<unsigned>(line), e.what()));
266  }
267  return Block::factory(parent, xml.first_node());
268 }
269 
276 {
277  vec3 local_vel;
278  local_vel.vals[2] = m_dq.vals[2]; // omega remains the same.
279 
280  const mrpt::poses::CPose2D p(0, 0, -m_q.yaw); // "-" means inverse pose
281  p.composePoint(
282  m_dq.vals[0], m_dq.vals[1], local_vel.vals[0], local_vel.vals[1]);
283  return local_vel;
284 }
285 
286 mrpt::poses::CPose2D Block::getCPose2D() const
287 {
288  return mrpt::poses::CPose2D(mrpt::math::TPose2D(m_q));
289 }
290 
292 void Block::gui_update(mrpt::opengl::COpenGLScene& scene)
293 {
294  // 1st time call?? -> Create objects
295  // ----------------------------------
296  if (!m_gl_block)
297  {
298  m_gl_block = mrpt::opengl::CSetOfObjects::Create();
299 
300  // Block shape:
301  auto gl_poly = mrpt::opengl::CPolyhedron::CreateCustomPrism(
302  m_block_poly, m_block_z_max - m_block_z_min);
303  gl_poly->setLocation(0, 0, m_block_z_min);
304  gl_poly->setColor(TColorf(m_block_color));
305  m_gl_block->insert(gl_poly);
306 
307  SCENE_INSERT_Z_ORDER(scene, 1, m_gl_block);
308 
309  // Visualization of forces:
310  m_gl_forces = mrpt::opengl::CSetOfLines::Create();
311  m_gl_forces->setLineWidth(3.0);
312  m_gl_forces->setColor_u8(TColor(0xff, 0xff, 0xff));
313 
315  scene, 3, m_gl_forces); // forces are in global coords
316  }
317 
318  // Update them:
319  m_gl_block->setPose(m_q);
320 
321  // Other common stuff:
322  internal_gui_update_forces(scene);
323 }
324 
325 void Block::internal_gui_update_forces(mrpt::opengl::COpenGLScene& scene)
326 {
328  {
329  std::lock_guard<std::mutex> csl(m_force_segments_for_rendering_cs);
330  m_gl_forces->clear();
331  m_gl_forces->appendLines(m_force_segments_for_rendering);
332  m_gl_forces->setVisibility(true);
333  }
334  else
335  {
336  m_gl_forces->setVisibility(false);
337  }
338 }
339 
340 void Block::updateMaxRadiusFromPoly()
341 {
342  using namespace mrpt::math;
343 
344  m_max_radius = 0.001f;
345  for (TPolygon2D::const_iterator it = m_block_poly.begin();
346  it != m_block_poly.end(); ++it)
347  {
348  const float n = it->norm();
349  keep_max(m_max_radius, n);
350  }
351 }
352 
355 {
356  // Define the dynamic body. We set its position and call the body factory.
357  b2BodyDef bodyDef;
358  bodyDef.type = b2_dynamicBody;
359 
360  m_b2d_block_body = world->CreateBody(&bodyDef);
361 
362  // Define shape of block:
363  // ------------------------------
364  {
365  // Convert shape into Box2D format:
366  const size_t nPts = m_block_poly.size();
367  ASSERT_(nPts >= 3);
368  ASSERT_BELOWEQ_(nPts, (size_t)b2_maxPolygonVertices);
369  std::vector<b2Vec2> pts(nPts);
370  for (size_t i = 0; i < nPts; i++)
371  pts[i] = b2Vec2(m_block_poly[i].x, m_block_poly[i].y);
372 
373  b2PolygonShape blockPoly;
374  blockPoly.Set(&pts[0], nPts);
375  // blockPoly.m_radius = 1e-3; // The "skin" depth of the body
376 
377  // Define the dynamic body fixture.
378  b2FixtureDef fixtureDef;
379  fixtureDef.shape = &blockPoly;
380  fixtureDef.restitution = m_restitution;
381 
382  // Set the box density to be non-zero, so it will be dynamic.
383  b2MassData mass;
384  blockPoly.ComputeMass(&mass, 1); // Mass with density=1 => compute area
385  fixtureDef.density = m_mass / mass.mass;
386 
387  // Override the default friction.
388  fixtureDef.friction = m_lateral_friction; // 0.3f;
389 
390  // Add the shape to the body.
391  m_fixture_block = m_b2d_block_body->CreateFixture(&fixtureDef);
392 
393  // Compute center of mass:
394  b2MassData vehMass;
395  m_fixture_block->GetMassData(&vehMass);
396  m_block_com.x = vehMass.center.x;
397  m_block_com.y = vehMass.center.y;
398  }
399 
400  // Create "archor points" to simulate friction with the ground:
401  // -----------------------------------------------------------------
402  const size_t nContactPoints = 2;
403  const double weight_per_contact_point =
404  m_mass * getWorldObject()->get_gravity() / nContactPoints;
405  const double mu = m_ground_friction;
406  const double max_friction = mu * weight_per_contact_point;
407 
408  // Location (local coords) of each contact-point:
409  const vec2 pt_loc[nContactPoints] = {vec2(m_max_radius, 0),
410  vec2(-m_max_radius, 0)};
411 
412  b2FrictionJointDef fjd;
413 
415  fjd.bodyB = m_b2d_block_body;
416 
417  for (size_t i = 0; i < nContactPoints; i++)
418  {
419  const b2Vec2 local_pt = b2Vec2(pt_loc[i].vals[0], pt_loc[i].vals[1]);
420 
421  fjd.localAnchorA = m_b2d_block_body->GetWorldPoint(local_pt);
422  fjd.localAnchorB = local_pt;
423  fjd.maxForce = max_friction;
424  fjd.maxTorque = 0;
425 
426  b2FrictionJoint* b2_friction = dynamic_cast<b2FrictionJoint*>(
427  m_world->getBox2DWorld()->CreateJoint(&fjd));
428  m_friction_joints.push_back(b2_friction);
429  }
430 }
431 
433  double fx, double fy, double local_ptx, double local_pty)
434 {
435  ASSERT_(m_b2d_block_body);
436  const b2Vec2 wPt = m_b2d_block_body->GetWorldPoint(
437  b2Vec2(local_ptx, local_pty)); // Application point -> world coords
438  m_b2d_block_body->ApplyForce(b2Vec2(fx, fy), wPt, true /*wake up*/);
439 }
const b2Shape * shape
Definition: b2Fixture.h:71
vec3 getVelocityLocal() const
Definition: Block.cpp:275
This file contains rapidxml parser and DOM implementation.
Ch * where() const
Definition: rapidxml.hpp:94
b2Vec2 localAnchorA
The local anchor point relative to bodyA&#39;s origin.
void parse_xmlnode_shape(const rapidxml::xml_node< char > &xml_node, mrpt::math::TPolygon2D &out_poly, const char *function_name_context="")
Definition: xml_utils.cpp:239
virtual void apply_force(double fx, double fy, double local_ptx=0.0, double local_pty=0.0)
Definition: Block.cpp:432
std::string m_name
User-supplied name of the block (e.g. "r1", "veh1")
Definition: Block.h:113
Ch * value() const
Definition: rapidxml.hpp:692
b2Vec2 localAnchorB
The local anchor point relative to bodyB&#39;s origin.
XmlRpcServer s
double vals[3]
Definition: basic_types.h:64
float32 maxForce
The maximum friction force in N.
virtual void simul_post_timestep(const TSimulContext &context)
Definition: Block.cpp:273
static void register_block_class(const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:93
void parse(Ch *text)
Definition: rapidxml.hpp:1381
void simul_post_timestep_common(const TSimulContext &context)
Definition: Block.cpp:69
b2Vec2 center
The position of the shape&#39;s centroid relative to the shape&#39;s origin.
Definition: b2Shape.h:33
TFSIMD_FORCE_INLINE const tfScalar & y() const
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
A 2D column vector.
Definition: b2Math.h:53
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
Friction joint definition.
double get_gravity() const
Definition: World.h:113
b2BodyType type
Definition: b2Body.h:74
void ComputeMass(b2MassData *massData, float32 density) const
float32 mass
The mass of the shape, usually in kilograms.
Definition: b2Shape.h:30
virtual void create_multibody_system(b2World *world)
Definition: Block.cpp:354
World * getWorldObject()
Definition: VisualObject.h:29
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const std::map< std::string, TParamEntry > &params, const char *function_name_context="")
Definition: xml_utils.cpp:196
b2Joint * CreateJoint(const b2JointDef *def)
Definition: b2World.cpp:212
#define SCENE_INSERT_Z_ORDER(_SCENE, _ZORDER_INDEX, _OBJ_TO_INSERT)
Definition: VisualObject.h:37
Ch * name() const
Definition: rapidxml.hpp:673
float32 maxTorque
The maximum friction torque in N-m.
TGUI_Options m_gui_options
Definition: World.h:265
virtual void gui_update(mrpt::opengl::COpenGLScene &scene)
Definition: Block.cpp:292
virtual const char * what() const
Definition: rapidxml.hpp:85
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
#define b2_maxPolygonVertices
Definition: b2Settings.h:54
TFSIMD_FORCE_INLINE const tfScalar & x() const
float32 density
The density, usually in kg/m^2.
Definition: b2Fixture.h:83
const rapidxml::xml_node< Ch > * first_node(const char *name)
Definition: JointXMLnode.h:28
float32 y
Definition: b2Math.h:140
static Block * factory(World *parent, const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:114
TFSIMD_FORCE_INLINE const tfScalar & w() const
void Set(const b2Vec2 *points, int32 count)
b2Body * getBox2DGroundBody()
Definition: World.h:180
float32 restitution
The restitution (elasticity) usually in the range [0,1].
Definition: b2Fixture.h:80
const rapidxml::xml_node< char > * get(const std::string &xml_node_class) const
b2World * getBox2DWorld()
Definition: World.h:178
This file contains rapidxml printer implementation.
void add(const std::string &input_xml_node_class)
float32 x
Definition: b2Math.h:140
Block(World *parent)
Definition: Block.cpp:45
b2Body * bodyA
The first attached body.
Definition: b2Joint.h:92
This holds the mass data computed for a shape.
Definition: b2Shape.h:27
void add(const rapidxml::xml_node< Ch > *node)
Definition: JointXMLnode.h:27
XmlClassesRegistry block_classes_registry("block:class")
float32 friction
The friction coefficient, usually in the range [0,1].
Definition: b2Fixture.h:77
mrpt::poses::CPose2D getCPose2D() const
Definition: Block.cpp:286
virtual void simul_pre_timestep(const TSimulContext &context)
Definition: Block.cpp:270
b2Body * CreateBody(const b2BodyDef *def)
Definition: b2World.cpp:107
b2Body * bodyB
The second attached body.
Definition: b2Joint.h:95
float float32
Definition: b2Settings.h:35


mvsim
Author(s):
autogenerated on Thu Jun 6 2019 19:36:40