Crocoddyl
Loading...
Searching...
No Matches
floating-base-thrusters.hpp
1
2// BSD 3-Clause License
3//
4// Copyright (C) 2014-2026, Heriot-Watt University
5// Copyright note valid unless otherwise stated in individual files.
6// All rights reserved.
8
9#ifndef CROCODDYL_MULTIBODY_ACTUATIONS_PROPELLERS_HPP_
10#define CROCODDYL_MULTIBODY_ACTUATIONS_PROPELLERS_HPP_
11
12#include "crocoddyl/core/actuation-base.hpp"
13#include "crocoddyl/core/utils/conversions.hpp"
14#include "crocoddyl/multibody/states/multibody.hpp"
15
16namespace crocoddyl {
17
18enum ThrusterType { CW = 0, CCW };
19
20template <typename _Scalar>
21struct ThrusterTpl {
22 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
23
24 typedef _Scalar Scalar;
25 typedef pinocchio::SE3Tpl<Scalar> SE3;
26 typedef ThrusterTpl<Scalar> Thruster;
27
38 ThrusterTpl(const SE3& pose, const Scalar ctorque,
39 const ThrusterType type = CW,
40 const Scalar min_thrust = Scalar(0.),
41 const Scalar max_thrust = std::numeric_limits<Scalar>::infinity())
42 : pose(pose),
44 type(type),
47
58 ThrusterTpl(const Scalar ctorque, const ThrusterType type = CW,
59 const Scalar min_thrust = Scalar(0.),
60 const Scalar max_thrust = std::numeric_limits<Scalar>::infinity())
61 : pose(SE3::Identity()),
63 type(type),
67 : pose(clone.pose),
68 ctorque(clone.ctorque),
69 type(clone.type),
71 max_thrust(clone.max_thrust) {}
72
73 template <typename NewScalar>
74 ThrusterTpl<NewScalar> cast() const {
75 typedef ThrusterTpl<NewScalar> ReturnType;
76 ReturnType ret(
77 pose.template cast<NewScalar>(), scalar_cast<NewScalar>(ctorque), type,
78 scalar_cast<NewScalar>(min_thrust), scalar_cast<NewScalar>(max_thrust));
79 return ret;
80 }
81
82 ThrusterTpl& operator=(const ThrusterTpl<Scalar>& other) {
83 if (this != &other) {
84 pose = other.pose;
85 ctorque = other.ctorque;
86 type = other.type;
87 min_thrust = other.min_thrust;
88 max_thrust = other.max_thrust;
89 }
90 return *this;
91 }
92
93 template <typename OtherScalar>
94 bool operator==(const ThrusterTpl<OtherScalar>& other) const {
95 return (pose == other.pose && ctorque == other.ctorque &&
96 type == other.type && min_thrust == other.min_thrust &&
97 max_thrust == other.max_thrust);
98 }
99
100 friend std::ostream& operator<<(std::ostream& os,
101 const ThrusterTpl<Scalar>& X) {
102 typedef typename ScalarSelector<Scalar>::type PrintableScalar;
103 os << " pose:" << std::endl
104 << " R =\n"
105 << X.pose.rotation().template cast<PrintableScalar>() << std::endl
106 << " p = "
107 << X.pose.translation().template cast<PrintableScalar>().transpose()
108 << std::endl
109 << " ctorque: " << scalar_cast<PrintableScalar>(X.ctorque) << std::endl
110 << " type: " << X.type << std::endl
111 << "min_thrust: " << scalar_cast<PrintableScalar>(X.min_thrust)
112 << std::endl
113 << "max_thrust: " << scalar_cast<PrintableScalar>(X.max_thrust)
114 << std::endl;
115 return os;
116 }
117
118 SE3 pose;
119 Scalar ctorque;
120 ThrusterType type;
122 Scalar min_thrust;
123 Scalar max_thrust;
124};
125
144template <typename _Scalar>
146 : public ActuationModelAbstractTpl<_Scalar> {
147 public:
148 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
149 CROCODDYL_DERIVED_CAST(ActuationModelBase,
151
152 typedef _Scalar Scalar;
153 typedef MathBaseTpl<Scalar> MathBase;
156 typedef StateMultibodyTpl<Scalar> StateMultibody;
157 typedef ThrusterTpl<Scalar> Thruster;
158 typedef typename MathBase::Vector3s Vector3s;
159 typedef typename MathBase::VectorXs VectorXs;
160 typedef typename MathBase::MatrixXs MatrixXs;
161
169 ActuationModelFloatingBaseThrustersTpl(std::shared_ptr<StateMultibody> state,
170 const std::vector<Thruster>& thrusters)
171 : Base(state,
172 state->get_nv() -
173 state->get_pinocchio()
174 ->joints[(
175 state->get_pinocchio()->existJointName("root_joint")
176 ? state->get_pinocchio()->getJointId("root_joint")
177 : 0)]
178 .nv() +
179 thrusters.size()),
180 thrusters_(thrusters),
181 n_thrusters_(thrusters.size()),
182 W_thrust_(state_->get_nv(), nu_),
183 update_data_(true) {
184 if (!state->get_pinocchio()->existJointName("root_joint")) {
185 throw_pretty(
186 "Invalid argument: "
187 << "the first joint has to be a root one (e.g., free-flyer joint)");
188 }
189 // Update the joint actuation part
190 const std::size_t nv_f =
191 state->get_pinocchio()
192 ->joints[(state->get_pinocchio()->existJointName("root_joint")
193 ? state->get_pinocchio()->getJointId("root_joint")
194 : 0)]
195 .nv();
196 W_thrust_.setZero();
197 if (nu_ > n_thrusters_) {
198 W_thrust_.bottomRightCorner(nu_ - n_thrusters_, nu_ - n_thrusters_)
199 .diagonal()
200 .setOnes();
202 Scalar(-1.) *
203 state->get_pinocchio()->effortLimit.segment(nv_f, nu_ - n_thrusters_);
205 state->get_pinocchio()->effortLimit.segment(nv_f, nu_ - n_thrusters_);
206 }
207 // Update the floating base actuation part
209 }
211
220 virtual void calc(const std::shared_ptr<Data>& data,
221 const Eigen::Ref<const VectorXs>&,
222 const Eigen::Ref<const VectorXs>& u) override {
223 if (static_cast<std::size_t>(u.size()) != nu_) {
224 throw_pretty(
225 "Invalid argument: " << "u has wrong dimension (it should be " +
226 std::to_string(nu_) + ")");
227 }
228 if (update_data_) {
229 updateData(data);
230 }
231 data->tau.noalias() = data->dtau_du * u;
232 }
233
242#ifndef NDEBUG
243 virtual void calcDiff(const std::shared_ptr<Data>& data,
244 const Eigen::Ref<const VectorXs>&,
245 const Eigen::Ref<const VectorXs>&) override {
246#else
247 virtual void calcDiff(const std::shared_ptr<Data>&,
248 const Eigen::Ref<const VectorXs>&,
249 const Eigen::Ref<const VectorXs>&) override {
250#endif
251 // The derivatives has constant values which were set in createData.
252 assert_pretty(MatrixXs(data->dtau_du).isApprox(W_thrust_),
253 "dtau_du has wrong value");
254 }
255
256 virtual void commands(const std::shared_ptr<Data>& data,
257 const Eigen::Ref<const VectorXs>&,
258 const Eigen::Ref<const VectorXs>& tau) override {
259 data->u.noalias() = data->Mtau * tau;
260 }
261
262#ifndef NDEBUG
263 virtual void torqueTransform(const std::shared_ptr<Data>& data,
264 const Eigen::Ref<const VectorXs>&,
265 const Eigen::Ref<const VectorXs>&) override {
266#else
267 virtual void torqueTransform(const std::shared_ptr<Data>&,
268 const Eigen::Ref<const VectorXs>&,
269 const Eigen::Ref<const VectorXs>&) override {
270#endif
271 // The torque transform has constant values which were set in createData.
272 assert_pretty(MatrixXs(data->Mtau).isApprox(Mtau_), "Mtau has wrong value");
273 }
274
280 virtual std::shared_ptr<Data> createData() override {
281 std::shared_ptr<Data> data =
282 std::allocate_shared<Data>(Eigen::aligned_allocator<Data>(), this);
283 updateData(data);
284 return data;
285 }
286
287 template <typename NewScalar>
290 typedef StateMultibodyTpl<NewScalar> StateType;
291 typedef ThrusterTpl<NewScalar> ThrusterType;
292 std::vector<ThrusterType> thrusters = vector_cast<NewScalar>(thrusters_);
293 ReturnType ret(
294 std::static_pointer_cast<StateType>(state_->template cast<NewScalar>()),
295 thrusters);
296 return ret;
297 }
298
302 const std::vector<Thruster>& get_thrusters() const { return thrusters_; }
303
307 std::size_t get_nthrusters() const { return n_thrusters_; }
308
317 void set_thrusters(const std::vector<Thruster>& thrusters) {
318 if (static_cast<std::size_t>(thrusters.size()) != n_thrusters_) {
319 throw_pretty("Invalid argument: "
320 << "the number of thrusters is wrong (it should be " +
321 std::to_string(n_thrusters_) + ")");
322 }
323 thrusters_ = thrusters;
324 // Update the mapping matrix from thrusters thrust to body force/moments
325 for (std::size_t i = 0; i < n_thrusters_; ++i) {
326 const Thruster& p = thrusters_[i];
327 const Vector3s& f_z = p.pose.rotation() * Vector3s::UnitZ();
328 W_thrust_.template topRows<3>().col(i) += f_z;
329 W_thrust_.template middleRows<3>(3).col(i).noalias() +=
330 p.pose.translation().cross(f_z);
331 switch (p.type) {
332 case CW:
333 W_thrust_.template middleRows<3>(3).col(i) += p.ctorque * f_z;
334 break;
335 case CCW:
336 W_thrust_.template middleRows<3>(3).col(i) -= p.ctorque * f_z;
337 break;
338 }
341 }
342 // Compute the torque transform matrix from generalized torques to joint
343 // torque inputs
344 Mtau_ = pseudoInverse(W_thrust_);
345 S_.noalias() = W_thrust_ * Mtau_;
346 update_data_ = true;
347 }
348
349 const MatrixXs& get_Wthrust() const { return W_thrust_; }
350
351 const MatrixXs& get_S() const { return S_; }
352
353 void print(std::ostream& os) const override {
354 os << "ActuationModelFloatingBaseThrusters {nu=" << nu_
355 << ", nthrusters=" << n_thrusters_ << ", thrusters=" << std::endl;
356 for (std::size_t i = 0; i < n_thrusters_; ++i) {
357 os << std::to_string(i) << ": " << thrusters_[i];
358 }
359 os << "}";
360 }
361
362 protected:
363 std::vector<Thruster> thrusters_;
364 std::size_t n_thrusters_;
365 MatrixXs W_thrust_;
366 MatrixXs Mtau_;
368 MatrixXs S_;
369
370 bool update_data_;
371 using Base::nu_;
372 using Base::state_;
373
374 private:
375 void updateData(const std::shared_ptr<Data>& data) {
376 data->dtau_du = W_thrust_;
377 data->Mtau = Mtau_;
378 const std::size_t nv = state_->get_nv();
379 for (std::size_t k = 0; k < nv; ++k) {
380 data->tau_set[k] = if_static(S_(k, k));
381 }
382 update_data_ = false;
383 }
384
385 // Use for floating-point types
386 template <typename Scalar>
387 typename std::enable_if<std::is_floating_point<Scalar>::value, bool>::type
388 if_static(const Scalar& condition) {
389 return (fabs(condition) < std::numeric_limits<Scalar>::epsilon()) ? false
390 : true;
391 }
392
393#ifdef CROCODDYL_WITH_CODEGEN
394 // Use for CppAD types
395 template <typename Scalar>
396 typename std::enable_if<!std::is_floating_point<Scalar>::value, bool>::type
397 if_static(const Scalar& condition) {
398 return CppAD::Value(CppAD::fabs(condition)) >=
399 CppAD::numeric_limits<Scalar>::epsilon();
400 }
401#endif
402};
403
404} // namespace crocoddyl
405
406CROCODDYL_DECLARE_EXTERN_TEMPLATE_STRUCT(crocoddyl::ThrusterTpl)
407CROCODDYL_DECLARE_EXTERN_TEMPLATE_CLASS(
408 crocoddyl::ActuationModelFloatingBaseThrustersTpl)
409
410#endif // CROCODDYL_MULTIBODY_ACTUATIONS_PROPELLERS_HPP_
ActuationModelAbstractTpl(std::shared_ptr< StateAbstract > state, const std::size_t nu)
Initialize the actuation model.
Actuation models for floating base systems actuated with thrusters.
virtual void calc(const std::shared_ptr< Data > &data, const Eigen::Ref< const VectorXs > &, const Eigen::Ref< const VectorXs > &u) override
Compute the actuation signal and actuation set from its thrust and joint torque inputs .
virtual std::shared_ptr< Data > createData() override
Create the floating base thruster actuation data.
ActuationModelFloatingBaseThrustersTpl(std::shared_ptr< StateMultibody > state, const std::vector< Thruster > &thrusters)
Initialize the floating base actuation model equipped with thrusters.
virtual void commands(const std::shared_ptr< Data > &data, const Eigen::Ref< const VectorXs > &, const Eigen::Ref< const VectorXs > &tau) override
Compute the joint torque input from the generalized torques.
virtual void torqueTransform(const std::shared_ptr< Data > &data, const Eigen::Ref< const VectorXs > &, const Eigen::Ref< const VectorXs > &) override
Compute the torque transform from generalized torques to joint torque inputs.
std::size_t get_nthrusters() const
Return the number of thrusters.
virtual void calcDiff(const std::shared_ptr< Data > &data, const Eigen::Ref< const VectorXs > &, const Eigen::Ref< const VectorXs > &) override
Compute the Jacobians of the floating base thruster actuation function.
void set_thrusters(const std::vector< Thruster > &thrusters)
Modify the vector of thrusters.
void print(std::ostream &os) const override
Print relevant information of the residual model.
const std::vector< Thruster > & get_thrusters() const
Return the vector of thrusters.
State multibody representation.
Definition multibody.hpp:34
ThrusterTpl(const SE3 &pose, const Scalar ctorque, const ThrusterType type=CW, const Scalar min_thrust=Scalar(0.), const Scalar max_thrust=std::numeric_limits< Scalar >::infinity())
Initialize the thruster in a give pose from the root joint.
ThrusterTpl(const Scalar ctorque, const ThrusterType type=CW, const Scalar min_thrust=Scalar(0.), const Scalar max_thrust=std::numeric_limits< Scalar >::infinity())
Initialize the thruster in a pose in the origin of the root joint.