Block.cpp
Go to the documentation of this file.
00001 /*+-------------------------------------------------------------------------+
00002   |                       MultiVehicle simulator (libmvsim)                 |
00003   |                                                                         |
00004   | Copyright (C) 2014  Jose Luis Blanco Claraco (University of Almeria)    |
00005   | Copyright (C) 2017  Borys Tymchenko (Odessa Polytechnic University)     |
00006   | Distributed under GNU General Public License version 3                  |
00007   |   See <http://www.gnu.org/licenses/>                                    |
00008   +-------------------------------------------------------------------------+ */
00009 
00010 #include <mvsim/World.h>
00011 #include <mvsim/Block.h>
00012 
00013 #include "JointXMLnode.h"
00014 #include "XMLClassesRegistry.h"
00015 #include "xml_utils.h"
00016 
00017 #include <rapidxml.hpp>
00018 #include <rapidxml_utils.hpp>
00019 #include <rapidxml_print.hpp>
00020 #include <mrpt/poses/CPose2D.h>
00021 #include <mrpt/opengl/CPolyhedron.h>
00022 
00023 #include <mrpt/version.h>
00024 #if MRPT_VERSION < 0x199
00025 #include <mrpt/utils/utils_defs.h>  // mrpt::format()
00026 using namespace mrpt::utils;
00027 #else
00028 #include <mrpt/math/TPose2D.h>
00029 #include <mrpt/core/format.h>
00030 #include <mrpt/core/bits_math.h>
00031 using namespace mrpt::img;
00032 using namespace mrpt;
00033 #endif
00034 
00035 #include <sstream>  // std::stringstream
00036 #include <map>
00037 #include <string>
00038 
00039 using namespace mvsim;
00040 using namespace std;
00041 
00042 XmlClassesRegistry block_classes_registry("block:class");
00043 
00044 // Protected ctor:
00045 Block::Block(World* parent)
00046         : VisualObject(parent),
00047           m_block_index(0),
00048           m_b2d_block_body(NULL),
00049           m_q(0, 0, 0, 0, 0, 0),
00050           m_dq(0, 0, 0),
00051           m_mass(30.0),
00052           m_block_z_min(0.0),
00053           m_block_z_max(1.0),
00054           m_block_color(0x00, 0x00, 0xff),
00055           m_block_com(.0, .0),
00056           m_lateral_friction(0.5),
00057           m_ground_friction(0.5),
00058           m_restitution(0.01)
00059 {
00060         using namespace mrpt::math;
00061         // Default shape:
00062         m_block_poly.push_back(TPoint2D(-0.5, -0.5));
00063         m_block_poly.push_back(TPoint2D(-0.5, 0.5));
00064         m_block_poly.push_back(TPoint2D(0.5, 0.5));
00065         m_block_poly.push_back(TPoint2D(0.5, -0.5));
00066         updateMaxRadiusFromPoly();
00067 }
00068 
00069 void Block::simul_post_timestep_common(const TSimulContext& /*context*/)
00070 {
00071         if (m_b2d_block_body)
00072         {
00073                 // Pos:
00074                 const b2Vec2& pos = m_b2d_block_body->GetPosition();
00075                 const float32 angle = m_b2d_block_body->GetAngle();
00076                 m_q.x = pos(0);
00077                 m_q.y = pos(1);
00078                 m_q.yaw = angle;
00079                 // The rest (z,pitch,roll) will be always 0, unless other world-element
00080                 // modifies them! (e.g. elevation map)
00081 
00082                 // Vel:
00083                 const b2Vec2& vel = m_b2d_block_body->GetLinearVelocity();
00084                 const float32 w = m_b2d_block_body->GetAngularVelocity();
00085                 m_dq.vals[0] = vel(0);
00086                 m_dq.vals[1] = vel(1);
00087                 m_dq.vals[2] = w;
00088         }
00089 }
00090 
00093 void Block::register_block_class(const rapidxml::xml_node<char>* xml_node)
00094 {
00095         // Sanity checks:
00096         if (!xml_node)
00097                 throw runtime_error("[Block::register_vehicle_class] XML node is NULL");
00098         if (0 != strcmp(xml_node->name(), "block:class"))
00099                 throw runtime_error(mrpt::format(
00100                         "[Block::register_block_class] XML element is '%s' "
00101                         "('block:class' expected)",
00102                         xml_node->name()));
00103 
00104         // rapidxml doesn't allow making copied of objects.
00105         // So: convert to txt; then re-parse.
00106         std::stringstream ss;
00107         ss << *xml_node;
00108 
00109         block_classes_registry.add(ss.str());
00110 }
00111 
00114 Block* Block::factory(World* parent, const rapidxml::xml_node<char>* root)
00115 {
00116         using namespace std;
00117         using namespace rapidxml;
00118 
00119         if (!root) throw runtime_error("[Block::factory] XML node is NULL");
00120         if (0 != strcmp(root->name(), "block"))
00121                 throw runtime_error(mrpt::format(
00122                         "[Block::factory] XML root element is '%s' ('block' expected)",
00123                         root->name()));
00124 
00125         // "class": When there is a 'class="XXX"' attribute, look for each parameter
00126         //  in the set of "root" + "class_root" XML nodes:
00127         // --------------------------------------------------------------------------------
00128         JointXMLnode<> block_root_node;
00129         const rapidxml::xml_node<char>* class_root = NULL;
00130         {
00131                 block_root_node.add(
00132                         root);  // Always search in root. Also in the class root, if any:
00133                 const xml_attribute<>* block_class = root->first_attribute("class");
00134                 if (block_class)
00135                 {
00136                         const string sClassName = block_class->value();
00137                         class_root = block_classes_registry.get(sClassName);
00138                         if (!class_root)
00139                                 throw runtime_error(mrpt::format(
00140                                         "[Block::factory] Block class '%s' undefined",
00141                                         sClassName.c_str()));
00142                         block_root_node.add(class_root);
00143                 }
00144         }
00145 
00146         // Build object (we don't use class factory for blocks)
00147         // ----------------------------------------------------
00148         Block* block = new Block(parent);
00149 
00150         // Init params
00151         // -------------------------------------------------
00152         // attrib: name
00153         {
00154                 const xml_attribute<>* attrib_name = root->first_attribute("name");
00155                 if (attrib_name && attrib_name->value())
00156                 {
00157                         block->m_name = attrib_name->value();
00158                 }
00159                 else
00160                 {
00161                         // Default name:
00162                         static int cnt = 0;
00163                         block->m_name = mrpt::format("block%i", ++cnt);
00164                 }
00165         }
00166 
00167         // (Mandatory) initial pose:
00168         {
00169                 const xml_node<>* node = block_root_node.first_node("init_pose");
00170                 if (!node)
00171                         throw runtime_error(
00172                                 "[Block::factory] Missing XML node <init_pose>");
00173 
00174                 if (3 != ::sscanf(
00175                                          node->value(), "%lf %lf %lf", &block->m_q.x, &block->m_q.y,
00176                                          &block->m_q.yaw))
00177                         throw runtime_error(
00178                                 "[Block::factory] Error parsing <init_pose>...</init_pose>");
00179                 block->m_q.yaw *= M_PI / 180.0;  // deg->rad
00180         }
00181 
00182         // (Optional) initial vel:
00183         {
00184                 const xml_node<>* node = block_root_node.first_node("init_vel");
00185                 if (node)
00186                 {
00187                         if (3 != ::sscanf(
00188                                                  node->value(), "%lf %lf %lf", &block->m_dq.vals[0],
00189                                                  &block->m_dq.vals[1], &block->m_dq.vals[2]))
00190                                 throw runtime_error(
00191                                         "[Block::factory] Error parsing <init_vel>...</init_vel>");
00192                         block->m_dq.vals[2] *= M_PI / 180.0;  // deg->rad
00193 
00194                         // Convert twist (velocity) from local -> global coords:
00195                         const mrpt::poses::CPose2D pose(
00196                                 0, 0, block->m_q.yaw);  // Only the rotation
00197                         pose.composePoint(
00198                                 block->m_dq.vals[0], block->m_dq.vals[1], block->m_dq.vals[0],
00199                                 block->m_dq.vals[1]);
00200                 }
00201         }
00202 
00203         // Params:
00204         std::map<std::string, TParamEntry> params;
00205         params["mass"] = TParamEntry("%lf", &block->m_mass);
00206         params["zmin"] = TParamEntry("%lf", &block->m_block_z_min);
00207         params["zmax"] = TParamEntry("%lf", &block->m_block_z_max);
00208         params["ground_friction"] = TParamEntry("%lf", &block->m_ground_friction);
00209         params["lateral_friction"] = TParamEntry("%lf", &block->m_lateral_friction);
00210         params["restitution"] = TParamEntry("%lf", &block->m_restitution);
00211         params["color"] = TParamEntry("%color", &block->m_block_color);
00212 
00213         parse_xmlnode_children_as_param(*root, params, "[Block::factory]");
00214         if (class_root)
00215                 parse_xmlnode_children_as_param(
00216                         *class_root, params, "[Block::factory]");
00217 
00218         // Shape node (optional, fallback to default shape if none found)
00219         const rapidxml::xml_node<char>* xml_shape =
00220                 block_root_node.first_node("shape");
00221         if (xml_shape)
00222         {
00223                 mvsim::parse_xmlnode_shape(
00224                         *xml_shape, block->m_block_poly, "[Block::factory]");
00225                 block->updateMaxRadiusFromPoly();
00226         }
00227 
00228         // Register bodies, fixtures, etc. in Box2D simulator:
00229         // ----------------------------------------------------
00230         b2World* b2world = parent->getBox2DWorld();
00231         block->create_multibody_system(b2world);
00232 
00233         if (block->m_b2d_block_body)
00234         {
00235                 // Init pos:
00236                 block->m_b2d_block_body->SetTransform(
00237                         b2Vec2(block->m_q.x, block->m_q.y), block->m_q.yaw);
00238                 // Init vel:
00239                 block->m_b2d_block_body->SetLinearVelocity(
00240                         b2Vec2(block->m_dq.vals[0], block->m_dq.vals[1]));
00241                 block->m_b2d_block_body->SetAngularVelocity(block->m_dq.vals[2]);
00242         }
00243 
00244         return block;
00245 }
00246 
00247 Block* Block::factory(World* parent, const std::string& xml_text)
00248 {
00249         // Parse the string as if it was an XML file:
00250         std::stringstream s;
00251         s.str(xml_text);
00252 
00253         char* input_str = const_cast<char*>(xml_text.c_str());
00254         rapidxml::xml_document<> xml;
00255         try
00256         {
00257                 xml.parse<0>(input_str);
00258         }
00259         catch (rapidxml::parse_error& e)
00260         {
00261                 unsigned int line =
00262                         static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
00263                 throw std::runtime_error(mrpt::format(
00264                         "[Block::factory] XML parse error (Line %u): %s",
00265                         static_cast<unsigned>(line), e.what()));
00266         }
00267         return Block::factory(parent, xml.first_node());
00268 }
00269 
00270 void Block::simul_pre_timestep(const TSimulContext& context) {}
00273 void Block::simul_post_timestep(const TSimulContext& context) {}
00275 vec3 Block::getVelocityLocal() const
00276 {
00277         vec3 local_vel;
00278         local_vel.vals[2] = m_dq.vals[2];  // omega remains the same.
00279 
00280         const mrpt::poses::CPose2D p(0, 0, -m_q.yaw);  // "-" means inverse pose
00281         p.composePoint(
00282                 m_dq.vals[0], m_dq.vals[1], local_vel.vals[0], local_vel.vals[1]);
00283         return local_vel;
00284 }
00285 
00286 mrpt::poses::CPose2D Block::getCPose2D() const
00287 {
00288         return mrpt::poses::CPose2D(mrpt::math::TPose2D(m_q));
00289 }
00290 
00292 void Block::gui_update(mrpt::opengl::COpenGLScene& scene)
00293 {
00294         // 1st time call?? -> Create objects
00295         // ----------------------------------
00296         if (!m_gl_block)
00297         {
00298                 m_gl_block = mrpt::opengl::CSetOfObjects::Create();
00299 
00300                 // Block shape:
00301                 auto gl_poly = mrpt::opengl::CPolyhedron::CreateCustomPrism(
00302                         m_block_poly, m_block_z_max - m_block_z_min);
00303                 gl_poly->setLocation(0, 0, m_block_z_min);
00304                 gl_poly->setColor(TColorf(m_block_color));
00305                 m_gl_block->insert(gl_poly);
00306 
00307                 SCENE_INSERT_Z_ORDER(scene, 1, m_gl_block);
00308 
00309                 // Visualization of forces:
00310                 m_gl_forces = mrpt::opengl::CSetOfLines::Create();
00311                 m_gl_forces->setLineWidth(3.0);
00312                 m_gl_forces->setColor_u8(TColor(0xff, 0xff, 0xff));
00313 
00314                 SCENE_INSERT_Z_ORDER(
00315                         scene, 3, m_gl_forces);  // forces are in global coords
00316         }
00317 
00318         // Update them:
00319         m_gl_block->setPose(m_q);
00320 
00321         // Other common stuff:
00322         internal_gui_update_forces(scene);
00323 }
00324 
00325 void Block::internal_gui_update_forces(mrpt::opengl::COpenGLScene& scene)
00326 {
00327         if (m_world->m_gui_options.show_forces)
00328         {
00329                 std::lock_guard<std::mutex> csl(m_force_segments_for_rendering_cs);
00330                 m_gl_forces->clear();
00331                 m_gl_forces->appendLines(m_force_segments_for_rendering);
00332                 m_gl_forces->setVisibility(true);
00333         }
00334         else
00335         {
00336                 m_gl_forces->setVisibility(false);
00337         }
00338 }
00339 
00340 void Block::updateMaxRadiusFromPoly()
00341 {
00342         using namespace mrpt::math;
00343 
00344         m_max_radius = 0.001f;
00345         for (TPolygon2D::const_iterator it = m_block_poly.begin();
00346                  it != m_block_poly.end(); ++it)
00347         {
00348                 const float n = it->norm();
00349                 keep_max(m_max_radius, n);
00350         }
00351 }
00352 
00354 void Block::create_multibody_system(b2World* world)
00355 {
00356         // Define the dynamic body. We set its position and call the body factory.
00357         b2BodyDef bodyDef;
00358         bodyDef.type = b2_dynamicBody;
00359 
00360         m_b2d_block_body = world->CreateBody(&bodyDef);
00361 
00362         // Define shape of block:
00363         // ------------------------------
00364         {
00365                 // Convert shape into Box2D format:
00366                 const size_t nPts = m_block_poly.size();
00367                 ASSERT_(nPts >= 3);
00368                 ASSERT_BELOWEQ_(nPts, (size_t)b2_maxPolygonVertices);
00369                 std::vector<b2Vec2> pts(nPts);
00370                 for (size_t i = 0; i < nPts; i++)
00371                         pts[i] = b2Vec2(m_block_poly[i].x, m_block_poly[i].y);
00372 
00373                 b2PolygonShape blockPoly;
00374                 blockPoly.Set(&pts[0], nPts);
00375                 // blockPoly.m_radius = 1e-3;  // The "skin" depth of the body
00376 
00377                 // Define the dynamic body fixture.
00378                 b2FixtureDef fixtureDef;
00379                 fixtureDef.shape = &blockPoly;
00380                 fixtureDef.restitution = m_restitution;
00381 
00382                 // Set the box density to be non-zero, so it will be dynamic.
00383                 b2MassData mass;
00384                 blockPoly.ComputeMass(&mass, 1);  // Mass with density=1 => compute area
00385                 fixtureDef.density = m_mass / mass.mass;
00386 
00387                 // Override the default friction.
00388                 fixtureDef.friction = m_lateral_friction;  // 0.3f;
00389 
00390                 // Add the shape to the body.
00391                 m_fixture_block = m_b2d_block_body->CreateFixture(&fixtureDef);
00392 
00393                 // Compute center of mass:
00394                 b2MassData vehMass;
00395                 m_fixture_block->GetMassData(&vehMass);
00396                 m_block_com.x = vehMass.center.x;
00397                 m_block_com.y = vehMass.center.y;
00398         }
00399 
00400         // Create "archor points" to simulate friction with the ground:
00401         // -----------------------------------------------------------------
00402         const size_t nContactPoints = 2;
00403         const double weight_per_contact_point =
00404                 m_mass * getWorldObject()->get_gravity() / nContactPoints;
00405         const double mu = m_ground_friction;
00406         const double max_friction = mu * weight_per_contact_point;
00407 
00408         // Location (local coords) of each contact-point:
00409         const vec2 pt_loc[nContactPoints] = {vec2(m_max_radius, 0),
00410                                                                                  vec2(-m_max_radius, 0)};
00411 
00412         b2FrictionJointDef fjd;
00413 
00414         fjd.bodyA = m_world->getBox2DGroundBody();
00415         fjd.bodyB = m_b2d_block_body;
00416 
00417         for (size_t i = 0; i < nContactPoints; i++)
00418         {
00419                 const b2Vec2 local_pt = b2Vec2(pt_loc[i].vals[0], pt_loc[i].vals[1]);
00420 
00421                 fjd.localAnchorA = m_b2d_block_body->GetWorldPoint(local_pt);
00422                 fjd.localAnchorB = local_pt;
00423                 fjd.maxForce = max_friction;
00424                 fjd.maxTorque = 0;
00425 
00426                 b2FrictionJoint* b2_friction = dynamic_cast<b2FrictionJoint*>(
00427                         m_world->getBox2DWorld()->CreateJoint(&fjd));
00428                 m_friction_joints.push_back(b2_friction);
00429         }
00430 }
00431 
00432 void Block::apply_force(
00433         double fx, double fy, double local_ptx, double local_pty)
00434 {
00435         ASSERT_(m_b2d_block_body);
00436         const b2Vec2 wPt = m_b2d_block_body->GetWorldPoint(
00437                 b2Vec2(local_ptx, local_pty));  // Application point -> world coords
00438         m_b2d_block_body->ApplyForce(b2Vec2(fx, fy), wPt, true /*wake up*/);
00439 }


mvsim
Author(s):
autogenerated on Thu Jun 6 2019 22:08:35