Skip to content
Snippets Groups Projects
geom.cpp 21.50 KiB
//
// Copyright (c) 2015-2016 CNRS
//
// This file is part of Pinocchio
// Pinocchio is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// Pinocchio is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// Pinocchio If not, see
// <http://www.gnu.org/licenses/>.

#include <iostream>
#include <iomanip>

#include "pinocchio/multibody/model.hpp"

#include "pinocchio/spatial/explog.hpp"
#include "pinocchio/multibody/geometry.hpp"
#include "pinocchio/algorithm/kinematics.hpp"
#include "pinocchio/algorithm/geometry.hpp"
#include "pinocchio/parsers/urdf.hpp"

#include <vector>

#ifdef WITH_HPP_MODEL_URDF
#include <hpp/util/debug.hh>
#include <hpp/model/device.hh>
#include <hpp/model/body.hh>
#include <hpp/model/collision-object.hh>
#include <hpp/model/joint.hh>
#include <hpp/model/urdf/util.hh>
#endif

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE GeomTest
#include <boost/test/unit_test.hpp>

using namespace se3;

typedef std::map <std::string, se3::SE3> PositionsMap_t;
typedef std::map <std::string, se3::SE3> JointPositionsMap_t;
typedef std::map <std::string, se3::SE3> GeometryPositionsMap_t;
typedef std::map <std::pair < std::string , std::string >, fcl::DistanceResult > PairDistanceMap_t;
JointPositionsMap_t fillPinocchioJointPositions(const se3::Model& model, const se3::Data & data);
GeometryPositionsMap_t fillPinocchioGeometryPositions(const se3::GeometryModel & geomModel,
                                                      const se3::GeometryData & geomData);
#ifdef WITH_HPP_MODEL_URDF
JointPositionsMap_t fillHppJointPositions(const hpp::model::HumanoidRobotPtr_t robot);
GeometryPositionsMap_t fillHppGeometryPositions(const hpp::model::HumanoidRobotPtr_t robot);
#endif
std::vector<std::string> getBodiesList();

#ifdef WITH_HPP_MODEL_URDF
class IsDistanceResultPair
{
typedef std::string string;
public:
IsDistanceResultPair( string body1, string body2): _body1(body1), _body2(body2){}

bool operator()(hpp::model::DistanceResult dist) const
{
  return (dist.innerObject->name() == _body1 && dist.outerObject->name() == _body2);
}
private:
string _body1;
string _body2;
};

struct Distance_t
{
  Distance_t () :d_ (0), x0_ (0), y0_ (0), z0_ (0), x1_ (0), y1_ (0), z1_ (0)
  {
  }
  Distance_t (double d, double x0, double y0, double z0,
        double x1, double y1, double z1) : d_ (d),
             x0_ (x0), y0_ (y0), z0_ (z0),
             x1_ (x1), y1_ (y1), z1_ (z1)
  {
  }
  Distance_t ( fcl::DistanceResult dr) : d_ (dr.min_distance),
   x0_ (dr.nearest_points [0][0]), y0_ (dr.nearest_points [0][1]), z0_ (dr.nearest_points [0][2]),
   x1_ (dr.nearest_points [1][0]), y1_ (dr.nearest_points [1][1]), z1_ (dr.nearest_points [1][2])
  {
  }
  Distance_t (const Distance_t& other) :
    d_ (other.d_), x0_ (other.x0_), y0_ (other.y0_), z0_ (other.z0_),
    x1_ (other.x1_), y1_ (other.y1_), z1_ (other.z1_)
  {
  }
  Distance_t& swap ()
  {
    double tmp;
    tmp = x1_; x1_ = x0_; x0_ = tmp;
    tmp = y1_; y1_ = y0_; y0_ = tmp;
    tmp = z1_; z1_ = z0_; z0_ = tmp;
    return *this;
  }
  void checkClose (const Distance_t& other)
  {
    BOOST_CHECK_MESSAGE (fabs (d_ - other.d_ ) < 1e-5, "values d are " << d_ << " and " << other.d_);
    BOOST_CHECK_MESSAGE (fabs (x0_ - other.x0_ ) < 1e-5, "values x0 are " << x0_ << " and " << other.x0_);
    BOOST_CHECK_MESSAGE (fabs (y0_ - other.y0_ ) < 1e-5, "values y0 are " << y0_ << " and " << other.y0_);
    BOOST_CHECK_MESSAGE (fabs (z0_ - other.z0_ ) < 1e-5, "values z0 are " << z0_ << " and " << other.z0_);
    BOOST_CHECK_MESSAGE (fabs (x1_ - other.x1_ ) < 1e-5, "values x1 are " << x1_ << " and " << other.x1_);
    BOOST_CHECK_MESSAGE (fabs (y1_ - other.y1_ ) < 1e-5, "values y1 are " << y1_ << " and " << other.y1_);
    BOOST_CHECK_MESSAGE (fabs (z1_ - other.z1_ ) < 1e-5, "values z1 are " << z1_ << " and " << other.z1_);
  }
  // Distance and closest points on bodies.
  double d_, x0_, y0_, z0_, x1_, y1_, z1_;
}; // struct Distance_t

