Skip to content

Commit 216a0b6

Browse files
committed
pbio/imu: Implement 3D fusion.
1 parent 9388ddb commit 216a0b6

File tree

6 files changed

+503
-47
lines changed

6 files changed

+503
-47
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@
88

99
- Added optional `calibrated=True` parameter to `acceleration()` and `up()` and
1010
`angular_velocity()` methods of the IMU ([support#943]).
11+
- Implemented `hub.imu.orientation()` to give the rotation matrix of the hub or
12+
robot with respect to the inertial frame.
13+
- Added calibration parameters that can be set for angular velocity offset and
14+
scale and acceleration offset and scale.
1115

1216
### Changed
1317

1418
- The method `DriveBase.angle()` now returns a float ([support#1844]). This
1519
makes it properly equivalent to `hub.imu.heading`.
20+
- Re-implemented tilt using the gyro data by default. Pure accelerometer tilt
21+
can still be obtained with `hub.imu.tilt(use_gyro=False)`.
22+
- Re-implemented `hub.imu.heading()` to use projection of 3D orientation to
23+
improve performance when the hub is lifted off the ground. If necessary,
24+
pure 1D rotation can still be obtained from `hub.imu.rotation()`.
1625

1726
### Fixed
1827
- Fixed `DriveBase.angle()` getting an incorrectly rounded gyro value, which

lib/pbio/include/pbio/geometry.h

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,46 @@ typedef union {
4343
/**
4444
* Representation of a 3x3 matrix.
4545
*/
46-
typedef struct _pbio_geometry_matrix_3x3_t {
47-
union {
48-
struct {
49-
float m11;
50-
float m12;
51-
float m13;
52-
float m21;
53-
float m22;
54-
float m23;
55-
float m31;
56-
float m32;
57-
float m33;
58-
};
59-
float values[9];
46+
typedef union {
47+
struct {
48+
float m11;
49+
float m12;
50+
float m13;
51+
float m21;
52+
float m22;
53+
float m23;
54+
float m31;
55+
float m32;
56+
float m33;
6057
};
58+
float values[9];
6159
} pbio_geometry_matrix_3x3_t;
6260

61+
/**
62+
* Quaternion orientation or its time derivative.
63+
*/
64+
typedef union {
65+
struct {
66+
float q1; /**< q1 coordinate.*/
67+
float q2; /**< q2 coordinate.*/
68+
float q3; /**< q3 coordinate.*/
69+
float q4; /**< q4 coordinate.*/
70+
};
71+
float values[4];
72+
} pbio_geometry_quaternion_t;
73+
74+
#define pbio_geometry_degrees_to_radians(degrees) ((degrees) * 0.017453293f)
75+
76+
#define pbio_geometry_radians_to_degrees(radians) ((radians) * 57.29577951f)
77+
6378
void pbio_geometry_side_get_axis(pbio_geometry_side_t side, uint8_t *index, int8_t *sign);
6479

6580
void pbio_geometry_get_complementary_axis(uint8_t *index, int8_t *sign);
6681

6782
pbio_geometry_side_t pbio_geometry_side_from_vector(pbio_geometry_xyz_t *vector);
6883

84+
float pbio_geometry_vector_norm(pbio_geometry_xyz_t *input);
85+
6986
pbio_error_t pbio_geometry_vector_normalize(pbio_geometry_xyz_t *input, pbio_geometry_xyz_t *output);
7087

7188
void pbio_geometry_vector_cross_product(pbio_geometry_xyz_t *a, pbio_geometry_xyz_t *b, pbio_geometry_xyz_t *output);
@@ -74,8 +91,22 @@ pbio_error_t pbio_geometry_vector_project(pbio_geometry_xyz_t *axis, pbio_geomet
7491

7592
void pbio_geometry_vector_map(pbio_geometry_matrix_3x3_t *map, pbio_geometry_xyz_t *input, pbio_geometry_xyz_t *output);
7693

94+
void pbio_geometry_matrix_multiply(pbio_geometry_matrix_3x3_t *a, pbio_geometry_matrix_3x3_t *b, pbio_geometry_matrix_3x3_t *output);
95+
7796
pbio_error_t pbio_geometry_map_from_base_axes(pbio_geometry_xyz_t *x_axis, pbio_geometry_xyz_t *z_axis, pbio_geometry_matrix_3x3_t *rotation);
7897

98+
void pbio_geometry_quaternion_to_rotation_matrix(pbio_geometry_quaternion_t *q, pbio_geometry_matrix_3x3_t *R);
99+
100+
void pbio_geometry_quaternion_from_gravity_unit_vector(pbio_geometry_xyz_t *g, pbio_geometry_quaternion_t *q);
101+
102+
void pbio_geometry_quaternion_get_rate_of_change(pbio_geometry_quaternion_t *q, pbio_geometry_xyz_t *w, pbio_geometry_quaternion_t *dq);
103+
104+
void pbio_geometry_quaternion_normalize(pbio_geometry_quaternion_t *q);
105+
106+
float pbio_geometry_maxf(float a, float b);
107+
108+
float pbio_geometry_absf(float a);
109+
79110
#endif // _PBIO_GEOMETRY_H_
80111

81112
/** @} */

lib/pbio/include/pbio/imu.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ void pbio_imu_get_angular_velocity(pbio_geometry_xyz_t *values, bool calibrated)
9292

9393
void pbio_imu_get_acceleration(pbio_geometry_xyz_t *values, bool calibrated);
9494

95+
void pbio_imu_get_tilt_vector(pbio_geometry_xyz_t *values);
96+
9597
pbio_error_t pbio_imu_get_single_axis_rotation(pbio_geometry_xyz_t *axis, float *angle);
9698

9799
pbio_geometry_side_t pbio_imu_get_up_side(bool calibrated);
@@ -102,6 +104,8 @@ void pbio_imu_set_heading(float desired_heading);
102104

103105
void pbio_imu_get_heading_scaled(pbio_angle_t *heading, int32_t *heading_rate, int32_t ctl_steps_per_degree);
104106

107+
void pbio_orientation_imu_get_rotation(pbio_geometry_matrix_3x3_t *rotation);
108+
105109
#else // PBIO_CONFIG_IMU
106110

107111
static inline void pbio_imu_init(void) {
@@ -149,6 +153,10 @@ static inline void pbio_imu_set_heading(float desired_heading) {
149153
static inline void pbio_imu_get_heading_scaled(pbio_angle_t *heading, int32_t *heading_rate, int32_t ctl_steps_per_degree) {
150154
}
151155

156+
static inline void pbio_orientation_imu_get_rotation(pbio_geometry_matrix_3x3_t *rotation) {
157+
}
158+
159+
152160
#endif // PBIO_CONFIG_IMU
153161

154162
#endif // _PBIO_IMU_H_

lib/pbio/src/geometry.c

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,29 @@ pbio_geometry_side_t pbio_geometry_side_from_vector(pbio_geometry_xyz_t *vector)
104104
return axis | (negative << 2);
105105
}
106106

107+
/**
108+
* Gets the norm of a vector.
109+
*
110+
* @param [in] input The vector.
111+
* @return The norm of the vector.
112+
*/
113+
float pbio_geometry_vector_norm(pbio_geometry_xyz_t *input) {
114+
return sqrtf(input->x * input->x + input->y * input->y + input->z * input->z);
115+
}
116+
107117
/**
108118
* Normalizes a vector so it has unit length.
109119
*
120+
* Output is allowed to be same as input, in which case input is normalized.
121+
*
110122
* @param [in] input The vector to normalize.
111123
* @param [out] output The normalized vector.
112124
* @return ::PBIO_ERROR_INVALID_ARG if the input has zero length, otherwise ::PBIO_SUCCESS.
113125
*/
114126
pbio_error_t pbio_geometry_vector_normalize(pbio_geometry_xyz_t *input, pbio_geometry_xyz_t *output) {
115127

116128
// Compute the norm.
117-
float norm = sqrtf(input->x * input->x + input->y * input->y + input->z * input->z);
129+
float norm = pbio_geometry_vector_norm(input);
118130

119131
// If the vector norm is zero, do nothing.
120132
if (norm == 0.0f) {
@@ -176,6 +188,25 @@ void pbio_geometry_vector_map(pbio_geometry_matrix_3x3_t *map, pbio_geometry_xyz
176188
output->z = input->x * map->m31 + input->y * map->m32 + input->z * map->m33;
177189
}
178190

191+
/**
192+
* Multiplies two 3x3 matrices.
193+
*
194+
* @param [in] a The first 3x3 matrix.
195+
* @param [in] b The second 3x3 matrix.
196+
* @param [out] output The resulting 3x3 matrix after multiplication.
197+
*/
198+
void pbio_geometry_matrix_multiply(pbio_geometry_matrix_3x3_t *a, pbio_geometry_matrix_3x3_t *b, pbio_geometry_matrix_3x3_t *output) {
199+
output->m11 = a->m11 * b->m11 + a->m12 * b->m21 + a->m13 * b->m31;
200+
output->m12 = a->m11 * b->m12 + a->m12 * b->m22 + a->m13 * b->m32;
201+
output->m13 = a->m11 * b->m13 + a->m12 * b->m23 + a->m13 * b->m33;
202+
output->m21 = a->m21 * b->m11 + a->m22 * b->m21 + a->m23 * b->m31;
203+
output->m22 = a->m21 * b->m12 + a->m22 * b->m22 + a->m23 * b->m32;
204+
output->m23 = a->m21 * b->m13 + a->m22 * b->m23 + a->m23 * b->m33;
205+
output->m31 = a->m31 * b->m11 + a->m32 * b->m21 + a->m33 * b->m31;
206+
output->m32 = a->m31 * b->m12 + a->m32 * b->m22 + a->m33 * b->m32;
207+
output->m33 = a->m31 * b->m13 + a->m32 * b->m23 + a->m33 * b->m33;
208+
}
209+
179210
/**
180211
* Gets a mapping (a rotation matrix) from two orthogonal base axes.
181212
*
@@ -216,3 +247,101 @@ pbio_error_t pbio_geometry_map_from_base_axes(pbio_geometry_xyz_t *x_axis, pbio_
216247

217248
return PBIO_SUCCESS;
218249
}
250+
251+
/**
252+
* Computes a rotation matrix for a quaternion.
253+
*
254+
* @param [in] q The quaternion.
255+
* @param [out] R The rotation matrix.
256+
*/
257+
void pbio_geometry_quaternion_to_rotation_matrix(pbio_geometry_quaternion_t *q, pbio_geometry_matrix_3x3_t *R) {
258+
R->m11 = 1 - 2 * (q->q2 * q->q2 + q->q3 * q->q3);
259+
R->m21 = 2 * (q->q1 * q->q2 + q->q3 * q->q4);
260+
R->m31 = 2 * (q->q1 * q->q3 - q->q2 * q->q4);
261+
R->m12 = 2 * (q->q1 * q->q2 - q->q3 * q->q4);
262+
R->m22 = 1 - 2 * (q->q1 * q->q1 + q->q3 * q->q3);
263+
R->m32 = 2 * (q->q2 * q->q3 + q->q1 * q->q4);
264+
R->m13 = 2 * (q->q1 * q->q3 + q->q2 * q->q4);
265+
R->m23 = 2 * (q->q2 * q->q3 - q->q1 * q->q4);
266+
R->m33 = 1 - 2 * (q->q1 * q->q1 + q->q2 * q->q2);
267+
}
268+
269+
/**
270+
* Computes a quaternion from a gravity unit vector.
271+
*
272+
* @param [in] g The gravity unit vector.
273+
* @param [out] q The resulting quaternion.
274+
*/
275+
void pbio_geometry_quaternion_from_gravity_unit_vector(pbio_geometry_xyz_t *g, pbio_geometry_quaternion_t *q) {
276+
if (g->z >= 0) {
277+
q->q4 = sqrtf((g->z + 1) / 2);
278+
q->q1 = g->y / sqrtf(2 * (g->z + 1));
279+
q->q2 = -g->x / sqrtf(2 * (g->z + 1));
280+
q->q3 = 0;
281+
} else {
282+
q->q4 = -g->y / sqrtf(2 * (1 - g->z));
283+
q->q1 = -sqrtf((1 - g->z) / 2);
284+
q->q2 = 0;
285+
q->q3 = -g->x / sqrtf(2 * (1 - g->z));
286+
}
287+
}
288+
289+
/**
290+
* Computes the rate of change of a quaternion given the angular velocity vector.
291+
*
292+
* @param [in] q Quaternion of the current orientation.
293+
* @param [in] angular_velocity The angular velocity vector in the body frame.
294+
* @param [out] dq The rate of change of the quaternion.
295+
*/
296+
void pbio_geometry_quaternion_get_rate_of_change(pbio_geometry_quaternion_t *q, pbio_geometry_xyz_t *angular_velocity, pbio_geometry_quaternion_t *dq) {
297+
298+
pbio_geometry_xyz_t w = {
299+
.x = pbio_geometry_degrees_to_radians(angular_velocity->x),
300+
.y = pbio_geometry_degrees_to_radians(angular_velocity->y),
301+
.z = pbio_geometry_degrees_to_radians(angular_velocity->z),
302+
};
303+
304+
dq->q1 = 0.5f * (w.z * q->q2 - w.y * q->q3 + w.x * q->q4);
305+
dq->q2 = 0.5f * (-w.z * q->q1 + w.x * q->q3 + w.y * q->q4);
306+
dq->q3 = 0.5f * (w.y * q->q1 - w.x * q->q2 + w.z * q->q4);
307+
dq->q4 = 0.5f * (-w.x * q->q1 - w.y * q->q2 - w.z * q->q3);
308+
}
309+
310+
/**
311+
* Normalizes a quaternion so it has unit length.
312+
*
313+
* @param [inout] q The quaternion to normalize.
314+
*/
315+
void pbio_geometry_quaternion_normalize(pbio_geometry_quaternion_t *q) {
316+
float norm = sqrtf(q->q1 * q->q1 + q->q2 * q->q2 + q->q3 * q->q3 + q->q4 * q->q4);
317+
318+
if (norm < 0.0001f && norm > -0.0001f) {
319+
return;
320+
}
321+
322+
q->q1 /= norm;
323+
q->q2 /= norm;
324+
q->q3 /= norm;
325+
q->q4 /= norm;
326+
}
327+
328+
/**
329+
* Returns the maximum of two floating-point numbers.
330+
*
331+
* @param a The first floating-point number.
332+
* @param b The second floating-point number.
333+
* @return The maximum of the two floating-point numbers.
334+
*/
335+
float pbio_geometry_maxf(float a, float b) {
336+
return a > b ? a : b;
337+
}
338+
339+
/**
340+
* Returns the absolute value of a floating-point number.
341+
*
342+
* @param a The floating-point number.
343+
* @return The absolute value of the floating-point number.
344+
*/
345+
float pbio_geometry_absf(float a) {
346+
return a < 0 ? -a : a;
347+
}

0 commit comments

Comments
 (0)