HEBI C++ API  2.2.0
robot_model.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include "hebi.h"
4 
5 #include <memory>
6 #include <vector>
7 
8 #include "Eigen/Eigen"
9 #include "util.hpp"
10 
11 using namespace Eigen;
12 
13 namespace hebi {
14 namespace robot_model {
15 
16 using Matrix4dVector = std::vector<Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>;
17 using MatrixXdVector = std::vector<MatrixXd, Eigen::aligned_allocator<Eigen::MatrixXd>>;
18 // The result of an IK operation. More fields will be added to this structure
19 // in future API releases.
20 struct IKResult {
21  HebiStatusCode result; // Success or failure
22 };
23 
24 class RobotModel;
25 
26 class Objective {
27  friend RobotModel;
28 
29 public:
30  virtual ~Objective() {}
31 
32 protected:
33  virtual HebiStatusCode addObjective(HebiIKPtr ik) const = 0;
34 };
35 
37 public:
38  EndEffectorPositionObjective(const Eigen::Vector3d&);
39  EndEffectorPositionObjective(double weight, const Eigen::Vector3d&);
40 
41 private:
42  HebiStatusCode addObjective(HebiIKPtr ik) const override;
43  double _weight, _x, _y, _z;
44 };
45 
46 class EndEffectorSO3Objective final : public Objective {
47 public:
48  EndEffectorSO3Objective(const Eigen::Matrix3d&);
49  EndEffectorSO3Objective(double weight, const Eigen::Matrix3d&);
50 
51 private:
52  HebiStatusCode addObjective(HebiIKPtr ik) const override;
53  double _weight;
54  const double _matrix[9];
55 };
56 
57 class EndEffectorTipAxisObjective final : public Objective {
58 public:
59  EndEffectorTipAxisObjective(const Eigen::Vector3d&);
60  EndEffectorTipAxisObjective(double weight, const Eigen::Vector3d&);
61 
62 private:
63  HebiStatusCode addObjective(HebiIKPtr ik) const override;
64  double _weight, _x, _y, _z;
65 };
66 
67 class JointLimitConstraint final : public Objective {
68 public:
69  JointLimitConstraint(const Eigen::VectorXd& min_positions, const Eigen::VectorXd& max_positions);
70  JointLimitConstraint(double weight, const Eigen::VectorXd& min_positions, const Eigen::VectorXd& max_positions);
71 
72 private:
73  HebiStatusCode addObjective(HebiIKPtr ik) const override;
74  double _weight;
75  Eigen::VectorXd _min_positions;
76  Eigen::VectorXd _max_positions;
77 
78 public:
79  // Allow Eigen member variables:
80  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
81 };
82 
91 template<size_t T>
92 inline void custom_objective_callback_wrapper(void* user_data, size_t num_positions, const double* positions,
93  double* errors);
94 
134 template<size_t N>
135 class CustomObjective final : public Objective {
136 public:
137  // This function is called with the following parameters:
138  // const std::vector<double>& positions
139  // std::array<double, N>& errors (NOTE: FILL THIS IN VIA THE CALLBACK FUNCTION)
140  using ObjectiveCallback = std::function<void(const std::vector<double>&, std::array<double, N>&)>;
141 
142  CustomObjective(ObjectiveCallback error_function) : _weight(1.0f), _callback(error_function) {}
143  CustomObjective(double weight, ObjectiveCallback error_function) : _weight(weight), _callback(error_function) {}
144 
145  // Note -- internal function to be called only from
146  // custom_objective_callback_wrapper.
147  void callCallback(void*, size_t num_positions, const double* positions, double* errors) const {
148  // Note -- the data here is copied to/from the C-style arrays. This isn't
149  // optimally efficient, but allows us to use type-checked C++ vectors and
150  // arrays. For performance critical applications, this could be modified
151  // to support user callbacks using the raw C-style arrays directly.
152 
153  // Process data into C++ structures.
154  std::vector<double> positions_array(num_positions);
155  for (size_t i = 0; i < num_positions; ++i)
156  positions_array[i] = positions[i];
157  // Note -- std::array is not guaranteed to be layout-compatible with a
158  // C-style array, even for POD, so we must copy here it we want the type
159  // safety of std::array.
160  std::array<double, N> errors_array;
161  for (size_t i = 0; i < N; ++i)
162  errors_array[i] = errors[i];
163 
164  _callback(positions_array, errors_array);
165 
166  for (size_t i = 0; i < N; ++i)
167  errors[i] = errors_array[i];
168  }
169 
170 private:
171  HebiStatusCode addObjective(HebiIKPtr ik) const override {
172  return hebiIKAddObjectiveCustom(ik, _weight, N, &custom_objective_callback_wrapper<N>,
173  const_cast<CustomObjective*>(this));
174  }
175  double _weight;
176  ObjectiveCallback _callback;
177 };
178 
184 template<size_t T>
185 inline void custom_objective_callback_wrapper(void* user_data, size_t num_positions, const double* positions,
186  double* errors) {
187  reinterpret_cast<CustomObjective<T>*>(user_data)->callCallback(user_data, num_positions, positions, errors);
188 }
189 
190 class ActuatorMetadata;
191 class BracketMetadata;
192 class JointMetadata;
193 class LinkMetadata;
194 class RigidBodyMetadata;
195 
200  friend class RobotModel;
201 public:
202  MetadataBase() = default;
203  MetadataBase(const MetadataBase&) = delete;
204  MetadataBase& operator=(const MetadataBase&) = delete;
205  MetadataBase(MetadataBase&&) = default;
206  MetadataBase& operator=(MetadataBase&&) = default;
207 
208  HebiRobotModelElementType elementType() const {
209  return metadata_.element_type_;
210  }
211 
217  const ActuatorMetadata* asActuator() const {
218  if (elementType() == HebiRobotModelElementTypeActuator) {
219  return reinterpret_cast<const ActuatorMetadata*>(this);
220  }
221  return nullptr;
222  }
223 
229  const BracketMetadata* asBracket() const {
230  if (elementType() == HebiRobotModelElementTypeBracket) {
231  return reinterpret_cast<const BracketMetadata*>(this);
232  }
233  return nullptr;
234  }
235 
241  const JointMetadata* asJoint() const {
242  if (elementType() == HebiRobotModelElementTypeJoint) {
243  return reinterpret_cast<const JointMetadata*>(this);
244  }
245  return nullptr;
246  }
247 
253  const LinkMetadata* asLink() const {
254  if (elementType() == HebiRobotModelElementTypeLink) {
255  return reinterpret_cast<const LinkMetadata*>(this);
256  }
257  return nullptr;
258  }
259 
266  if (elementType() == HebiRobotModelElementTypeRigidBody) {
267  return reinterpret_cast<const RigidBodyMetadata*>(this);
268  }
269  return nullptr;
270  }
271 
272 protected:
273  // Raw data can only be accessed by the sub classes
274  const HebiRobotModelElementMetadata& metadata() const { return metadata_; }
275 private:
276  HebiRobotModelElementMetadata metadata_;
277 };
278 
283 public:
287  HebiActuatorType actuatorType() const { return metadata().actuator_type_; }
288 };
289 
294 public:
298  HebiBracketType bracketType() const { return metadata().bracket_type_; }
299 };
300 
304 class JointMetadata : public MetadataBase {
305 public:
309  HebiJointType jointType() const { return metadata().joint_type_; }
310 };
311 
315 class LinkMetadata : public MetadataBase {
316 public:
320  HebiLinkType linkType() const { return metadata().link_type_; }
321 
325  float extension() const { return metadata().extension_; }
326 
330  float twist() const { return metadata().twist_; }
331 };
332 
336 class RigidBodyMetadata : public MetadataBase {};
337 
344 class RobotModel final {
345  friend Objective;
346 
347 private:
351  HebiRobotModelPtr const internal_;
352 
357  template<typename T>
358  HebiStatusCode addObjectives(HebiIKPtr ik, const T& objective) const {
359  static_assert(std::is_convertible<T*, Objective*>::value,
360  "Must pass arguments of base type hebi::robot_model::Objective to the variable args of solveIK!");
361  return static_cast<const Objective*>(&objective)->addObjective(ik);
362  }
363 
367  template<typename T, typename... Args>
368  HebiStatusCode addObjectives(HebiIKPtr ik, const T& objective, Args... args) const {
369  static_assert(std::is_convertible<T*, Objective*>::value,
370  "Must pass arguments of base type hebi::robot_model::Objective to the variable args of solveIK!");
371  auto res = static_cast<const Objective*>(&objective)->addObjective(ik);
372  if (res != HebiStatusSuccess)
373  return res;
374  return addObjectives(ik, args...);
375  }
376 
380  bool tryAdd(HebiRobotModelElementPtr element, bool combine);
381 
386  RobotModel(HebiRobotModelPtr);
387 
388 public:
389  enum class ActuatorType { X5_1, X5_4, X5_9, X8_3, X8_9, X8_16 };
390 
391  enum class LinkType { X5 };
392 
393  enum class BracketType {
394  X5LightLeft,
395  X5LightRight,
396  X5HeavyLeftInside,
397  X5HeavyLeftOutside,
398  X5HeavyRightInside,
399  X5HeavyRightOutside
400  };
401 
406  RobotModel();
407 
412  static std::unique_ptr<RobotModel> loadHRDF(const std::string& file);
413 
418  ~RobotModel() noexcept;
419 
431  void setBaseFrame(const Eigen::Matrix4d& base_frame);
432 
437  Eigen::Matrix4d getBaseFrame() const;
438 
448  size_t getFrameCount(HebiFrameType frame_type) const;
449 
454  size_t getDoFCount() const;
455 
478  bool addRigidBody(const Eigen::Matrix4d& com, const Eigen::VectorXd& inertia, double mass,
479  const Eigen::Matrix4d& output, bool combine);
480 
490  bool addJoint(HebiJointType joint_type, bool combine);
491 
498  bool addActuator(ActuatorType actuator_type);
499 
514  bool addLink(LinkType link_type, double extension, double twist);
515 
522  bool addBracket(BracketType bracket_type);
523 
529  void getForwardKinematics(HebiFrameType, const Eigen::VectorXd& positions, Matrix4dVector& frames) const;
555  void getFK(HebiFrameType, const Eigen::VectorXd& positions, Matrix4dVector& frames) const;
556 
576  void getEndEffector(const Eigen::VectorXd& positions, Eigen::Matrix4d& transform) const;
577 
584  template<typename... Args>
585  IKResult solveInverseKinematics(const Eigen::VectorXd& initial_positions, Eigen::VectorXd& result,
586  Args... args) const {
587  return solveIK(initial_positions, result, args...);
588  }
589 
604  template<typename... Args>
605  IKResult solveIK(const Eigen::VectorXd& initial_positions, Eigen::VectorXd& result, Args... objectives) const {
606  // Create a HEBI C library IK object
607  auto ik = hebiIKCreate();
608 
609  // (Try) to add objectives to the IK object
610  IKResult res;
611  res.result = addObjectives(ik, objectives...);
612  if (res.result != HebiStatusSuccess) {
613  hebiIKRelease(ik);
614  return res;
615  }
616 
617  // Transfer/initialize from Eigen to C arrays
618  auto positions_array = new double[initial_positions.size()];
619  {
620  Map<Eigen::VectorXd> tmp(positions_array, initial_positions.size());
621  tmp = initial_positions;
622  }
623  auto result_array = new double[initial_positions.size()];
624 
625  // Call into C library to solve
626  res.result = hebiIKSolve(ik, internal_, positions_array, result_array, nullptr);
627 
628  // Transfer/cleanup from C arrays to Eigen
629  delete[] positions_array;
630  {
631  Map<Eigen::VectorXd> tmp(result_array, initial_positions.size());
632  result = tmp;
633  }
634  delete[] result_array;
635 
636  hebiIKRelease(ik);
637 
638  return res;
639  }
640 
646  void getJacobians(HebiFrameType, const Eigen::VectorXd& positions, MatrixXdVector& jacobians) const;
660  void getJ(HebiFrameType, const Eigen::VectorXd& positions, MatrixXdVector& jacobians) const;
661 
667  void getJacobianEndEffector(const Eigen::VectorXd& positions, Eigen::MatrixXd& jacobian) const;
687  void getJEndEffector(const Eigen::VectorXd& positions, Eigen::MatrixXd& jacobian) const;
688 
697  void getMasses(Eigen::VectorXd& masses) const;
698 
702  void getMetadata(std::vector<MetadataBase>& metadata) const;
703 private:
708 };
709 
710 } // namespace robot_model
711 } // namespace hebi
std::vector< Matrix4d, Eigen::aligned_allocator< Eigen::Matrix4d > > Matrix4dVector
Definition: robot_model.hpp:16
const RigidBodyMetadata * asRigidBody() const
View the rigid body specific fields, if this metadata describes a rigid body.
Definition: robot_model.hpp:265
BracketType
Definition: robot_model.hpp:393
Link specific view of an element in a RobotModel instance.
Definition: robot_model.hpp:315
void custom_objective_callback_wrapper(void *user_data, size_t num_positions, const double *positions, double *errors)
C-style callback wrapper to call into CustomObjective class; this should only be used by the CustomOb...
Definition: robot_model.hpp:185
const ActuatorMetadata * asActuator() const
View the actuator specific fields, if this metadata describes an actuator.
Definition: robot_model.hpp:217
Definition: color.hpp:5
const BracketMetadata * asBracket() const
View the bracket specific fields, if this metadata describes a bracket.
Definition: robot_model.hpp:229
HebiActuatorType actuatorType() const
Specifies the particular actuator model.
Definition: robot_model.hpp:287
CustomObjective(ObjectiveCallback error_function)
Definition: robot_model.hpp:142
#define HEBI_DISABLE_COPY_MOVE(Class)
Definition: util.hpp:6
Represents a chain or tree of robot elements (rigid bodies and joints).
Definition: robot_model.hpp:344
HebiRobotModelElementType elementType() const
Definition: robot_model.hpp:208
IKResult solveIK(const Eigen::VectorXd &initial_positions, Eigen::VectorXd &result, Args... objectives) const
Solves for an inverse kinematics solution given a set of objectives.
Definition: robot_model.hpp:605
float twist() const
The twist of the link (e.g., the value specified in invocation of RobotModel::addLink)
Definition: robot_model.hpp:330
Actuator specific view of an element in a RobotModel instance.
Definition: robot_model.hpp:282
HebiBracketType bracketType() const
Specifies the particular bracket type.
Definition: robot_model.hpp:298
const JointMetadata * asJoint() const
View the joint specific fields, if this metadata describes a joint.
Definition: robot_model.hpp:241
HebiStatusCode result
Definition: robot_model.hpp:21
Definition: robot_model.hpp:67
Base class for all metadata. Do not instantiate directly.
Definition: robot_model.hpp:199
virtual ~Objective()
Definition: robot_model.hpp:30
LinkType
Definition: robot_model.hpp:391
Definition: robot_model.hpp:26
Definition: robot_model.hpp:46
HebiLinkType linkType() const
Specifies the particular link type.
Definition: robot_model.hpp:320
Rigid Body specific view of an element in a RobotModel instance.
Definition: robot_model.hpp:336
Allows you to add a custom objective function.
Definition: robot_model.hpp:135
Bracket specific view of an element in a RobotModel instance.
Definition: robot_model.hpp:293
HebiJointType jointType() const
Specifies the particular joint type.
Definition: robot_model.hpp:309
Joint specific view of an element in a RobotModel instance.
Definition: robot_model.hpp:304
void callCallback(void *, size_t num_positions, const double *positions, double *errors) const
Definition: robot_model.hpp:147
Definition: robot_model.hpp:20
std::function< void(const std::vector< double > &, std::array< double, N > &)> ObjectiveCallback
Definition: robot_model.hpp:140
float extension() const
The extension of the link (e.g., the value specified in invocation of RobotModel::addLink)
Definition: robot_model.hpp:325
ActuatorType
Definition: robot_model.hpp:389
const HebiRobotModelElementMetadata & metadata() const
Definition: robot_model.hpp:274
std::vector< MatrixXd, Eigen::aligned_allocator< Eigen::MatrixXd > > MatrixXdVector
Definition: robot_model.hpp:17
CustomObjective(double weight, ObjectiveCallback error_function)
Definition: robot_model.hpp:143
const LinkMetadata * asLink() const
View the link specific fields, if this metadata describes a link.
Definition: robot_model.hpp:253