Directory: | ./ |
---|---|
File: | include/ndcurves/se3_curve.h |
Date: | 2025-03-05 17:18:30 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 113 | 124 | 91.1% |
Branches: | 86 | 172 | 50.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | #ifndef _STRUCT_SE3_CURVE_H | ||
2 | #define _STRUCT_SE3_CURVE_H | ||
3 | |||
4 | #include <Eigen/Dense> | ||
5 | #include <boost/math/constants/constants.hpp> | ||
6 | |||
7 | #include "MathDefs.h" | ||
8 | #include "curve_abc.h" | ||
9 | #include "polynomial.h" | ||
10 | #include "so3_linear.h" | ||
11 | |||
12 | namespace ndcurves { | ||
13 | |||
14 | /// \class SE3Curve. | ||
15 | /// \brief Composition of a curve of any type of dimension 3 and a curve | ||
16 | /// representing an rotation (in current implementation, only SO3Linear can be | ||
17 | /// used for the rotation part) The output is a vector of size 7 | ||
18 | /// (pos_x,pos_y,pos_z,quat_x,quat_y,quat_z,quat_w) The output of the derivative | ||
19 | /// of any order is a vector of size 6 | ||
20 | /// (linear_x,linear_y,linear_z,angular_x,angular_y,angular_z) | ||
21 | /// | ||
22 | /// | ||
23 | template <typename Time = double, typename Numeric = Time, bool Safe = false> | ||
24 | struct SE3Curve : public curve_abc<Time, Numeric, Safe, | ||
25 | Eigen::Transform<Numeric, 3, Eigen::Affine>, | ||
26 | Eigen::Matrix<Numeric, 6, 1> > { | ||
27 | typedef Numeric Scalar; | ||
28 | typedef Eigen::Transform<Numeric, 3, Eigen::Affine> transform_t; | ||
29 | typedef transform_t point_t; | ||
30 | typedef Eigen::Matrix<Scalar, 6, 1> point_derivate_t; | ||
31 | typedef Eigen::Quaternion<Scalar> Quaternion; | ||
32 | typedef Time time_t; | ||
33 | typedef curve_abc<Time, Numeric, Safe, point_t, point_derivate_t> | ||
34 | curve_abc_t; // parent class | ||
35 | typedef polynomial<Time, Numeric, Safe, point_derivate_t> curve_derivate_t; | ||
36 | typedef curve_abc<Time, Numeric, Safe, pointX_t> | ||
37 | curve_X_t; // generic class of curve | ||
38 | typedef curve_abc<Time, Numeric, Safe, point3_t, point3_t> | ||
39 | curve_translation_t; // templated class used for the translation (return | ||
40 | // dimension are fixed) | ||
41 | typedef curve_abc<Time, Numeric, Safe, matrix3_t, point3_t> | ||
42 | curve_rotation_t; // templated class used for the rotation (return | ||
43 | // dimension are fixed) | ||
44 | typedef std::shared_ptr<curve_X_t> curve_ptr_t; | ||
45 | typedef std::shared_ptr<curve_rotation_t> curve_rotation_ptr_t; | ||
46 | typedef std::shared_ptr<curve_translation_t> curve_translation_ptr_t; | ||
47 | |||
48 | typedef SO3Linear<Time, Numeric, Safe> SO3Linear_t; | ||
49 | typedef polynomial<Time, Numeric, Safe, pointX_t> polynomial_t; | ||
50 | typedef SE3Curve<Time, Numeric, Safe> SE3Curve_t; | ||
51 | |||
52 | public: | ||
53 | /* Constructors - destructors */ | ||
54 | /// \brief Empty constructor. Curve obtained this way can not perform other | ||
55 | /// class functions. | ||
56 | /// | ||
57 | 24 | SE3Curve() | |
58 | : curve_abc_t(), | ||
59 | 24 | dim_(3), | |
60 | 24 | translation_curve_(), | |
61 | 24 | rotation_curve_(), | |
62 | 24 | T_min_(0), | |
63 | 24 | T_max_(0) {} | |
64 | |||
65 | /// \brief Destructor | ||
66 | 198 | virtual ~SE3Curve() { | |
67 | // should we delete translation_curve and rotation_curve here ? | ||
68 | // better switch to shared ptr | ||
69 | 198 | } | |
70 | |||
71 | /* Constructor without curve object for the translation : */ | ||
72 | /// \brief Constructor from init/end transform use polynomial of degree 1 for | ||
73 | /// position and SO3Linear for rotation | ||
74 | 11 | SE3Curve(const transform_t& init_transform, const transform_t& end_transform, | |
75 | const time_t& t_min, const time_t& t_max) | ||
76 | : curve_abc_t(), | ||
77 | 11 | dim_(6), | |
78 |
3/4✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
|
11 | translation_curve_(new polynomial3_t( |
79 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
12 | point3_t(init_transform.translation()), |
80 |
3/6✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
|
12 | point3_t(end_transform.translation()), t_min, t_max)), |
81 |
5/10✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
|
10 | rotation_curve_(new SO3Linear_t( |
82 | init_transform.rotation(), end_transform.rotation(), t_min, t_max)), | ||
83 | 10 | T_min_(t_min), | |
84 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | T_max_(t_max) { |
85 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | safe_check(); |
86 | 11 | } | |
87 | |||
88 | /// \brief Constructor from init/end pose, with quaternion. use polynomial of | ||
89 | /// degree 1 for position and SO3Linear for rotation | ||
90 | 2 | SE3Curve(const point3_t& init_pos, const point3_t& end_pos, | |
91 | const Quaternion& init_rot, const Quaternion& end_rot, | ||
92 | const time_t& t_min, const time_t& t_max) | ||
93 | : curve_abc_t(), | ||
94 | 2 | dim_(6), | |
95 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | translation_curve_(new polynomial3_t(init_pos, end_pos, t_min, t_max)), |
96 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
2 | rotation_curve_(new SO3Linear_t(init_rot, end_rot, t_min, t_max)), |
97 | 2 | T_min_(t_min), | |
98 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | T_max_(t_max) { |
99 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | safe_check(); |
100 | 2 | } | |
101 | |||
102 | /// \brief Constructor from init/end pose, with rotation matrix. use | ||
103 | /// polynomial of degree 1 for position and SO3Linear for rotation | ||
104 | 1 | SE3Curve(const point3_t& init_pos, const point3_t& end_pos, | |
105 | const matrix3_t& init_rot, const matrix3_t& end_rot, | ||
106 | const time_t& t_min, const time_t& t_max) | ||
107 | : curve_abc_t(), | ||
108 | 1 | dim_(6), | |
109 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | translation_curve_(new polynomial3_t(init_pos, end_pos, t_min, t_max)), |
110 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | rotation_curve_(new SO3Linear_t(init_rot, end_rot, t_min, t_max)), |
111 | 1 | T_min_(t_min), | |
112 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | T_max_(t_max) { |
113 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | safe_check(); |
114 | 1 | } | |
115 | |||
116 | /* Constructor with curve object for the translation : */ | ||
117 | /// \brief Constructor from curve for the translation and init/end rotation, | ||
118 | /// with quaternion. Use SO3Linear for rotation with the same time bounds as | ||
119 | /// the | ||
120 | 1 | SE3Curve(curve_translation_ptr_t translation_curve, | |
121 | const Quaternion& init_rot, const Quaternion& end_rot) | ||
122 | : curve_abc_t(), | ||
123 | 1 | dim_(6), | |
124 | 1 | translation_curve_(translation_curve), | |
125 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | rotation_curve_(new SO3Linear_t(init_rot, end_rot, |
126 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | translation_curve->min(), |
127 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | translation_curve->max())), |
128 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | T_min_(translation_curve->min()), |
129 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | T_max_(translation_curve->max()) { |
130 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | safe_check(); |
131 | 1 | } | |
132 | /// \brief Constructor from curve for the translation and init/end rotation, | ||
133 | /// with rotation matrix. Use SO3Linear for rotation with the same time bounds | ||
134 | /// as the | ||
135 | 16 | SE3Curve(curve_translation_ptr_t translation_curve, const matrix3_t& init_rot, | |
136 | const matrix3_t& end_rot) | ||
137 | : curve_abc_t(), | ||
138 | 16 | dim_(6), | |
139 | 16 | translation_curve_(translation_curve), | |
140 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
16 | rotation_curve_(new SO3Linear_t(init_rot, end_rot, |
141 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | translation_curve->min(), |
142 |
2/4✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
|
16 | translation_curve->max())), |
143 |
1/2✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
16 | T_min_(translation_curve->min()), |
144 |
1/2✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
32 | T_max_(translation_curve->max()) { |
145 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | safe_check(); |
146 | 16 | } | |
147 | |||
148 | /* Constructor from translation and rotation curves object : */ | ||
149 | /// \brief Constructor from from translation and rotation curves object | ||
150 | 6 | SE3Curve(curve_translation_ptr_t translation_curve, | |
151 | curve_rotation_ptr_t rotation_curve) | ||
152 | : curve_abc_t(), | ||
153 | 6 | dim_(6), | |
154 | 6 | translation_curve_(translation_curve), | |
155 | 6 | rotation_curve_(rotation_curve), | |
156 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | T_min_(translation_curve->min()), |
157 |
1/2✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
12 | T_max_(translation_curve->max()) { |
158 |
2/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
|
6 | if (translation_curve->dim() != 3) { |
159 | ✗ | throw std::invalid_argument( | |
160 | "The translation curve should be of dimension 3."); | ||
161 | } | ||
162 |
3/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
|
6 | if (rotation_curve->min() != T_min_) { |
163 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | throw std::invalid_argument( |
164 | "Min bounds of translation and rotation curve are not the same."); | ||
165 | } | ||
166 |
3/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
|
4 | if (rotation_curve->max() != T_max_) { |
167 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | throw std::invalid_argument( |
168 | "Max bounds of translation and rotation curve are not the same."); | ||
169 | } | ||
170 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | safe_check(); |
171 | 14 | } | |
172 | |||
173 | /// \brief Evaluation of the SE3Curve at time t | ||
174 | /// \param t : time when to evaluate the spline. | ||
175 | /// \return \f$x(t)\f$ point corresponding on spline at time t. | ||
176 | /// (pos_x,pos_y,pos_z,quat_x,quat_y,quat_z,quat_w) | ||
177 | 8648 | virtual point_t operator()(const time_t t) const { | |
178 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 8648 times.
|
8648 | if (translation_curve_->dim() != 3) { |
179 | ✗ | throw std::invalid_argument( | |
180 | "Translation curve should always be of dimension 3"); | ||
181 | } | ||
182 | 8648 | point_t res = point_t::Identity(); | |
183 |
1/2✓ Branch 3 taken 8640 times.
✗ Branch 4 not taken.
|
8648 | res.translate(point3_t((*translation_curve_)(t))); |
184 |
1/2✓ Branch 3 taken 8640 times.
✗ Branch 4 not taken.
|
8640 | res.rotate((*rotation_curve_)(t)); |
185 | 8640 | return res; | |
186 | } | ||
187 | |||
188 | /** | ||
189 | * @brief isApprox check if other and *this are approximately equals. | ||
190 | * Only two curves of the same class can be approximately equals, for | ||
191 | * comparison between different type of curves see isEquivalent | ||
192 | * @param other the other curve to check | ||
193 | * @param prec the precision threshold, default | ||
194 | * Eigen::NumTraits<Numeric>::dummy_precision() | ||
195 | * @return true is the two curves are approximately equals | ||
196 | */ | ||
197 | 18 | bool isApprox( | |
198 | const SE3Curve_t& other, | ||
199 | const Numeric prec = Eigen::NumTraits<Numeric>::dummy_precision()) const { | ||
200 |
1/2✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
|
36 | return ndcurves::isApprox<Numeric>(T_min_, other.min()) && |
201 |
2/2✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
|
36 | ndcurves::isApprox<Numeric>(T_max_, other.max()) && |
202 | 18 | (translation_curve_ == other.translation_curve_ || | |
203 |
2/2✓ Branch 3 taken 7 times.
✓ Branch 4 taken 5 times.
|
12 | translation_curve_->isApprox(other.translation_curve_.get(), |
204 |
3/4✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 5 times.
|
44 | prec)) && |
205 | 13 | (rotation_curve_ == other.rotation_curve_ || | |
206 |
2/2✓ Branch 3 taken 6 times.
✓ Branch 4 taken 2 times.
|
26 | rotation_curve_->isApprox(other.rotation_curve_.get(), prec)); |
207 | } | ||
208 | |||
209 | 7 | virtual bool isApprox( | |
210 | const curve_abc_t* other, | ||
211 | const Numeric prec = Eigen::NumTraits<Numeric>::dummy_precision()) const { | ||
212 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | const SE3Curve_t* other_cast = dynamic_cast<const SE3Curve_t*>(other); |
213 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (other_cast) |
214 | 7 | return isApprox(*other_cast, prec); | |
215 | else | ||
216 | ✗ | return false; | |
217 | } | ||
218 | |||
219 | 10 | virtual bool operator==(const SE3Curve_t& other) const { | |
220 | 10 | return isApprox(other); | |
221 | } | ||
222 | |||
223 | 4 | virtual bool operator!=(const SE3Curve_t& other) const { | |
224 | 4 | return !(*this == other); | |
225 | } | ||
226 | |||
227 | /// \brief Evaluation of the derivative of order N of spline at time t. | ||
228 | /// \param t : the time when to evaluate the spline. | ||
229 | /// \param order : order of derivative. | ||
230 | /// \return \f$\frac{d^Nx(t)}{dt^N}\f$ point corresponding on derivative | ||
231 | /// spline at time t. | ||
232 | 829 | virtual point_derivate_t derivate(const time_t t, | |
233 | const std::size_t order) const { | ||
234 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 829 times.
|
829 | if (translation_curve_->dim() != 3) { |
235 | ✗ | throw std::invalid_argument( | |
236 | "Translation curve should always be of dimension 3"); | ||
237 | } | ||
238 |
1/2✓ Branch 2 taken 829 times.
✗ Branch 3 not taken.
|
829 | point_derivate_t res = point_derivate_t::Zero(); |
239 |
2/4✓ Branch 3 taken 825 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 825 times.
✗ Branch 7 not taken.
|
829 | res.segment(0, 3) = point3_t(translation_curve_->derivate(t, order)); |
240 |
2/4✓ Branch 3 taken 822 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 822 times.
✗ Branch 7 not taken.
|
825 | res.segment(3, 3) = rotation_curve_->derivate(t, order); |
241 | 822 | return res; | |
242 | } | ||
243 | |||
244 | ✗ | curve_derivate_t compute_derivate(const std::size_t /*order*/) const { | |
245 | ✗ | throw std::logic_error("Compute derivate for SE3 is not implemented yet."); | |
246 | } | ||
247 | |||
248 | /// \brief Compute the derived curve at order N. | ||
249 | /// \param order : order of derivative. | ||
250 | /// \return A pointer to \f$\frac{d^Nx(t)}{dt^N}\f$ derivative order N of the | ||
251 | /// curve. | ||
252 | ✗ | curve_derivate_t* compute_derivate_ptr(const std::size_t order) const { | |
253 | ✗ | return new curve_derivate_t(compute_derivate(order)); | |
254 | } | ||
255 | |||
256 | /*Helpers*/ | ||
257 | /// \brief Get dimension of curve. | ||
258 | /// \return dimension of curve. | ||
259 | 22 | std::size_t virtual dim() const { return dim_; }; | |
260 | /// \brief Get the minimum time for which the curve is defined | ||
261 | /// \return \f$t_{min}\f$ lower bound of time range. | ||
262 | 96 | time_t min() const { return T_min_; } | |
263 | /// \brief Get the maximum time for which the curve is defined. | ||
264 | /// \return \f$t_{max}\f$ upper bound of time range. | ||
265 | 146 | time_t max() const { return T_max_; } | |
266 | /// \brief Get the degree of the curve. | ||
267 | /// \return \f$degree\f$, the degree of the curve. | ||
268 | ✗ | virtual std::size_t degree() const { return translation_curve_->degree(); } | |
269 | /// \brief const accessor to the translation curve | ||
270 | 4 | const curve_translation_ptr_t translation_curve() const { | |
271 | 4 | return translation_curve_; | |
272 | } | ||
273 | /// \brief const accessor to the rotation curve | ||
274 | 4 | const curve_rotation_ptr_t rotation_curve() const { return rotation_curve_; } | |
275 | /*Helpers*/ | ||
276 | |||
277 | /*Attributes*/ | ||
278 | std::size_t dim_; // dim doesn't mean anything in this class ... | ||
279 | curve_translation_ptr_t translation_curve_; | ||
280 | curve_rotation_ptr_t rotation_curve_; | ||
281 | time_t T_min_, T_max_; | ||
282 | /*Attributes*/ | ||
283 | |||
284 | // Serialization of the class | ||
285 | friend class boost::serialization::access; | ||
286 | |||
287 | template <class Archive> | ||
288 | 88 | void serialize(Archive& ar, const unsigned int version) { | |
289 | if (version) { | ||
290 | // Do something depending on version ? | ||
291 | } | ||
292 |
1/2✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
|
88 | ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(curve_abc_t); |
293 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | ar& boost::serialization::make_nvp("dim", dim_); |
294 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | ar& boost::serialization::make_nvp("translation_curve", translation_curve_); |
295 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | ar& boost::serialization::make_nvp("rotation_curve", rotation_curve_); |
296 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | ar& boost::serialization::make_nvp("T_min", T_min_); |
297 |
1/2✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
|
88 | ar& boost::serialization::make_nvp("T_max", T_max_); |
298 | 88 | } | |
299 | |||
300 | private: | ||
301 | 32 | void safe_check() { | |
302 | if (Safe) { | ||
303 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (T_min_ > T_max_) { |
304 | ✗ | throw std::invalid_argument("Tmin should be inferior to Tmax"); | |
305 | } | ||
306 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
|
32 | if (translation_curve_->dim() != 3) { |
307 | ✗ | throw std::invalid_argument( | |
308 | "Translation curve should always be of dimension 3"); | ||
309 | } | ||
310 | } | ||
311 | 32 | } | |
312 | |||
313 | }; // SE3Curve | ||
314 | |||
315 | } // namespace ndcurves | ||
316 | |||
317 | DEFINE_CLASS_TEMPLATE_VERSION( | ||
318 | SINGLE_ARG(typename Time, typename Numeric, bool Safe), | ||
319 | SINGLE_ARG(ndcurves::SE3Curve<Time, Numeric, Safe>)) | ||
320 | |||
321 | #endif // SE3_CURVE_H | ||
322 |