void loadHumanoidPathPlanerModel (const hpp::model::HumanoidRobotPtr_t& robot,
            const std::string& rootJointType,
            const std::string& package,
            const std::string& modelName,
            const std::string& urdfSuffix)
{
  hpp::model::urdf::Parser urdfParser (rootJointType, robot);
  urdfParser.prefix ("");

  std::string urdfPath = "package://" + package + "/urdf/"
    + modelName + urdfSuffix + ".urdf";

  // Build robot model from URDF.
  urdfParser.parse (urdfPath);

}

#endif


BOOST_AUTO_TEST_SUITE ( GeomTest )

BOOST_AUTO_TEST_CASE ( GeomNoFcl )
{

}

BOOST_AUTO_TEST_CASE ( simple_boxes )
{
  using namespace se3;
  Model model;
  GeometryModel geomModel;

  Model::JointIndex idx;
  idx = model.addJoint(model.getJointId("universe"),JointModelPlanar(),SE3::Identity(),"planar1_joint");
  model.addJointFrame(idx);
  model.appendBodyToJoint(idx,Inertia::Random(),SE3::Identity());
  model.addBodyFrame("planar1_body", idx, SE3::Identity());
  
  idx = model.addJoint(model.getJointId("universe"),JointModelPlanar(),SE3::Identity(),"planar2_joint");
  model.addJointFrame(idx);
  model.appendBodyToJoint(idx,Inertia::Random(),SE3::Identity());
  model.addBodyFrame("planar2_body", idx, SE3::Identity());
  
  boost::shared_ptr<fcl::Box> sample(new fcl::Box(1));
  geomModel.addGeometryObject(GeometryObject("ff1_collision_object",
                                             model.getBodyId("planar1_body"),0,
                                             sample,SE3::Identity(), ""),
                              model,true);
  
  boost::shared_ptr<fcl::Box> sample2(new fcl::Box(1));
  geomModel.addGeometryObject(GeometryObject("ff2_collision_object",
                                             model.getBodyId("planar2_body"),0,
                                             sample2,SE3::Identity(), ""),
                              model,true);

  geomModel.addAllCollisionPairs();
  se3::Data data(model);
  se3::GeometryData geomData(geomModel);

  BOOST_CHECK(CollisionPair(0,1) == geomModel.collisionPairs[0]);

  std::cout << "------ Model ------ " << std::endl;
  std::cout << model;
  std::cout << "------ Geom ------ " << std::endl;
  std::cout << geomModel;
  std::cout << "------ DataGeom ------ " << std::endl;
  std::cout << geomData;

  Eigen::VectorXd q(model.nq);
  q <<  0, 0, 0,
        0, 0, 0 ;

  se3::updateGeometryPlacements(model, data, geomModel, geomData, q);
  BOOST_CHECK(computeCollision(geomModel,geomData,0) == true);

  q <<  2, 0, 0,
        0, 0, 0 ;

  se3::updateGeometryPlacements(model, data, geomModel, geomData, q);
  BOOST_CHECK(computeCollision(geomModel,geomData,0) == false);

  q <<  0.99, 0, 0,
        0, 0, 0 ;

  se3::updateGeometryPlacements(model, data, geomModel, geomData, q);
  BOOST_CHECK(computeCollision(geomModel,geomData,0) == true);

  q <<  1.01, 0, 0,
        0, 0, 0 ;

  se3::updateGeometryPlacements(model, data, geomModel, geomData, q);
  BOOST_CHECK(computeCollision(geomModel,geomData,0) == false);
}

