MoveG 1.0.0
A modern C++ library for Robotics
Loading...
Searching...
No Matches
rotation_lib.cpp
Go to the documentation of this file.
1
9#include "rotation_lib.h"
10
11namespace MoveG
12{
13
14// Implementation of the Rotation class methods
15
16// Default constructor (identity rotation)
17Rotation::Rotation() : q(Eigen::Quaterniond::Identity())
18{
19}
20
21// Copy constructor
22Rotation::Rotation(const Rotation &other) : q(other.q)
23{
24}
25
26// Move constructor
27Rotation::Rotation(Rotation &&other) noexcept : q(std::move(other.q))
28{
29}
30
31// Constructor from rotation matrix
32Rotation::Rotation(const Eigen::Matrix3d &rotation_matrix) : q(Eigen::Quaterniond(rotation_matrix))
33{
34}
35
36// Constructor from quaternion
37Rotation::Rotation(const Eigen::Quaterniond &quaternion) : q(quaternion.normalized())
38{
39}
40
41// Constructor from axis-angle
42Rotation::Rotation(const Eigen::AngleAxisd &angle_axis) : q(Eigen::Quaterniond(angle_axis))
43{
44}
45
46// Constructor from Euler angles using axis-angle representations
47Rotation::Rotation(double angle1,
48 double angle2,
49 double angle3,
50 bool intrinsic,
51 const std::string &sequence,
52 bool degree)
53 : q(Eigen::Quaterniond::Identity())
54{
55 checkSequence(sequence);
56
57 if (degree)
58 {
59 angle1 = deg2rad(angle1);
60 angle2 = deg2rad(angle2);
61 angle3 = deg2rad(angle3);
62 }
63
64 const Eigen::AngleAxisd aa1 = angleAxis(sequence[0], angle1);
65 const Eigen::AngleAxisd aa2 = angleAxis(sequence[1], angle2);
66 const Eigen::AngleAxisd aa3 = angleAxis(sequence[2], angle3);
67
68 if (intrinsic)
69 {
70 // For intrinsic rotations, multiply in the order q3 * q2 * q1
71 q = Eigen::Quaterniond(aa3) * Eigen::Quaterniond(aa2) * Eigen::Quaterniond(aa1);
72 }
73 else
74 {
75 // For extrinsic rotations, multiply in the order q1 * q2 * q3
76 q = Eigen::Quaterniond(aa1) * Eigen::Quaterniond(aa2) * Eigen::Quaterniond(aa3);
77 }
78
79 q.normalize();
80}
81
82// Static methods
83Rotation Rotation::fromRotationMatrix(const Eigen::Matrix3d &rotation_matrix)
84{
85 return Rotation(rotation_matrix);
86}
87
88Rotation Rotation::fromQuaternion(const Eigen::Quaterniond &quaternion)
89{
90 return Rotation(quaternion.normalized());
91}
92
93Rotation Rotation::fromAngleAxis(const Eigen::AngleAxisd &angle_axis)
94{
95 return Rotation(angle_axis);
96}
97
99 double angle2,
100 double angle3,
101 bool intrinsic,
102 const std::string &sequence,
103 bool degree)
104{
105 return Rotation(angle1, angle2, angle3, intrinsic, sequence, degree);
106}
107
108// Converter to rotation matrix
109Eigen::Matrix3d Rotation::toRotationMatrix() const
110{
111 return q.toRotationMatrix();
112}
113
114// Converter to quaternion
115Eigen::Quaterniond Rotation::toQuaternion() const
116{
117 return q;
118}
119
120// Converter to axis-angle
121Eigen::AngleAxisd Rotation::toAngleAxis() const
122{
123 return Eigen::AngleAxisd(q);
124}
125
126// Converter to Euler angles
127Eigen::Vector3d Rotation::toEulerAngles(bool intrinsic, const std::string &sequence) const
128{
129 checkSequence(sequence);
130
131 const int a0 = axisToIndex(sequence[0]);
132 const int a1 = axisToIndex(sequence[1]);
133 const int a2 = axisToIndex(sequence[2]);
134
135 Eigen::Vector3d angles = Eigen::Vector3d::Zero();
136
137 if (intrinsic)
138 {
139 // For intrinsic rotations, reverse the order of axes
140 angles = q.toRotationMatrix().eulerAngles(a2, a1, a0).reverse();
141 }
142 else
143 {
144 // For extrinsic rotations, use the axes as they are
145 angles = q.toRotationMatrix().eulerAngles(a0, a1, a2);
146 }
147
148 return angles;
149}
150
151// Overloading the * operator (composition of rotations)
153{
154 return Rotation(q * other.q);
155}
156
158{
159 if (this != &other)
160 {
161 q = other.q;
162 }
163 return *this;
164}
165
167{
168 q = std::move(other.q);
169 return *this;
170}
171
172// Implementation of the output stream operator
173std::ostream &operator<<(std::ostream &os, const Rotation &rotation)
174{
175 Eigen::Quaterniond quat = rotation.toQuaternion();
176 os << "Rotation (quaternion x,y,z,w): [" << quat.x() << ", " << quat.y() << ", " << quat.z()
177 << ", " << quat.w() << "]";
178 return os;
179}
180
181// Angle normalization methods
182double Rotation::normalizeAngle(double angle)
183{
184 // Use fmod to get the remainder of division by 2π
185 angle = std::fmod(angle, 2 * M_PI);
186
187 // Adjust to the range [-π, π]
188 if (angle > M_PI)
189 {
190 angle -= 2 * M_PI;
191 }
192 else if (angle < -M_PI)
193 {
194 angle += 2 * M_PI;
195 }
196
197 return angle;
198}
199
200// Normalize angle to a specified range [minVal, maxVal]
201double Rotation::normalizeAngleRange(double angle, double minVal, double maxVal)
202{
203 const double width = maxVal - minVal;
204
205 // Validate range width
206 if (width <= 0)
207 {
208 throw std::invalid_argument("Invalid range: maxVal must be greater than minVal");
209 }
210
211 // Get a value in range [0, width) using modulo
212 const double offsetAngle = std::fmod(angle - minVal, width);
213
214 // Handle negative angles
215 if (offsetAngle < 0)
216 {
217 return offsetAngle + maxVal;
218 }
219 else
220 {
221 return offsetAngle + minVal;
222 }
223}
224
225// Normalize Euler angles
226Eigen::Vector3d Rotation::normalizeEulerAngles(const Eigen::Vector3d &angles)
227{
228 return Eigen::Vector3d(normalizeAngle(angles.x()),
229 normalizeAngle(angles.y()),
230 normalizeAngle(angles.z()));
231}
232
233// Auxiliary function to create an axis-angle representation
234Eigen::AngleAxisd Rotation::angleAxis(char axis, double angle)
235{
236 switch (axis)
237 {
238 case 'X':
239 case 'x':
240 return Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitX());
241 case 'Y':
242 case 'y':
243 return Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitY());
244 case 'Z':
245 case 'z':
246 return Eigen::AngleAxisd(angle, Eigen::Vector3d::UnitZ());
247 default:
248 throw std::invalid_argument("Invalid axis. Valid axes are 'X', 'Y', or 'Z'.");
249 }
250}
251
252// Auxiliary function to convert axis to index
254{
255 switch (axis)
256 {
257 case 'X':
258 case 'x':
259 return 0;
260 case 'Y':
261 case 'y':
262 return 1;
263 case 'Z':
264 case 'z':
265 return 2;
266 default:
267 throw std::invalid_argument("Invalid axis. Valid axes are 'X', 'Y', or 'Z'.");
268 }
269}
270
271// Auxiliary function to check the Euler angle sequence
272int Rotation::checkSequence(const std::string &sequence)
273{
274 if (sequence.length() != 3 || sequence.find_first_not_of("XYZxyz") != std::string::npos)
275 {
276 throw std::invalid_argument("The axis sequence must be a string of three characters "
277 "from 'X', 'Y', and 'Z' or 'x', 'y', and 'z'.");
278 }
279 return 1;
280}
281
282// Conversion from degrees to radians
283double Rotation::deg2rad(double degree)
284{
285 return degree * M_PI / 180.0;
286}
287
288// Conversion from radians to degrees
289double Rotation::rad2deg(double radian)
290{
291 return radian * 180.0 / M_PI;
292}
293
294Eigen::Quaterniond Rotation::quaternion_difference(const Eigen::Quaterniond &a,
295 const Eigen::Quaterniond &b)
296{
297 return a.inverse() * b;
298}
299
300Eigen::Quaterniond Rotation::quaternion_negation(const Eigen::Quaterniond &v)
301{
302 return Eigen::Quaterniond(-v.w(), -v.x(), -v.y(), -v.z());
303}
304
305Eigen::Quaterniond Rotation::scalar_product(const Eigen::Quaterniond &v, double t)
306{
307 return Eigen::Quaterniond(t * v.w(), t * v.x(), t * v.y(), t * v.z());
308}
309
310Eigen::Quaterniond Rotation::quaternion_minus(const Eigen::Quaterniond &v1,
311 const Eigen::Quaterniond &v0)
312{
313 return Eigen::Quaterniond(v1.w() - v0.w(), v1.x() - v0.x(), v1.y() - v0.y(), v1.z() - v0.z());
314}
315
316Eigen::Quaterniond Rotation::quaternion_plus(const Eigen::Quaterniond &v1,
317 const Eigen::Quaterniond &v0)
318{
319 return Eigen::Quaterniond(v1.w() + v0.w(), v1.x() + v0.x(), v1.y() + v0.y(), v1.z() + v0.z());
320}
321
322// Elementary rotation matrices
323Eigen::Matrix3d Rotation::rotationX(double angle)
324{
325 Eigen::Matrix3d rot = Eigen::Matrix3d::Identity();
326 rot << 1, 0, 0, 0, cos(angle), -sin(angle), 0, sin(angle), cos(angle);
327 return rot;
328}
329
330Eigen::Matrix3d Rotation::rotationY(double angle)
331{
332 Eigen::Matrix3d rot = Eigen::Matrix3d::Identity();
333 rot << cos(angle), 0, sin(angle), 0, 1, 0, -sin(angle), 0, cos(angle);
334 return rot;
335}
336
337Eigen::Matrix3d Rotation::rotationZ(double angle)
338{
339 Eigen::Matrix3d rot = Eigen::Matrix3d::Identity();
340 rot << cos(angle), -sin(angle), 0, sin(angle), cos(angle), 0, 0, 0, 1;
341 return rot;
342}
343
344// Matrix S
345Eigen::Matrix3d Rotation::matrixS(const Eigen::Vector3d &omega)
346{
347 Eigen::Matrix3d S = Eigen::Matrix3d::Zero();
348 S << 0, -omega.z(), omega.y(), omega.z(), 0, -omega.x(), -omega.y(), omega.x(), 0;
349 return S;
350}
351
352// Matrix R_dot
353Eigen::Matrix3d Rotation::matrixR_dot(const Eigen::Matrix3d &R, const Eigen::Matrix3d &S)
354{
355 return S * R;
356}
357
358Eigen::Matrix3d Rotation::matrixR_dot(const Eigen::Matrix3d &R, const Eigen::Vector3d &omega)
359{
360 const Eigen::Matrix3d S = matrixS(omega);
361 return matrixR_dot(R, S);
362}
363
364Eigen::Matrix3d Rotation::matrixT(const Eigen::Vector3d &angles, const std::string &sequence)
365{
366 checkSequence(sequence);
367
368 Eigen::Matrix3d T = Eigen::Matrix3d::Zero();
369
370 std::array<Eigen::Vector3d, 3> theta;
371
372 for (std::size_t i = 0; i < 3; ++i)
373 {
374 const char c = sequence[i];
375 switch (c)
376 {
377 case 'X':
378 case 'x':
379 theta[i] = Eigen::Vector3d::UnitX();
380 break;
381 case 'Y':
382 case 'y':
383 theta[i] = Eigen::Vector3d::UnitY();
384 break;
385 case 'Z':
386 case 'z':
387 theta[i] = Eigen::Vector3d::UnitZ();
388 break;
389 default:
390 break;
391 }
392 }
393
394 T.col(0) = theta[0];
395
396 Eigen::Matrix3d R = Eigen::Matrix3d::Identity();
397
398 for (std::size_t i = 0; i < 2; ++i)
399 {
400 Eigen::Matrix3d Ri;
401
402 const char c = sequence[i];
403 switch (c)
404 {
405 case 'X':
406 case 'x':
407 Ri = Rotation::rotationX(angles[static_cast<Eigen::Index>(i)]);
408 break;
409 case 'Y':
410 case 'y':
411 Ri = Rotation::rotationY(angles[static_cast<Eigen::Index>(i)]);
412 break;
413 case 'Z':
414 case 'z':
415 Ri = Rotation::rotationZ(angles[static_cast<Eigen::Index>(i)]);
416 break;
417 default:
418 Ri = Eigen::Matrix3d::Identity();
419 break;
420 }
421
422 R *= Ri;
423 T.col(static_cast<Eigen::Index>(i + 1)) = R * theta[i + 1];
424 }
425
426 // Check if it is singular and so the determinant is near zero
427 if (std::abs(T.determinant()) < 1e-6)
428 {
429 std::cerr << "WARNING: The matrix is singular\n";
430 throw std::runtime_error("Matrix T is singular, which may cause numerical instability.");
431 }
432
433 return T;
434}
435
436} // namespace MoveG
Class for representing and managing rotations in three-dimensional space.
Rotation operator*(const Rotation &other) const
Composition of two rotations.
Eigen::Quaterniond q
Internal representation of rotation via quaternion.
static int axisToIndex(char axis)
Converts an axis character to index.
static Eigen::Vector3d normalizeEulerAngles(const Eigen::Vector3d &angles)
Normalizes Euler angles to the range [-π, π].
static Rotation fromRotationMatrix(const Eigen::Matrix3d &rotation_matrix)
Creates a rotation from a rotation matrix.
Rotation()
Default constructor.
static double normalizeAngle(double angle)
Normalizes an angle to the range [-π, π].
static Eigen::Matrix3d matrixT(const Eigen::Vector3d &angles, const std::string &sequence)
Calculates the T matrix.
Eigen::Quaterniond toQuaternion() const
Converts the rotation to a quaternion.
Eigen::Matrix3d toRotationMatrix() const
Converts the rotation to a rotation matrix.
static Rotation fromAngleAxis(const Eigen::AngleAxisd &angle_axis)
Creates a rotation from an axis-angle representation.
static Rotation fromEulerAngles(double angle1, double angle2, double angle3, bool intrinsic=true, const std::string &sequence="XYZ", bool degree=false)
Creates a rotation from Euler angles.
Eigen::AngleAxisd toAngleAxis() const
Converts the rotation to an axis-angle representation.
static Rotation fromQuaternion(const Eigen::Quaterniond &quaternion)
Creates a rotation from a quaternion.
static Eigen::Matrix3d rotationY(double angle)
Rotation matrix for a rotation around the Y axis.
static Eigen::Quaterniond quaternion_difference(const Eigen::Quaterniond &a, const Eigen::Quaterniond &b)
Calculates the difference between two quaternions.
Rotation & operator=(const Rotation &other)
Assignment operator.
static double deg2rad(double degree)
Converts degrees to radians.
static double rad2deg(double radian)
Converts radians to degrees.
static Eigen::Quaterniond quaternion_negation(const Eigen::Quaterniond &v)
Negates the quaternion.
static Eigen::Matrix3d matrixS(const Eigen::Vector3d &omega)
Calculates the S matrix.
static Eigen::Quaterniond quaternion_minus(const Eigen::Quaterniond &v1, const Eigen::Quaterniond &v0)
Calculates the difference between two quaternions (v1 - v0).
static double normalizeAngleRange(double angle, double minVal, double maxVal)
Normalizes an angle to a specified range.
static Eigen::Quaterniond scalar_product(const Eigen::Quaterniond &v, double t)
Calculates the scalar product of a quaternion by a scalar.
static int checkSequence(const std::string &sequence)
Checks the validity of the Euler angle sequence.
static Eigen::Quaterniond quaternion_plus(const Eigen::Quaterniond &v1, const Eigen::Quaterniond &v0)
Calculates the sum of two quaternions.
static Eigen::Matrix3d rotationZ(double angle)
Rotation matrix for a rotation around the Z axis.
static Eigen::Matrix3d rotationX(double angle)
Rotation matrix for a rotation around the X axis.
static Eigen::Matrix3d matrixR_dot(const Eigen::Matrix3d &R, const Eigen::Matrix3d &S)
Calculates the R_dot matrix.
Eigen::Vector3d toEulerAngles(bool intrinsic=true, const std::string &sequence="ZYX") const
Converts the rotation to Euler angles.
static Eigen::AngleAxisd angleAxis(char axis, double angle)
Creates an axis-angle representation.
Namespace for movement and manipulation of poses.
Definition pose_lib.cpp:12
std::ostream & operator<<(std::ostream &os, const Pose &pose)
Definition pose_lib.cpp:281
Class for representing and manipulating 3D rotations.