6static int g_quaternion_explicit_debug = 0;
10 double w = q[0], x = q[1], y = q[2], z = q[3];
12 double xx = x*x;
double yy = y*y;
double zz = z*z;
13 double xy = x*y;
double xz = x*z;
double yz = y*z;
14 double wx = w*x;
double wy = w*y;
double wz = w*z;
16 dcm[0][0] = 1.0 - 2.0*(yy + zz);
17 dcm[0][1] = 2.0*(xy - wz);
18 dcm[0][2] = 2.0*(xz + wy);
19 dcm[1][0] = 2.0*(xy + wz);
20 dcm[1][1] = 1.0 - 2.0*(xx + zz);
21 dcm[1][2] = 2.0*(yz - wx);
22 dcm[2][0] = 2.0*(xz - wy);
23 dcm[2][1] = 2.0*(yz + wx);
24 dcm[2][2] = 1.0 - 2.0*(xx + yy);
28 double w = q[0], x = q[1], y = q[2], z = q[3];
31 double norm = sqrt(w * w + x * x + y * y + z * z);
38 w /= norm; x /= norm; y /= norm; z /= norm;
41 double sinr_cosp = 2.0 * (w * x + y * z);
42 double cosr_cosp = 1.0 - 2.0 * (x * x + y * y);
43 *roll = atan2(sinr_cosp, cosr_cosp);
45 double sinp = 2.0 * (w * y - z * x);
46 if (fabs(sinp) >= 1.0) {
52 double siny_cosp = 2.0 * (w * z + x * y);
53 double cosy_cosp = 1.0 - 2.0 * (y * y + z * z);
54 *yaw = atan2(siny_cosp, cosy_cosp);
58 double norm = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
61 if (norm > 1e-6 && fabs(norm - 1.0) > 1e-6) {
69 if (fabs(norm - 1.0) > 1e-6) {
70 printf(
"Quaternion is not normalized: norm = %f\n", norm);
76 double w1 = q1[0], x1 = q1[1], y1 = q1[2], z1 = q1[3];
77 double w2 = q2[0], x2 = q2[1], y2 = q2[2], z2 = q2[3];
79 q_out[0] = w1*w2 - x1*x2 - y1*y2 - z1*z2;
80 q_out[1] = w1*x2 + x1*w2 + y1*z2 - z1*y2;
81 q_out[2] = w1*y2 - x1*z2 + y1*w2 + z1*x2;
82 q_out[3] = w1*z2 + x1*y2 - y1*x2 + z1*w2;
86 double norm_sq = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
87 if (norm_sq < 1e-14) {
91 q_inv[0] = q[0] / norm_sq;
92 q_inv[1] = -q[1] / norm_sq;
93 q_inv[2] = -q[2] / norm_sq;
94 q_inv[3] = -q[3] / norm_sq;
99 double q_current_inv[4];
108 if (q_error[0] < 0.0) {
109 for (
int i = 0; i < 4; ++i) {
110 q_error[i] = -q_error[i];
118 const double q_target[4],
131 double norm_vec = sqrt(q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
132 if (norm_vec < 1e-14) {
135 axis[0] = axis[1] = axis[2] = 0.0;
138 *angle = 2.0 * acos(w);
139 double s = 1.0 / norm_vec;
148 double dot = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
151 double q2_adjusted[4];
158 q2_adjusted[0] = -q2[0];
159 q2_adjusted[1] = -q2[1];
160 q2_adjusted[2] = -q2[2];
161 q2_adjusted[3] = -q2[3];
164 q2_adjusted[0] = q2[0];
165 q2_adjusted[1] = q2[1];
166 q2_adjusted[2] = q2[2];
167 q2_adjusted[3] = q2[3];
174 for (
int i = 0; i < 4; ++i) {
175 q_out[i] = q1[i] + t * (q2_adjusted[i] - q1[i]);
183 double theta = acos(dot);
184 double sin_theta = sin(theta);
188 double weight1 = sin((1 - t) * theta) / sin_theta;
189 double weight2 = sin(t * theta) / sin_theta;
193 for (
int i = 0; i < 4; ++i) {
194 q_out[i] = weight1 * q1[i] + weight2 * q2_adjusted[i];
198void axis_angle_rotate(
const double axis[3],
double angle,
const double v_in[3],
double v_out[3]) {
199 double cos_theta = cos(angle);
200 double sin_theta = sin(angle);
202 double dot = axis[0] * v_in[0] + axis[1] * v_in[1] + axis[2] * v_in[2];
204 cross[0] = axis[1] * v_in[2] - axis[2] * v_in[1];
205 cross[1] = axis[2] * v_in[0] - axis[0] * v_in[2];
206 cross[2] = axis[0] * v_in[1] - axis[1] * v_in[0];
208 for (
int i = 0; i < 3; ++i) {
209 v_out[i] = v_in[i] * cos_theta + cross[i] * sin_theta + axis[i] * dot * (1 - cos_theta);
220 double q0q0 = q[0] * q[0];
221 double q1q1 = q[1] * q[1];
222 double q2q2 = q[2] * q[2];
223 double q3q3 = q[3] * q[3];
225 double q0q1 = q[0] * q[1];
226 double q0q2 = q[0] * q[2];
227 double q0q3 = q[0] * q[3];
228 double q1q2 = q[1] * q[2];
229 double q1q3 = q[1] * q[3];
230 double q2q3 = q[2] * q[3];
233 double r11 = q0q0 + q1q1 - q2q2 - q3q3;
234 double r12 = 2.0 * (q1q2 - q0q3);
235 double r13 = 2.0 * (q1q3 + q0q2);
237 double r21 = 2.0 * (q1q2 + q0q3);
238 double r22 = q0q0 - q1q1 + q2q2 - q3q3;
239 double r23 = 2.0 * (q2q3 - q0q1);
241 double r31 = 2.0 * (q1q3 - q0q2);
242 double r32 = 2.0 * (q2q3 + q0q1);
243 double r33 = q0q0 - q1q1 - q2q2 + q3q3;
246 v_out[0] = r11 * v_in[0] + r12 * v_in[1] + r13 * v_in[2];
247 v_out[1] = r21 * v_in[0] + r22 * v_in[1] + r23 * v_in[2];
248 v_out[2] = r31 * v_in[0] + r32 * v_in[1] + r33 * v_in[2];
255 double v_quat[4] = {0.0, v_in[0], v_in[1], v_in[2]};
256 if (g_quaternion_explicit_debug) {
257 printf(
"[explicit] Step 1 (vector→quat): [%.6f, %.6f, %.6f, %.6f]\n",
258 v_quat[0], v_quat[1], v_quat[2], v_quat[3]);
270 const double w = q[0];
271 const double x = q[1];
272 const double y = q[2];
273 const double z = q[3];
275 const double vx = v_quat[1];
276 const double vy = v_quat[2];
277 const double vz = v_quat[3];
280 temp[0] = -x * vx - y * vy - z * vz;
281 temp[1] = w * vx + y * vz - z * vy;
282 temp[2] = w * vy + z * vx - x * vz;
283 temp[3] = w * vz + x * vy - y * vx;
285 if (g_quaternion_explicit_debug) {
286 printf(
"[explicit] Step 2 (temp=q⊗v): [%.6f, %.6f, %.6f, %.6f]\n",
287 temp[0], temp[1], temp[2], temp[3]);
292 const double q_conj[4] = {w, -x, -y, -z};
293 if (g_quaternion_explicit_debug) {
294 printf(
"[explicit] Step 3 (q_conj): [%.6f, %.6f, %.6f, %.6f]\n",
295 q_conj[0], q_conj[1], q_conj[2], q_conj[3]);
300 const double cw = q_conj[0];
301 const double cx = q_conj[1];
302 const double cy = q_conj[2];
303 const double cz = q_conj[3];
306 result[0] = temp[0] * cw - temp[1] * cx - temp[2] * cy - temp[3] * cz;
307 result[1] = temp[0] * cx + temp[1] * cw + temp[2] * cz - temp[3] * cy;
308 result[2] = temp[0] * cy - temp[1] * cz + temp[2] * cw + temp[3] * cx;
309 result[3] = temp[0] * cz + temp[1] * cy - temp[2] * cx + temp[3] * cw;
311 if (g_quaternion_explicit_debug) {
312 printf(
"[explicit] Step 4 (result=temp⊗q*): [%.6f, %.6f, %.6f, %.6f]\n",
313 result[0], result[1], result[2], result[3]);
317 v_out[0] = result[1];
318 v_out[1] = result[2];
319 v_out[2] = result[3];
320 if (g_quaternion_explicit_debug) {
321 printf(
"[explicit] Step 5 (rotated vector): [%.6f, %.6f, %.6f]\n",
322 v_out[0], v_out[1], v_out[2]);
327 g_quaternion_explicit_debug = enabled;
#define ATTITUDE_PI
Macro helper converting degrees to radians.
int quaternion_to_axis_angle(const double q[4], double axis[3], double *angle)
Convert a quaternion to axis-angle representation.
void axis_angle_rotate(const double axis[3], double angle, const double v_in[3], double v_out[3])
Rotate a vector about an axis by a given angle.
int quaternion_inverse(const double q[4], double q_inv[4])
Compute the inverse of a quaternion.
void quaternion_to_euler(const double q[4], double *roll, double *pitch, double *yaw)
Convert a quaternion to intrinsic ZYX Euler angles.
void quaternion_to_dcm(const double q[4], double dcm[3][3])
Convert a quaternion to a direction cosine matrix (DCM).
void quaternion_normalize(double q[4])
Normalise a quaternion in-place.
void quaternion_set_explicit_debug(int enabled)
Enable verbose logging for the explicit quaternion rotation implementation.
void quaternion_rotate_vector_explicit(const double q[4], const double v_in[3], double v_out[3])
Rotate a vector using the explicit formulation.
void quaternion_slerp(const double q1[4], const double q2[4], double t, double q_out[4])
Interpolate two quaternions using spherical linear interpolation (SLERP).
void quaternion_rotate_vector(const double q[4], const double v_in[3], double v_out[3])
Rotate a vector using a quaternion.
int quaternion_relative(const double q_current[4], const double q_target[4], double q_error[4])
Compute the relative rotation from the current orientation to a target orientation.
void quaternion_multiply(const double q1[4], const double q2[4], double q_out[4])
Multiply two quaternions using Hamilton product.
int quaternion_orientation_error_axis_angle(const double q_current[4], const double q_target[4], double axis[3], double *angle)
Compute current-to-target orientation error as an axis-angle command.