libalmath  2.8.7.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
urdf.h
Go to the documentation of this file.
1 /*
2  * Copyright 2015 Aldebaran. All rights reserved.
3  *
4  */
5 
6 #ifndef LIB_ALMATH_SCENEGRAPH_URDF_H
7 #define LIB_ALMATH_SCENEGRAPH_URDF_H
8 
9 #include <almath/api.h>
10 #include <string>
11 #include <iosfwd>
12 #include <memory>
13 #include <array>
14 #include <boost/property_tree/ptree.hpp>
15 #include <boost/optional.hpp>
16 #include <boost/variant.hpp>
17 #include <boost/range/adaptor/map.hpp>
18 #include <boost/range/adaptor/filtered.hpp>
19 #include <boost/function/function_fwd.hpp>
20 #include <boost/ref.hpp>
21 #include <boost/tokenizer.hpp>
22 #include <boost/multi_index_container.hpp>
23 #include <boost/multi_index/ordered_index.hpp>
24 #include <boost/multi_index/identity.hpp>
25 #include <boost/multi_index/mem_fun.hpp>
26 #include <boost/multi_index/global_fun.hpp>
27 #include <boost/multi_index/sequenced_index.hpp>
28 #include <set>
29 #include <vector>
30 #include <type_traits>
31 #include <boost/property_tree/stream_translator.hpp>
32 
33 namespace boost {
34 namespace property_tree {
35 
36 // hack around ptree bug #10188 [1]: for floating point numbers they don't
37 // serialize with enough precision:
38 // they use digits10+1 instead of max_digits10
39 // [1] https://svn.boost.org/trac/boost/ticket/10188
40 template <typename Ch, typename Traits>
41 struct customize_stream<Ch, Traits, double, void> {
42  static void insert(std::basic_ostream<Ch, Traits> &s, const double &e) {
43  s.precision(std::numeric_limits<double>::max_digits10);
44  s << e;
45  }
46  static void extract(std::basic_istream<Ch, Traits> &s, double &e) {
47  s >> e;
48  if (!s.eof()) {
49  s >> std::ws;
50  }
51  }
52 };
53 }
54 }
55 
56 namespace AL {
57 
58 namespace urdf {
59 
61 
62 namespace detail {
63 class RobotTreeP;
64 }
65 
66 typedef std::array<double, 3> Array3d;
67 
68 inline bool is_zero(const Array3d &a) {
69  return (a[0] == 0) && (a[1] == 0) && (a[2] == 0);
70 }
71 
72 inline bool is_ones(const Array3d &a) {
73  return (a[0] == 1) && (a[1] == 1) && (a[2] == 1);
74 }
75 
76 class Pose;
77 
78 // return the name attribute of a joint or link element
79 ALMATH_API std::string name(const ptree &pt);
80 
81 // return the parent link of a joint element
82 ALMATH_API std::string parent_link(const ptree &pt);
83 
84 // return the child link of a joint element
85 ALMATH_API std::string child_link(const ptree &pt);
86 
87 // A parser for urdf xml files [1].
88 //
89 // Contrary to the urdf parser from ROS [2], [3], this parser:
90 //
91 // * preserves joint ordering defined in XML file. This is useful if one
92 // wants to use this ordering to define the joint indexes in a
93 // state vector.
94 //
95 // * provides access to the raw XML elements, so that you can leverage the
96 // parser to handle unofficial URDF extensions and
97 // to modify the XML tree and write it back while altering the document as
98 // little as possible.
99 //
100 // * does not systematically convert roll-pitch-yaw angles to quaternions,
101 // which saves us some bugs and better fits the "alter the document as
102 // little as possible" philosophy.
103 //
104 // [1] http://wiki.ros.org/urdf/XML
105 // [2] https://github.com/ros/robot_model
106 // [3] https://github.com/ros/urdfdom_headers
107 //
108 // The UrdfTree class acts as an index for the boost::property_tree of the
109 // (URDF) XML document.
110 // Given the ptree of the robot XML element, it traverses the tree and
111 // indexes the ptree elements for the URDF joints and links.
112 //
113 // The class is akin to a set of iterators: it does not copy nor own the
114 // XML tree, and may be invalidated if the XML tree changes.
115 //
116 // The user shall ensure that the XML root and the joint and link ptree
117 // elements stay alive and valid for the lifetime of this RobotTree.
118 // For instance the user may use direct access to the ptree elements to alter
119 // the mass of a link, but shall no delete the link, otherwise calls to
120 // RobotTree::link("my_link_name") would use dandling pointers and return
121 // invalid references.
122 //
123 // On the other hand, some RobotTree functions enable the safe modification or
124 // removal of joints and links.
125 class ALMATH_API RobotTree {
126  public:
127  // Read a XML tree given the robot element.
128  // The ptree is not copied, a reference is kept instead.
129  // The user shall ensure that the reference is valid for the whole life
130  // of the RobotTree object.
131  RobotTree(ptree &robot);
132  ~RobotTree();
133  RobotTree(RobotTree const &) = delete;
134  void operator=(RobotTree const &other) = delete;
135 
136  const ptree &link(const std::string &name) const;
137  ptree &link(const std::string &name);
138 
139  const ptree &joint(const std::string &name) const;
140  ptree &joint(const std::string &name);
141 
142  const std::string &root_link() const;
143 
144  // If there is a single root joint, remove it and its parent link, throw otherwise.
145  //
146  // References and iterators to the removed link and joint are invalidated.
147  void rm_root_joint();
148 
149  // If the given joint is a leaf in the kinematic tree,
150  // remove it and its child link, throw otherwise.
151  //
152  // References and iterators to the removed link and joint are invalidated.
153  void rm_leaf_joint(const std::string &name);
154 
155  // Change the root link frame of reference.
156  //
157  // Each urdf link has an implicitly defined frame of reference with respect
158  // to which the pose of the link children elements (inertia, collisions,
159  // visuals) and joints are defined.
160  //
161  // This function changes the root link frame of reference, while updating
162  // all its children elements and joints accordingly so that the physical
163  // system being described does not change.
164  //
165  // The function takes one argument "pose" which is used to define the
166  // transform from the old frame of reference of the new one according to
167  // the formula:
168  //
169  // point_in_new_frame = pose * point_in_old_frame
170  //
171  // children elements and joints poses are updated using:
172  //
173  // child_pose_in_new_frame = pose.inverse() * child_pose_in_old_frame
174  //
175  // Note: changing the frame of reference of a link which is not the root one
176  // would also be possible, but only to some extent since its origin must
177  // lie on the parent joint axis.
178  void transport_root_link_frame(const Pose &pose);
179 
180  // Modify the kinematic tree so that the given link is the root without
181  // changing the physical meaning of the described system.
182  //
183  // Flips all the joints on the path between the old and the new root.
184  //
185  // Throws is there is a multi-dof joint on the path, because flipping it
186  // would change the joint-space parametrization.
187 
188  // Change the frame of reference of the links involved so that its
189  // origin lies on the parent joint axis.
190  //
191  // Let consider a root link "a" with children links "b" and "c" and
192  // joints "ab" and "bc".
193  //
194  // The kinematic tree can be written as: a --ab--> b
195  // +-ac--> c
196  //
197  // The frame may look like:
198  // / \ .
199  // / \ .
200  // / \ / / .
201  // / \ / / .
202  // --/ \/ /--/ \/ /-- .
203  // __________/ ab / \ b / .
204  // / / \ / .
205  // / a |_ / .
206  // / \ / __________/ .
207  // / \ / / .
208  // --/ \/ /--/ \/ /-- .
209  // / c / \ ac/ .
210  // / / \ / .
211  // \ / .
212  // \ / .
213  //
214  // After calling define_as_root_link("b")
215  // * the kinematic tree can be written as
216  // b --ab--> a --ac--> c
217  // * the reference frame of "b" and "c" are unchanged
218  // * the reference frame of "a" moved to joint "ab" frame, so as to lie
219  // on joint "ab" axis.
220  // * joint "ab" origin pose is now defined with respecte to link "b"
221  // frame and is thus the identity
222  // * joint "ac" origin pose has been updated to acount fot the
223  //
224  // The frames look like:
225  // / \ .
226  // / \ .
227  // / \ / / .
228  // / \ / / .
229  // --/ \/ /--/ \/ /-- .
230  // __________/ a / \ b / == ba .
231  // / / \ / .
232  // / / .
233  // / \ / __________/ .
234  // / \ / / .
235  // --/ \/ /--/ \/ /-- .
236  // / c / \ ac/ .
237  // / / \ / .
238  // \ / .
239  // \ / .
240  void define_as_root_link(const std::string &name);
241 
243  public:
244  // a false return value stops the traversal for the current branch
245  virtual bool discover(const ptree &) { return true; }
246  virtual void finish(const ptree &) {}
247  };
248  class JointVisitor {
249  public:
250  virtual bool discover(ptree &) { return true; }
251  virtual void finish(ptree &) {}
252  };
253 
254  // Traverse the kinematic tree with a depth first traversal.
255  // Siblings joints are visited using the ordering from the urdf file.
256  void traverse_joints(JointConstVisitor &visitor) const;
257  void traverse_joints(JointVisitor &visitor);
258 
259  bool is_mimic_tree_flat() const;
260 
261  private:
262  std::unique_ptr<detail::RobotTreeP> _p;
263 };
264 
267 
268 // Convenience wrapper classes around URDF ptree elements
269 
270 // helper to convert ptree to/from an array of floating point numbers
271 template <typename Scalar, std::size_t N>
273  typedef std::string internal_type;
274  typedef std::array<Scalar, N> external_type;
275  static_assert(std::is_scalar<Scalar>::value,
276  "ScalarArrayTranslator requires a scalar");
277  boost::optional<external_type> get_value(const internal_type &str) {
278  // str is expected to hold N floating point numbers.
279  // let split it into N strings, then use the usual boost::property_tree
280  // translator to convert each substring into a Scalar.
281  // In case of failure, return an uninitialized boost::optional, like
282  // boost::property_tree does itself.
283  boost::tokenizer<> tok(
284  str, boost::char_delimiters_separator<char>(false, "", " \t\n\v\f\r"));
285  boost::optional<Scalar> d;
286  // note: maybe the tr variable could be made static const?
287  typename
288  boost::property_tree::translator_between<internal_type, Scalar>::type tr;
289  external_type x;
290  boost::tokenizer<>::iterator beg = tok.begin();
291  size_t i = 0;
292  for (; beg != tok.end() && i < N; ++beg, ++i) {
293  d = tr.get_value(*beg);
294  if (!d) return boost::optional<external_type>();
295  x[i] = *d;
296  }
297  if (i != N || beg != tok.end())
298  return boost::optional<external_type>();
299  return boost::optional<external_type>(x);
300  }
301 
302  boost::optional<internal_type> put_value(const external_type &v) {
303  // note: maybe the tr variable could be made static const?
304  typename
305  boost::property_tree::translator_between<internal_type, Scalar>::type tr;
306  std::ostringstream ss;
307  if (N > 0u)
308  ss << *tr.put_value(v[0]);
309  for (auto i=1u; i < N; ++i) {
310  ss << " " << *tr.put_value(v[i]);
311  }
312  return ss.str();
313  }
314 };
315 
317 
318 // Models an URDF pose/transform ("origin" XML element)
319 class ALMATH_API Pose {
320  public:
321  Pose(const Array3d &xyz, const Array3d &rpy) : _xyz(xyz), _rpy(rpy) {}
322  Pose() : Pose({{0, 0, 0}}, {{0, 0, 0}}) {}
323  const Array3d &xyz() const { return _xyz; }
324  const Array3d &rpy() const { return _rpy; }
325  Pose inverse() const;
326  ALMATH_API friend Pose operator*(const Pose &lhs, const Pose &rhs);
327 
328  static Pose from_ptree(const ptree &pt);
329  static Pose from_ptree(const boost::optional<const ptree &> &pt);
330  boost::optional<ptree> to_ptree() const;
331 
332  private:
333  Array3d _xyz;
334  Array3d _rpy;
335 };
336 
337 inline bool operator==(const Pose &lhs, const Pose &rhs) {
338  return (lhs.xyz() == rhs.xyz()) && (lhs.rpy() == rhs.rpy());
339 }
340 inline bool operator!=(const Pose &lhs, const Pose &rhs) {
341  return !(lhs == rhs);
342 }
343 
344 inline bool is_identity(const Pose &p) {
345  return is_zero(p.xyz()) && is_zero(p.rpy());
346 }
347 
348 // Convenience wrapper around an URDF mimic XML element
349 class ALMATH_API Mimic {
350  public:
351  const ptree &pt;
352  Mimic(const ptree &pt);
353  std::string joint() const;
354  double multiplier() const;
355  double offset() const;
356 };
357 
358 // Convenience wrapper around an URDF joint XML element
359 class ALMATH_API Joint {
360  public:
361  enum Type { revolute, continuous, prismatic, fixed, floating, planar };
362  const ptree &pt;
363  Joint(const ptree &pt);
364  std::string name() const;
365  Type type() const;
366  std::string parent_link() const;
367  std::string child_link() const;
368  Pose origin() const;
369  Array3d axis() const;
370 
371  // Return the (lower, upper) limits pair.
372  //
373  // If both limits are missing, the pair is uninitialized
374  // If one of them is missing, it is defaulted to 0.
375  // This behavior is slightly different from the
376  // http://wiki.ros.org/urdf/XML/joint spec.
377  // Beware, there is no warranty that lower <= upper.
378  boost::optional<std::pair<double, double>> limit_lower_upper() const;
379  boost::optional<double> limit_effort() const;
380  boost::optional<double> limit_velocity() const;
381  boost::optional<Mimic> mimic() const;
382 };
383 
384 // Convenience wrapper around an URDF inertial XML element
385 class ALMATH_API Inertial {
386  public:
387  const ptree &pt;
388  Inertial(const ptree &pt);
389  Pose origin() const;
390  double mass() const;
391  double ixx() const;
392  double ixy() const;
393  double ixz() const;
394  double iyy() const;
395  double iyz() const;
396  double izz() const;
397 };
398 
399 class ALMATH_API Box {
400  public:
401  const ptree &pt;
402  Box(const ptree &pt);
403  Array3d size();
404 };
405 
406 // z-axis cylinder, centered at the origin
407 class ALMATH_API Cylinder {
408  public:
409  const ptree &pt;
410  Cylinder(const ptree &pt);
411  double radius();
412  double length();
413 };
414 
415 class ALMATH_API Sphere {
416  public:
417  const ptree &pt;
418  Sphere(const ptree &pt);
419  double radius();
420 };
421 
422 class ALMATH_API Mesh {
423  public:
424  const ptree &pt;
425  Mesh(const ptree &pt);
426  std::string filename() const;
427  // Note: scale element is optional. When absent, we return (1, 1, 1)
428  Array3d scale() const;
429 };
430 
431 using Geometry = boost::variant<Box, Cylinder, Sphere, Mesh>;
432 
433 class ALMATH_API Visual {
434 public:
435  const ptree &pt;
436  Visual(const ptree &pt);
437  Pose origin() const;
438  Geometry geometry() const;
439 
440  static bool is_visual(const ptree::value_type &val);
441 };
442 
443 // Convenience wrapper around an URDF link XML element
444 class ALMATH_API Link {
445  public:
446  const ptree &pt;
447  Link(const ptree &pt);
448  std::string name() const;
449  boost::optional<Inertial> inertial() const;
450 
451  private:
452  // helper: return the range of visual ptree children
453  inline auto _visual_ptrees() const
454  -> boost::select_second_const_range<
455  decltype(boost::adaptors::filter(pt, Visual::is_visual))> {
456  return boost::adaptors::values(
457  boost::adaptors::filter(pt, Visual::is_visual));
458  }
459  // helper, call the Visual ctor
460  static inline Visual _makeVisual(const ptree &pt) { return Visual(pt); }
461 
462  public:
463  // return the range of Visual children
464  inline auto visuals() const
465  -> boost::transformed_range<
466  decltype(&_makeVisual),
467  const decltype(_visual_ptrees())> {
468  return boost::adaptors::transform(_visual_ptrees(), &_makeVisual);
469  }
470 };
471 
472 // Utils
473 
474 // make the type of the joint named "name" equal to "fixed",
475 // and erase the joint axis and limits, if any.
476 ALMATH_API void makeJointFixed(RobotTree &parser, const std::string &name);
477 
478 // make the type of the joint named "name" equal to "floating",
479 // and erase the joint axis and limits, if any.
480 ALMATH_API void makeJointFloating(RobotTree &parser, const std::string &name);
481 
482 // apply makeJointFixed to all joints of type "continuous".
483 // Return the names of the joints whose type was changed.
484 ALMATH_API
485 std::vector<std::string> makeContinuousJointsFixed(RobotTree &parser);
486 
487 // Squash a joint's child link mass into its parent link mass.
488 // The child link mass element (if any) is erased.
489 ALMATH_API void squashJointMass(RobotTree &parser, const std::string &name);
490 
491 // apply squashJointMass to all fixed joints.
492 ALMATH_API void squashFixedJointsMass(RobotTree &parser);
493 
494 // Squash all fixed joint's child link mass into their parent link mass and
495 // then convert all massless joints into fixed joints.
496 // The point is to avoid massless mobile joints, which have no physical
497 // meaning.
498 // Return the names of the joints whose type was changed.
499 ALMATH_API std::vector<std::string> makeMasslessJointsFixed(RobotTree &parser);
500 
501 // Remove kinematic tree subtrees starting at joints for which predicate
502 // returns true.
503 //
504 // Return the names of the removed joints.
505 //
506 // For instance, to cut the tree at joints whose name ends with "_useless",
507 // one could do:
508 //
509 // ptree robot = ...
510 // auto pred = [](const ptree &joint) {
511 // return boost::regex_match(urdf::name(joint), boost::regex(".*_useless$"));
512 // };
513 // urdf::removeSubTreeIfJoint(urdf::RobotTree(robot), pred);
514 ALMATH_API std::vector<std::string> removeSubTreeIfJoint(
515  RobotTree &parser,
516  std::function<bool(const ptree &joint)> pred);
517 
518 // a visitor which prints the URDF kinematic tree as a dot graph
519 // when visiting its joints.
521  const char tab;
522  int depth;
523  bool do_indent;
524  std::ostream &os;
525 
526  public:
527  UrdfDotPrinterVisitor(std::ostream &os);
528 
529  public:
530  bool discover(const ptree &joint);
531  void finish(const ptree &joint);
532 };
533 
534 
535 ALMATH_API void put_name(ptree &pt, const std::string &name);
536 
537 namespace robot {
538 
539 // Replace each mesh and texture filename by the result of applying op() to it.
540 //
541 // For instance, to replace .mesh extensions by .dae, one could do:
542 //
543 // ptree robot = ...
544 // auto op = [](std::string in) {
545 // return boost::regex_replace(in, boost::regex("\\.mesh$"), ".dae");};
546 // urdf::robot::transform_filenames(robot, op);
547 ALMATH_API void transform_filenames(
548  ptree &robot, std::function<std::string(std::string)> op);
549 }
550 }
551 }
552 
553 #endif
std::array< Scalar, N > external_type
Definition: urdf.h:274
boost::optional< internal_type > put_value(const external_type &v)
Definition: urdf.h:302
const ptree & pt
Definition: urdf.h:401
ALMATH_API void squashJointMass(RobotTree &parser, const std::string &name)
ALMATH_API std::string name(const ptree &pt)
ALMATH_API void put_name(ptree &pt, const std::string &name)
ALMATH_API std::vector< std::string > makeMasslessJointsFixed(RobotTree &parser)
ALMATH_API void squashFixedJointsMass(RobotTree &parser)
bool operator==(const Pose &lhs, const Pose &rhs)
Definition: urdf.h:337
boost::variant< Box, Cylinder, Sphere, Mesh > Geometry
Definition: urdf.h:431
const ptree & pt
Definition: urdf.h:351
const ptree & pt
Definition: urdf.h:417
std::array< double, 3 > Array3d
Definition: urdf.h:66
ALMATH_API std::string parent_link(const ptree &pt)
bool is_identity(const Pose &p)
Definition: urdf.h:344
ALMATH_API std::vector< std::string > removeSubTreeIfJoint(RobotTree &parser, std::function< bool(const ptree &joint)> pred)
static bool is_visual(const ptree::value_type &val)
bool operator!=(const Pose &lhs, const Pose &rhs)
Definition: urdf.h:340
const ptree & pt
Definition: urdf.h:362
virtual void finish(ptree &)
Definition: urdf.h:251
boost::optional< external_type > get_value(const internal_type &str)
Definition: urdf.h:277
Transform inverse(const Transform &tf)
Definition: qigeometry.h:120
const ptree & pt
Definition: urdf.h:387
static void extract(std::basic_istream< Ch, Traits > &s, double &e)
Definition: urdf.h:46
RobotTree::JointVisitor JointVisitor
Definition: urdf.h:266
virtual bool discover(ptree &)
Definition: urdf.h:250
ALMATH_API std::string child_link(const ptree &pt)
virtual bool discover(const ptree &)
Definition: urdf.h:245
static void insert(std::basic_ostream< Ch, Traits > &s, const double &e)
Definition: urdf.h:42
ALMATH_API void makeJointFloating(RobotTree &parser, const std::string &name)
ALMATH_API void makeJointFixed(RobotTree &parser, const std::string &name)
Transform operator*(const Transform &lhs, const Transform &rhs)
Definition: qigeometry.h:96
virtual void finish(const ptree &)
Definition: urdf.h:246
ALMATH_API std::vector< std::string > makeContinuousJointsFixed(RobotTree &parser)
ALMATH_API void transform_filenames(ptree &robot, std::function< std::string(std::string)> op)
boost::property_tree::ptree ptree
Definition: urdf.h:60
const ptree & pt
Definition: urdf.h:424
Pose(const Array3d &xyz, const Array3d &rpy)
Definition: urdf.h:321
bool is_ones(const Array3d &a)
Definition: urdf.h:72
const ptree & pt
Definition: urdf.h:435
RobotTree::JointConstVisitor JointConstVisitor
Definition: urdf.h:265
bool is_zero(const Array3d &a)
Definition: urdf.h:68
const ptree & pt
Definition: urdf.h:409