1
$\begingroup$

In short: How can I get a single angle along a local (body-fixed) axis from a quaternion?

Rotations (Click for 3D view)

In not-so-short: I am using an IMU with filtering to track the orientation of someone's upper leg. I end up with a quaternion describing the rotation from world frame to the leg frame (local frame, or body-fixed frame). But I only need a single angle from this quaternion: the swing angle (hip flexion). This is the angle around local +z (not as in the picture).

In more general terms: imagine I have an arbitrary rotation (see picture) and some local axis (the translucent blue line), how can I find the angle between the rotated frame and the original frame along this axis?

In the posted example I would expect to find about +170 degrees (sign based on right-hand rule from the selected rotation axis).

$\endgroup$

1 Answer 1

1
$\begingroup$

I think I found a solution. The procedure:

  1. Choose a world axis perpendicular to the interesting axis (in the picture I want to know the angle around local +Z, so I now chose the X axis.
  2. Express the unit vector of the chosen axis in the local frame, here: $\hat{x}^{bf} = \mathrm{R}\hat{x}^W$
  3. Project the unit vector onto the plane perpendicular to the rotation axis, here: $v = \begin{pmatrix}\hat{x}^{bf}_x \\ \hat{x}^{bf}_y \\ 0\end{pmatrix}$
  4. Now get the angle from this projected vector, relative to the local +x axis: $\theta = \mathrm{atan2}(v_y, v_x)$

enter image description here

You will get a singularity when the rotated axis is opposite the world axis. You can choose a unit vector on the axis where this is least likely.

Rough code example (assuming some rotation library is present):

float getLegAngle(const float quat[4]) {
    float v[3] = {0.0, 0.0, -1.0}; // Get angle between vector pointing
    // down and the same vector but rotated

    float quat_conj[4]; // `quat` rotates the frame to the world
    quaternionConjugate(quat, quat_conj);
    
    rotateVectorByQuaternion(v, quat_conj);

    return -atan2f(v[1], v[0]) * RAD_TO_DEG;
}
$\endgroup$
5
  • $\begingroup$ Implemented this in code and it seems to work properly. Accepting my own answer. $\endgroup$
    – Roberto
    Jul 16, 2021 at 11:13
  • $\begingroup$ can you share this code please? ( I am attempting to solve a similar problem ) $\endgroup$
    – angryip
    Jul 6, 2022 at 11:01
  • 1
    $\begingroup$ I added my code to the answer. It's not a lot though, I rely on existing rotation functions. $\endgroup$
    – Roberto
    Jul 6, 2022 at 11:48
  • $\begingroup$ what happens if you spin around in a circle while perform hip flexion? $\endgroup$
    – angryip
    Jul 6, 2022 at 11:50
  • $\begingroup$ With the situation of this post, the resulting angle would flip once the user has turned over 180 degrees, because we compare to a world axis. The heading of the IMU is a different issue, outside this post. But I did solve that, by resetting the heading of the leg quaternion every frame. Full project can be found here (sorry, code is a mess) $\endgroup$
    – Roberto
    Jul 6, 2022 at 12:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .