derbian
derbian

Reputation: 67

iOS core motion detect forard / backward tilt

I am using the iOS core motion framework to detect if the device is tilted forward or backwards. See image for details: https://i.sstatic.net/2Ojw5.jpg

Using the pitch value a can detect this movement but I can not distinguish between forward AND backward.

More details:

I try to detect if there is a movement (tilting forward and backward) in either the forward area OR backward area (see updated sketch).

The problem with the pitch is that it starts with a value of about 1.6 if the device is in an upright position. And the value decreases the same when I am tilting it towards a horizontal potion either forward or backward. The same behavior applies to the accelerometer y value.

It looks like I miss something in the whole core motion thing. ANy Ideas

thanks christian

Upvotes: 4

Views: 3568

Answers (5)

Conall McCabe
Conall McCabe

Reputation: 1

Use BOTH pitch and roll. If roll (which varies from -180 degrees to 180 degrees) is less than -90 degrees or greater than 90 degrees, it is face down. When the pitch of the device comes over the top (reaches 90 degrees, and proceeds to decrease) roll switches to represent the equivalent of a 180 degree flip. Between the two, you can determine which way the device is facing, even if orientation lock is engaged.

Upvotes: -1

inorganik
inorganik

Reputation: 25525

Using attitude pitch, leaning forward and backward are indistinguishable. However with quaternions you can calculate pitch, and if you convert radians to degrees,

  • 0 means the device is on its back
  • 90 means it's standing up
  • 180 means it's on its face

The opposite hemisphere of rotation is 0 to -180. Here's the code:

func radiansToDegrees(_ radians: Double) -> Double {
    return radians * (180.0 / Double.pi)
}

let quat = motionData.attitude.quaternion
let qPitch = CGFloat(radiansToDegrees(atan2(2 * (quat.x * quat.w + quat.y * quat.z), 1 - 2 * quat.x * quat.x - 2 * quat.z * quat.z)))

Upvotes: 11

DaveUT
DaveUT

Reputation: 153

You don't want to read the accelerometer for tilt. Accelerometer is for detecting differences in movements. You want the gyroscope so you can determine the absolute attitude (i.e. yaw, pitch and roll). In your case it sounds like you just want roll.

Use startDeviceMotionUpdatesToQueue and then attitude.roll for front and back and attitude.pitch for side to side. Here is what I did in Swift:

    func motion(data: CMDeviceMotion){  

    let pitch = data.attitude.pitch
    let roll = data.attitude.roll

    let dampener:Float = -0.25 // the ball was rolling too fast

    var forward_force = Float(1.6 - roll) * dampener  //1.6 is vertical
    var side_force = Float(pitch) * dampener  // 0 is untilted when rotating cw/ccw

    ballNode.physicsBody?.applyForce(SCNVector3Make(side_force, 0, forward_force), atPosition: SCNVector3Make(0, 0, 0), impulse: true)

}

With this you can see if it tilted frontward or backward based on whether the roll is greater than or equal to 1.6 which is approximately straight up.

Upvotes: 0

apascual
apascual

Reputation: 2980

You are right, pitch works that way, considering the 4 typical quadrants (http://en.wikipedia.org/wiki/Quadrant_(plane_geometry)) and the counterclockwise direction:

  • Quadrant I, values range from 0 to PI/2 (or 0 to 90 in degrees).
  • Quadrant II, values range from PI/2 to 0 (or 90 to 0 in degrees).
  • Quadrant III, values range from 0 to -PI/2 (or 0 to -90 in degrees).
  • Quadrant IV, values range from -PI/2 to 0 (or -90 to 0 in degrees).

Considering this looks pretty obvious that you cannot difference between the phone leaning forwards or backwards.

I have recently faced the same problem for an iOS app that counts the number of flips that the phone does. Apple has rejected it so I have published it on GitHub, may be useful for you:

Flip Your Phone! - https://github.com/apascual/flip-your-phone

Upvotes: 0

Jesse Hiatt
Jesse Hiatt

Reputation: 23

Try this:

// Create a CMMotionManager
CMMotionManager *mManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
// Check whether the accelerometer is available
if ([mManager isAccelerometerAvailable] == YES) {
    [mManager setAccelerometerUpdateInterval: .02];
    [mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
        [self updateGravityWithAccelerometerX:accelerometerData.acceleration.x y:accelerometerData.acceleration.y z:accelerometerData.acceleration.z];
    }];
}

This will call updateGravityWithAccelerometerData every .02 seconds. You should be able to create that method and use NSLog to watch the values change to decipher what you are looking for. I believe you are looking for the acceleration.y value.

Upvotes: 0

Related Questions