summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2013-10-27 23:11:37 -0700
committerKeith Packard <keithp@keithp.com>2013-10-27 23:11:37 -0700
commitc10cb9d31765e6ef0ba737bc484c5aed22a332f9 (patch)
treeafbcec7230f79059537d133c6ac45d0d4418f3be /src
parent3b25860b5b3b69642928dd9c30dec4b4b937a88c (diff)
altos: Add functions to init quaternions from vector pairs and euler angles
Our low sampling rate means that the "cheap" hack for integrating quaternion rotations by using sin(x) ≃ x doesn't work, so instead we have to compute the partial rotation the hard way. Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'src')
-rw-r--r--src/core/ao_quaternion.h97
1 files changed, 94 insertions, 3 deletions
diff --git a/src/core/ao_quaternion.h b/src/core/ao_quaternion.h
index 6a701a18..6c885500 100644
--- a/src/core/ao_quaternion.h
+++ b/src/core/ao_quaternion.h
@@ -110,17 +110,68 @@ static inline void ao_quaternion_normalize(struct ao_quaternion *r,
}
static inline void ao_quaternion_rotate(struct ao_quaternion *r,
- struct ao_quaternion *a,
- struct ao_quaternion *b)
+ const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
{
struct ao_quaternion c;
struct ao_quaternion t;
- ao_quaternion_conjugate(&c, b);
ao_quaternion_multiply(&t, b, a);
+ ao_quaternion_conjugate(&c, b);
ao_quaternion_multiply(r, &t, &c);
}
+/*
+ * Compute a rotation quaternion between two vectors
+ *
+ * cos(θ) + u * sin(θ)
+ *
+ * where θ is the angle between the two vectors and u
+ * is a unit vector axis of rotation
+ */
+
+static inline void ao_quaternion_vectors_to_rotation(struct ao_quaternion *r,
+ const struct ao_quaternion *a,
+ const struct ao_quaternion *b)
+{
+ /*
+ * The cross product will point orthogonally to the two
+ * vectors, forming our rotation axis. The length will be
+ * sin(θ), so these values are already multiplied by that.
+ */
+
+ float x = a->y * b->z - a->z * b->y;
+ float y = a->z * b->x - a->x * b->z;
+ float z = a->x * b->y - a->y * b->x;
+
+ float s_2 = x*x + y*y + z*z;
+ float s = sqrtf(s_2);
+
+ /* cos(θ) = a · b / (|a| |b|).
+ *
+ * a and b are both unit vectors, so the divisor is one
+ */
+ float c = a->x*b->x + a->y*b->y + a->z*b->z;
+
+ float c_half = sqrtf ((1 + c) / 2);
+ float s_half = sqrtf ((1 - c) / 2);
+
+ /*
+ * Divide out the sine factor from the
+ * cross product, then multiply in the
+ * half sine factor needed for the quaternion
+ */
+ float s_scale = s_half / s;
+
+ r->x = x * s_scale;
+ r->y = y * s_scale;
+ r->z = z * s_scale;
+
+ r->r = c_half;
+
+ ao_quaternion_normalize(r, r);
+}
+
static inline void ao_quaternion_init_vector(struct ao_quaternion *r,
float x, float y, float z)
{
@@ -146,4 +197,44 @@ static inline void ao_quaternion_init_zero_rotation(struct ao_quaternion *r)
r->x = r->y = r->z = 0;
}
+/*
+ * The sincosf from newlib just calls sinf and cosf. This is a bit
+ * faster, if slightly less precise
+ */
+
+static inline void
+ao_sincosf(float a, float *s, float *c) {
+ float _s = sinf(a);
+ *s = _s;
+ *c = sqrtf(1 - _s*_s);
+}
+
+/*
+ * Initialize a quaternion from 1/2 euler rotation angles (in radians).
+ *
+ * Yes, it would be nicer if there were a faster way, but because we
+ * sample the gyros at only 100Hz, we end up getting angles too large
+ * to take advantage of sin(x) ≃ x.
+ *
+ * We might be able to use just a couple of elements of the sin taylor
+ * series though, instead of the whole sin function?
+ */
+
+static inline void ao_quaternion_init_half_euler(struct ao_quaternion *r,
+ float x, float y, float z)
+{
+ float s_x, c_x;
+ float s_y, c_y;
+ float s_z, c_z;
+
+ ao_sincosf(x, &s_x, &c_x);
+ ao_sincosf(y, &s_y, &c_y);
+ ao_sincosf(z, &s_z, &c_z);
+
+ r->r = c_x * c_y * c_z + s_x * s_y * s_z;
+ r->x = s_x * c_y * c_z - c_x * s_y * s_z;
+ r->y = c_x * s_y * c_z + s_x * c_y * s_z;
+ r->z = c_x * c_y * s_z - s_x * s_y * c_z;
+}
+
#endif /* _AO_QUATERNION_H_ */