Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please help me correctly apply device rotation data

So I have a bit of a project I am trying to do. I am trying to get the devices rotation relative to gravity, and translation from where it started. So basically getting "tracking" data for the device. I plan to basically apply this by making a 3d pt that will mimic the data I record from the device later on.

Anyway to attempt to achieve this I thought it would be best to work with scene kit that way I can see things in 3 dimensions just like the data I am trying to record. Right now I have been trying to get the ship to rotate so that it always looks like its following gravity (like its on the ground or something) no mater what the device rotation is. I figure once I have this down it will be a sinch to apply this to a point. So I made the following code:

if let attitude = motionManager.deviceMotion?.attitude {
        print(attitude)


        ship.eulerAngles.y = -Float(attitude.roll)
        ship.eulerAngles.z = -Float(attitude.yaw)
        ship.eulerAngles.x = -Float(attitude.pitch)

    }

When you only run one of the rotation lines then everything is perfectly. It does behave properly on that axis. However when I do all three axis' at once it becomes chaotic and performs far from expected with jitter and everything.

I guess my question is: Does anyone know how to fix my code above so that the ship properly stays "upright" no matter what the orientation.

like image 941
J.Doe Avatar asked Oct 22 '15 01:10

J.Doe


People also ask

What is device orientation?

The default orientation for most smartphones and for the iPad is portrait. However, landscape is the default for Android, Windows 8 tablets and BlackBerry's Playbook.

What is orientation in mobile device?

The device orientation event returns rotation data, which includes how much the device is leaning front-to-back, side-to-side, and, if the phone or laptop has a compass, the direction the device is facing.

What is device motion?

The first-generation device motions support is a part of Device Orientation API. It allows Web applications to access the accelerometer data expressed as acceleration (in m/s2) and gyroscope data expressed as rotation angle change (in °/s) for each of the three dimensions, provided as events.

How do I check landscape mode in Swift?

orientation. isLandscape { print("Device is in landscape mode") } else { print("Device is in portrait mode") } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { determineMyDeviceOrientation() } override func didReceiveMemoryWarning() { super.


1 Answers

J.Doe!

First there is a slight trick. If you want to use the iphone laying down as the default position you have to notice that the axis used on sceneKit are different then those used by the DeviceMotion. Check the axis:

deviceMotion axissceneKit axis
(source: apple.com)

First thing you need to set is the camera position. When you start a SceneKit project it creates your camera in the position (0, 0, 15). There is a problem with that:

The values of eulerAngles = (0,0,0) would mean the object would be in the plane xz, but as long as you are looking from Z, you just see it from the side. For that to be equivalent to the iphone laying down, you would need to set the camera to look from above. So it would be like you were looking at it from the phone (like a camera, idk)

// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)

// place the camera
cameraNode.position = SCNVector3(x: 0, y: 15, z: 0)
// but then you need to make the cameraNode face the ship (the origin of the axis), rotating it
cameraNode.eulerAngles.x = -Float(M_PI)*0.5 //or Float(M_PI)*1.5

With this we are going to see the ship from above, so the first part is done. Now we gotta make the ship remain "still" (facing the ground) with the device rotation.

//First we need to use SCNRendererDelegate
class GameViewController : UIViewController SCNSceneRendererDelegate{
    private let motion = CMMotionManager();
...

Then on viewDidLoad:

//important if you remove the sceneKit initial action from the ship.
//The scene would be static, and static scenes do not trigger the renderer update, setting the playing property to true forces that:
scnView.playing = true;
if(motion.deviceMotionAvailable){
    motion.startDeviceMotionUpdates();
    motion.deviceMotionUpdateInterval = 1.0/60.0;
}

Then we go to the update method

Look at the axis: the axis Y and Z are "switched" if you compare the sceneKit axis and the deviceMotion axis. Z is up on the phone, while is to the side on the scene, and Y is up on the scene, while to the side on the phone. So the pitch, roll and yaw, respectively associated to the X, Y and Z axis, will be applied as pitch, yaw and roll.

Notice I've put the roll value positive, that's because there is something else "switched". It's kinda hard to visualize. See the Y axis of device motion is correlated to the Z axis of the scene. Now imagine an object rotation along this axis, in the same direction (clock-wise for example), they would be going in opposite directions because of the disposition of the axis. (you can set the roll negative too see how it goes wrong)

func renderer(renderer: SCNSceneRenderer, updateAtTime time: NSTimeInterval) {
    if let rot = motion.deviceMotion?.attitude{
        print("\(rot.pitch) \(rot.roll) \(rot.yaw)")
        ship.eulerAngles.x = -Float(rot.pitch);
        ship.eulerAngles.y = -Float(rot.yaw);
        ship.eulerAngles.z = Float(rot.roll);
    }

Hope that helps! See ya!

like image 103
Pietro Pepe Avatar answered Nov 08 '22 18:11

Pietro Pepe