Sensor Fusion Redux

Posted: January 2, 2012 by Mike Ilich in Design, Programming

In the previous article, we discussed the polling of individual gyroscope axis values for driving the orientation of an object model in virtual 3D space. As a review, the basic structure for developing a sensor-based UI for 3D navigation is entirely driven by the Core Motion library in iOS, for which the CMMotionManager class is instantiated as a direct interface to the onboard accelerometer and gyroscope hardware in iPhone, iTouch and iPad devices (gyroscope available as of iPhone 4, iTouch 4G and iPad 1G). Sample code to initialize the CMMotionManager is provided below:

   (CMTestViewController.h, in the private interface variable declaration area)
      CMMotionManager *motionManager;
   (CMTestViewController.m, in viewDidLoad or similar setup)
      motionManager = [[CMMotionManager alloc] init];
      motionManager.accelerometerUpdateInterval = 0.01;
      motionManager.deviceMotionUpdateInterval = 0.01;
      [motionManager startDeviceMotionUpdates];

At this point, the motionManager variable can be polled at any point in your code to retrieve a CMDeviceMotion structure, describing the physical motion of the device in discrete intervals defined by the update interval property described above. The accelerometerUpdateInterval and deviceMotionUpdateInterval represent the interval (in seconds) for providing accelerometer and device-motion updates to the block handler, respectively. The six degrees of physical motion can be described by accelerometer values for each independent axis (X, Y, Z) as well as the overall attitude of the device, derived from the embedded “attitude” class variable that can be retrieved through:

      motionManager.deviceMotion.attitude

This variable contains properties such as “.yaw”, “.pitch” and “.roll”, which we previously used to describe rotation of the device along its Z, X and Y axes respectively. In addition, “attitude” provides a property named “rotationMatrix” that provides a full 3×3 matrix describing the Euler rotations that are required to translate a rigid body in 3D Euclidian space. The Euler angles represent a series of aggregate rotations, each around a single axis, that define the orientation of a point of reference in a three dimensional coordinate system. Therefore, the rotation matrix derived from the gyroscope attitude can be described as the product of three elemental rotation matrices, each representing a single axis. As mentioned in the previous post, the order in which these rotations are applied can significantly affect the overall translation of a point in 3D space, consisting of yaw, pitch and roll.

In addition, these operations result in the introduction of Gimbal lock, or the loss of one degree of freedom when two out of the three rotation axes in 3D Euclidian space are oriented in parallel and the system becomes locked to rotation in 2D space. We observed this phenomenon as either the gyroscope X or Y axis was rotated in parallel with the Z axis, effectively locking rotation of the remaining axis. In order to counteract this effect, a static fourth axis is added by embedding the attitude.rotationMatrix in a 4×4 Matrix with an identity vector for the fourth dimension. Note that a model view matrix is defined for the object model in 3D space and subsequently multiplied by the rotation matrix to complete the transformation. The model view matrix is distinct from the projection matrix, which defines the camera angle, aspect ratio and near/far values for Z offset.

Here is an example of how this might be arranged in the GLKit library, exclusive to iOS 5.0:

      CMDeviceMotion *deviceMotion = motionManager.deviceMotion; 
      CMAttitude *attitude = deviceMotion.attitude;
      rotation = attitude.rotationMatrix;
      GLKMatrix4 rotationMatrix = GLKMatrix4Make(rotation.m11, rotation.m21, rotation.m31, 0, rotation.m12, rotation.m22, rotation.m32, 0, rotation.m13, rotation.m23, rotation.m33, 0, 0, 0, 0, 1);
      modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, rotationMatrix);

From this point, the modelViewMatrix can be scaled, rotated by a gravity correction factor or applied directly to the transform property of either an object model or skybox effect, inherent to the GLKit API that was included in the iOS 5.0 release to simplify OpenGL ES by providing a usable template and higher level function calls for common 3D modeling elements. By replacing the aggregate yaw, pitch and roll rotation operations with a full rotation matrix accounting for the Euler angles, we have resolved any further ambiguity in model view orientation as well as the incidence of Gimbal lock when taking into account the azimuth in 3D space.

Advertisements
Comments
  1. sam says:

    Dear sir, I appreciate your effort in writing good articles in a field where most find it difficult to understand. 🙂 . If possible please be kind enough to answer my question. I wanted to update rotational rate from the gyroscope to a predefined orientation like axis along the gravity(vertical axis), and obtain the individual values for x,y and z.

    What i did was to
    rot = attitude.rotationMatrix;
    float gyro_x_ref=((rotate.x)*(rot.m11)+(rotate.y)*(rot.m12)+(rotate.z)*(rot.m13));

    I get a reading, But i m bit confused as what this reading corresponds to, As i have no idea about rotational matrix generated by ios.

    I think ur way, will help me to establish this even without the problem of gimbal lock. Can you please help me?

    Thank you!

  2. Mike Ilich says:

    Hi Sam,

    Thanks for your question, first a quick primer on 3D Mathematics.

    We define our rotation matrix as:
    m11 m12 m13
    m21 m22 m23
    m31 m32 m33

    Consider each row to be a unit vector that is orthogonal to the other 2. Row 3 [m31,m32,m33] is the line-of-sight “view” unit vector projected onto the x, y and z axes. Row 2 [m21,m22,m23] is the “up” unit vector projected onto the x, y and z axes. Row 1 [m11,m12,m13] is the “right” unit vector projected onto the x, y and z axes (a cross product of “view” and “up”, all are perpendicular). So you are working with the “right” unit vector?

    If you’re looking for the rotation rate of the gyroscope, the CMDeviceMotion class has a rotationRate property. From my code above: “devicemotion.rotationRate”, which is merely a struct that has an x, y and z component. Is “rotate” above your rotation rate directly from the gyroscope?

    Also, I would suggest working in 4×4 matrices to give you a translation component, as often you need to use (0,0,0) as a reference when taking products by unit vectors.

    m11 m12 m13 m14
    m21 m22 m23 m24
    m31 m32 m33 m34
    m41 m42 m43 m44

    We literally embed the previous 3×3 into the upper left of this matrix and define m14,m24,m34,m41,m42 and m43 as 0 (no translation) and m44 as 1 (unit). m11, m22, m33, m44 define scale as usual.

    Here’s a more thorough primer on 3D math in the context of iOS that should help:
    http://iphonedevelopment.blogspot.it/2009/06/opengl-es-from-ground-up-part-7_04.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s