BOOST_AUTO_TEST_CASE ( loading_model )
{
  typedef se3::Model Model;
  typedef se3::GeometryModel GeometryModel;
  typedef se3::Data Data;
  typedef se3::GeometryData GeometryData;


  std::string filename = PINOCCHIO_SOURCE_DIR"/models/romeo.urdf";
  std::vector < std::string > packageDirs;
  std::string meshDir  = PINOCCHIO_SOURCE_DIR"/models/";
  packageDirs.push_back(meshDir);

  Model model;
  se3::urdf::buildModel(filename, se3::JointModelFreeFlyer(),model);
  GeometryModel geomModel;
  se3::urdf::buildGeom(model, filename, se3::COLLISION, geomModel, packageDirs );
  geomModel.addAllCollisionPairs();

  Data data(model);
  GeometryData geomData(geomModel);
  fcl::CollisionResult result;

  Eigen::VectorXd q(model.nq);
  q << 0, 0, 0.840252, 0, 0, 0, 1, 0, 0, -0.3490658, 0.6981317, -0.3490658, 0, 0, 0, -0.3490658,
       0.6981317, -0.3490658, 0, 0, 1.5, 0.6, -0.5, -1.05, -0.4, -0.3, -0.2, 0, 0, 0, 0,
       1.5, -0.6, 0.5, 1.05, -0.4, -0.3, -0.2 ;

  se3::updateGeometryPlacements(model, data, geomModel, geomData, q);
  se3::Index idx = geomModel.findCollisionPair(CollisionPair(1,10));
  BOOST_CHECK(computeCollision(geomModel,geomData,idx) == false);
}


#if defined(WITH_URDFDOM) && defined(WITH_HPP_FCL)
BOOST_AUTO_TEST_CASE (radius)
{
  std::vector < std::string > packageDirs;
#ifdef ROMEO_DESCRIPTION_MODEL_DIR
  std::string filename = ROMEO_DESCRIPTION_MODEL_DIR"/romeo_description/urdf/romeo_small.urdf";
  packageDirs.push_back(ROMEO_DESCRIPTION_MODEL_DIR);
#else
  std::string filename = PINOCCHIO_SOURCE_DIR"/models/romeo.urdf";
  std::string meshDir  = PINOCCHIO_SOURCE_DIR"/models/";
  packageDirs.push_back(meshDir);
#endif // ROMEO_DESCRIPTION_MODEL_DIR

  se3::Model model;
  se3::urdf::buildModel(filename, se3::JointModelFreeFlyer(),model);
  se3::GeometryModel geom;
  se3::urdf::buildGeom(model, filename, se3::COLLISION, geom, packageDirs);
  Data data(model);
  GeometryData geomData(geom);

  // Test that the algorithm does not crash
  se3::computeBodyRadius(model, geom, geomData);
  BOOST_FOREACH( double radius, geomData.radius) BOOST_CHECK(radius>=0.);

#if defined(WITH_HPP_MODEL_URDF) && defined(ROMEO_DESCRIPTION_MODEL_DIR)
  /// *************  HPP  ************* /// 
  /// ********************************* ///
  using hpp::model::JointVector_t;
  using hpp::model::Joint;
  using hpp::model::Body;
  using hpp::model::BodyPtr_t;

  hpp::model::HumanoidRobotPtr_t humanoidRobot =
    hpp::model::HumanoidRobot::create ("romeo");
  loadHumanoidPathPlanerModel(humanoidRobot, "freeflyer",
              "romeo_description", "romeo_small",
              "");

  BOOST_CHECK_MESSAGE(model.nq == humanoidRobot->configSize () , "Pinocchio model & HPP model config sizes are not the same ");

  /// **********  COMPARISON  ********* /// 
  /// ********************************* ///

  JointVector_t joints = humanoidRobot->getJointVector ();
  for (JointVector_t::iterator it = joints.begin (); it != joints.end (); ++it)
  {
    BodyPtr_t body = (*it)->linkedBody ();
    if (body) {
      double radius_hpp = body->radius();
      std::string bodyName = body->name();
      if(bodyName != "base_link")
      {
        FrameIndex fid = model.getFrameId(bodyName, BODY);
        double radius_pino = geomData.radius[model.frames[fid].parent];
        BOOST_CHECK_MESSAGE(radius_hpp - radius_pino < 1e-6, "Radius of body " << bodyName << " are not equals between hpp and pinocchio");
      }

    }
  }
#endif // WITH_HPP_MODEL_URDF
}
#endif // if defined(WITH_URDFDOM) && defined(WITH_HPP_FCL)

#if defined(WITH_HPP_MODEL_URDF) && defined(ROMEO_DESCRIPTION_MODEL_DIR)
BOOST_AUTO_TEST_CASE ( romeo_joints_meshes_positions )
{
  typedef se3::Model Model;
  typedef se3::GeometryModel GeometryModel;
  typedef se3::Data Data;
  typedef se3::GeometryData GeometryData;
  using hpp::model::Device;
  using hpp::model::Joint;
  using hpp::model::Body;



  /// **********  Pinocchio  ********** /// 
  /// ********************************* ///

  // Building the model in pinocchio and compute kinematics/geometry for configuration q_pino
  std::vector < std::string > packageDirs;
#ifdef ROMEO_DESCRIPTION_MODEL_DIR
  std::string filename = ROMEO_DESCRIPTION_MODEL_DIR"/romeo_description/urdf/romeo_small.urdf";
  packageDirs.push_back(ROMEO_DESCRIPTION_MODEL_DIR);
#else
  std::string filename = PINOCCHIO_SOURCE_DIR"/models/romeo.urdf";
  std::string meshDir  = PINOCCHIO_SOURCE_DIR"/models/";
  packageDirs.push_back(meshDir);
#endif // ROMEO_DESCRIPTION_MODEL_DIR

  Model model;
  se3::urdf::buildModel(filename, se3::JointModelFreeFlyer(),model);
  se3::GeometryModel geom;
  se3::urdf::buildGeom(model, filename, se3::COLLISION, geom, packageDirs);
  std::cout << model << std::endl;


  Data data(model);
  GeometryData geomData(geom);

  // Configuration to be tested
  

  Eigen::VectorXd q_pino(Eigen::VectorXd::Random(model.nq));
  q_pino.segment<4>(3) /= q_pino.segment<4>(3).norm();

  Eigen::VectorXd q_hpp(q_pino);
  Eigen::Vector4d quaternion;
  quaternion <<  q_pino[6], q_pino[3], q_pino[4], q_pino[5];
  q_hpp.segment<4>(3) = quaternion ;

  BOOST_CHECK_MESSAGE(q_pino.size() == model.nq , "wrong config size" );

  se3::updateGeometryPlacements(model, data, geom, geomData, q_pino);


  /// *************  HPP  ************* /// 
  /// ********************************* ///


  hpp::model::HumanoidRobotPtr_t humanoidRobot =
    hpp::model::HumanoidRobot::create ("romeo");
  loadHumanoidPathPlanerModel(humanoidRobot, "freeflyer",
              "romeo_description", "romeo_small",
              "");

  BOOST_CHECK_MESSAGE(model.nq == humanoidRobot->configSize () , "Pinocchio model & HPP model config sizes are not the same ");

  humanoidRobot->currentConfiguration (q_hpp);
  humanoidRobot->computeForwardKinematics ();



  /// **********  COMPARISON  ********* /// 
  /// ********************************* ///
  // retrieve all joint and geometry objects positions
  JointPositionsMap_t joints_pin  = fillPinocchioJointPositions(model, data);
  JointPositionsMap_t joints_hpp  = fillHppJointPositions(humanoidRobot);
  GeometryPositionsMap_t geom_pin = fillPinocchioGeometryPositions(geom, geomData);
  GeometryPositionsMap_t geom_hpp = fillHppGeometryPositions(humanoidRobot);


  std::map <std::string, se3::SE3>::iterator it_hpp;
  std::map <std::string, se3::SE3>::iterator it_pin;

  // Comparing position of joints
  se3::SE3 ff_pin = joints_pin["root_joint"];
  se3::SE3 ff_hpp = joints_hpp["base_joint_SO3"];
  BOOST_CHECK_MESSAGE(ff_pin.isApprox(ff_hpp) , "ff_hpp and ff_pin are not ==");

  for (it_pin = joints_pin.begin(); it_pin != joints_pin.end();
        ++it_pin)
  {
    it_hpp = joints_hpp.find(it_pin->first);
    if (it_hpp != joints_hpp.end())
    {
      BOOST_CHECK_MESSAGE(it_pin->second.isApprox(it_hpp->second) , "joint positions are not equal");
      // se3::Motion nu = se3::log6(it_pin->second.inverse() * it_hpp->second);
    }
  }

  // Comparing position of geometry objects
  for (it_pin = geom_pin.begin(); it_pin != geom_pin.end();
        ++it_pin)
  {
    it_hpp = geom_hpp.find(it_pin->first);
    if (it_hpp != geom_hpp.end())
    {
      BOOST_CHECK_MESSAGE(it_pin->second.isApprox(it_hpp->second) , "geometry objects positions are not equal");
    }
  }


}

BOOST_AUTO_TEST_CASE ( hrp2_mesh_distance)
{
  typedef se3::Model Model;
  typedef se3::GeometryModel GeometryModel;
  typedef se3::Data Data;
  typedef se3::GeometryData GeometryData;
  using hpp::model::Device;
  using hpp::model::Joint;
  using hpp::model::Body;


  /// **********  Pinocchio  ********** /// 
  /// ********************************* /// 

  // Building the model in pinocchio and compute kinematics/geometry for configuration q_pino
  std::vector < std::string > packageDirs;
#ifdef ROMEO_DESCRIPTION_MODEL_DIR
  std::string filename = ROMEO_DESCRIPTION_MODEL_DIR"/romeo_description/urdf/romeo_small.urdf";
  packageDirs.push_back(ROMEO_DESCRIPTION_MODEL_DIR);
#else
  std::string filename = PINOCCHIO_SOURCE_DIR"/models/romeo.urdf";
  std::string meshDir  = PINOCCHIO_SOURCE_DIR"/models/";
  packageDirs.push_back(meshDir);
#endif // ROMEO_DESCRIPTION_MODEL_DIR

  Model model;
  se3::urdf::buildModel(filename, se3::JointModelFreeFlyer(),model);
  se3::GeometryModel geom;
  se3::urdf::buildGeom(model, filename, se3::COLLISION, geom, packageDirs);
  geom.addAllCollisionPairs();
  std::cout << model << std::endl;


  Data data(model);
  GeometryData geomData(geom);

  // Configuration to be tested
  

  Eigen::VectorXd q_pino(Eigen::VectorXd::Random(model.nq));
  q_pino.segment<4>(3) /= q_pino.segment<4>(3).norm();

  Eigen::VectorXd q_hpp(q_pino);
  Eigen::Vector4d quaternion;
  quaternion <<  q_pino[6], q_pino[3], q_pino[4], q_pino[5];
  q_hpp.segment<4>(3) = quaternion ;

  BOOST_CHECK_MESSAGE(q_pino.size() == model.nq , "wrong config size");

  se3::updateGeometryPlacements(model, data, geom, geomData, q_pino);


  /// *************  HPP  ************* /// 
  /// ********************************* ///


  hpp::model::HumanoidRobotPtr_t humanoidRobot =
    hpp::model::HumanoidRobot::create ("romeo");
  loadHumanoidPathPlanerModel(humanoidRobot, "freeflyer",
              "romeo_description", "romeo_small",
              "");

  BOOST_CHECK_MESSAGE(model.nq == humanoidRobot->configSize () , "Pinocchio model & HPP model config sizes are not the same ");

  humanoidRobot->currentConfiguration (q_hpp);
  humanoidRobot->computeForwardKinematics ();


  humanoidRobot->computeDistances ();

  /// **********  COMPARISON  ********* /// 
  /// ********************************* ///

  const hpp::model::DistanceResults_t& distances(humanoidRobot->distanceResults ());
  std::vector<std::string> bodies = getBodiesList();

  for (std::vector<std::string>::iterator i = bodies.begin(); i != bodies.end(); ++i)
  {
    for (std::vector<std::string>::iterator j = bodies.begin(); j != bodies.end(); ++j)
    {
      std::string body1 (*i); std::string inner1(body1 + "_0");
      std::string body2 (*j); std::string inner2(body2 + "_0");
      hpp::model::DistanceResults_t::const_iterator it = std::find_if ( distances.begin(),
                                                                    distances.end(),
                                                                    IsDistanceResultPair(inner1, inner2)
                                                                    );
      if(it != distances .end())
      {
        Distance_t distance_hpp(it->fcl);


        std::cout << "comparison between " << body1 << " and " << body2 << std::endl;
        se3::CollisionPair pair (geom.getGeometryId(body1),
                                 geom.getGeometryId(body2));
        BOOST_REQUIRE (geom.existCollisionPair(pair));

        fcl::DistanceResult dist_pin
          = se3::computeDistance( geom, geomData, geom.findCollisionPair(pair));

        Distance_t distance_pin(dist_pin);
        distance_hpp.checkClose(distance_pin);
      }
    }
  }

}
#endif
BOOST_AUTO_TEST_SUITE_END ()

JointPositionsMap_t fillPinocchioJointPositions(const se3::Model& model, const se3::Data & data)
{
  JointPositionsMap_t result;
  for (se3::Model::Index i = 0; i < (se3::Model::Index)model.njoints; ++i)
  {
    result[model.names[i]] = data.oMi[i];
  }
  return result;
}

GeometryPositionsMap_t fillPinocchioGeometryPositions(const se3::GeometryModel & geomModel,
                                                      const se3::GeometryData & geomData)
{
  GeometryPositionsMap_t result;
  for (std::size_t i = 0; i < geomModel.ngeoms ; ++i)
  {
    result[geomModel.getGeometryName(i)] = geomData.oMg[i];
  }
  return result;
}

#ifdef WITH_HPP_MODEL_URDF
JointPositionsMap_t fillHppJointPositions(const hpp::model::HumanoidRobotPtr_t robot)
{
  JointPositionsMap_t result;
  const hpp::model::JointVector_t& joints(robot->getJointVector ());
  
  for (hpp::model::JointVector_t::const_iterator it = joints.begin (); it != joints.end (); ++it)
  {
    if((*it)->configSize() != 0) // Retrieving joints that are not anchors
    {
      result[(*it)->name()] = se3::toPinocchioSE3((*it)->currentTransformation() * (*it)->linkInJointFrame());
    }
  }
  return result;  

}

GeometryPositionsMap_t fillHppGeometryPositions(const hpp::model::HumanoidRobotPtr_t robot)
{
  GeometryPositionsMap_t result;

  const hpp::model::JointVector_t& joints(robot->getJointVector ());
  // retrieving only the positions of link that have collision geometry
  for (hpp::model::JointVector_t::const_iterator it = joints.begin (); it != joints.end (); ++it)
  {
    if((*it)->configSize() != 0) // Retrieving body attached to joints that are not anchors
    {
      hpp::model::BodyPtr_t body = (*it)->linkedBody ();
      if(body)
      {
        const hpp::model::ObjectVector_t& ov (body->innerObjects (hpp::model::COLLISION));
        for (hpp::model::ObjectVector_t::const_iterator itObj = ov.begin ();itObj != ov.end (); itObj ++)
        {
          result[body->name()] = se3::toPinocchioSE3((*itObj)->fcl ()->getTransform ());
        }
      }
    }
  }

  return result;
}

#endif

std::vector<std::string> getBodiesList()
{
  std::vector<std::string> result;

  result.push_back( "CHEST_LINK0");
  result.push_back( "torso");
  result.push_back( "HEAD_LINK0");
  result.push_back( "HEAD_LINK1");
  result.push_back( "LARM_LINK0");
  result.push_back( "LARM_LINK1");
  result.push_back( "LARM_LINK2");
  result.push_back( "LARM_LINK3");
  result.push_back( "LARM_LINK4");
  result.push_back( "l_wrist");
  result.push_back( "LARM_LINK6");
  result.push_back( "LHAND_LINK0");
  result.push_back( "LHAND_LINK1");
  result.push_back( "LHAND_LINK2");
  result.push_back( "LHAND_LINK3");
  result.push_back( "LHAND_LINK4");
  result.push_back( "RARM_LINK0");
  result.push_back( "RARM_LINK1");
  result.push_back( "RARM_LINK2");
  result.push_back( "RARM_LINK3");
  result.push_back( "RARM_LINK4");
  result.push_back( "r_wrist");
  result.push_back( "RARM_LINK6");
  result.push_back( "RHAND_LINK0");
  result.push_back( "RHAND_LINK1");
  result.push_back( "RHAND_LINK2");
  result.push_back( "RHAND_LINK3");
  result.push_back( "RHAND_LINK4");
  result.push_back( "LLEG_LINK0");
  result.push_back( "LLEG_LINK1");
  result.push_back( "LLEG_LINK2");
  result.push_back( "LLEG_LINK3");
  result.push_back( "LLEG_LINK4");
  result.push_back( "l_ankle");
  result.push_back( "RLEG_LINK0");
  result.push_back( "RLEG_LINK1");
  result.push_back( "RLEG_LINK2");
  result.push_back( "RLEG_LINK3");
  result.push_back( "RLEG_LINK4");
  result.push_back( "r_ankle");

  return result;
}