I was wondering how to get the iphones orientation with respect to cardinal direction using Core Motion. I think the best route to go is to use the attitude of the device with respect to the magnetic field given by the magnetometer. From the appl e documentation it describes the attitude as:
A CMAttitude object represents a measurement of attitude—that is, the orientation of a body relative to a given frame of reference.
I was wondering how I could make this "given frame of reference" the cardinal directions. Maybe by using using CMCalibratedMagneticField?
Can anybody tell me how to do this or go an easier alternate route.
I'm not 100% sure if this is what you're looking for, but you can get device attitude values from the motion manager using the CMAttitudeReferenceFrameXMagneticNorthZVertical reference frame with the following. This code will dump the attitude of the device to log 30 times per second. If seems to me that yaw values are around 0 facing east, and extend to 180 degrees in both directions, positive values being counter clockwise, and negative being clockwise.
- (void)startMonitoring
{
if (!self.motionManager) {
self.motionManager = [CMMotionManager new];
[self.motionManager setDeviceMotionUpdateInterval:1.0 / 30.0];
}
NSOperationQueue *currentQueue = [NSOperationQueue currentQueue];
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical
toQueue:currentQueue
withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"%#",motion.attitude);
}];
}
Related
The quaternion you get from CMAttitude seems flawed. In Unity I can get the iPhone's rotation, apply it to a 3d object, and the object will rotate as you rotate the iPhone. In Xcode it seems to be different.
To set up a quick test project, follow these steps:
In Xcode, create a new project from the iOS > Game template (this gives us a 3d object to test on).
In GameViewController.h add #import <CoreMotion/CoreMotion.h> and #property (strong, nonatomic) CMMotionManager *motionManager;
In GameViewController.m remove the animation at line:46 [ship runAction: etc.. and don't allow camera control at line:55 (not necessary)
In GameViewController.m add to bottom of ViewDidLoad
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = .1;
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error) {
CMQuaternion q = motion.attitude.quaternion;
ship.rotation = SCNVector4Make(q.x, q.y, q.z, q.w);
if(error){ NSLog(#"%#", error); }
} ];
To avoid annoyances, in Project > Targets > Project Name > Deployment Info > Device Orientation, allow only Portrait (or just lock your iPhone's rotation)
When I build this to my iPhone the 3d object (an airplane) doesn't follow the rotation of the phone. Pitching kind of works.
Is this intended? How am I using this wrong?
In 3D space there are different representations of rotations.
You are using the rotation property of SCNNode:
The four-component rotation vector specifies the direction
of the rotation axis in the first three components and the
angle of rotation (in radians) in the fourth.
But you are assigning the device rotation as quaternion.
Either assign the quaternion to the orientation property of SCNNode or use the yaw, pitch and roll angles of CMAttitude and assign these to the eulerAngles property of SCNNode:
The node’s orientation, expressed as pitch, yaw, and roll angles,
each in radians.
I am working on an iOS camera based app, in which I have to capture a first point and then I need to draw the line to the current focus point to the first captured point. MagicPlan works this way.
Here is an image:
I have tried to fix a point for first point using accelerometer values and the tilted angle of the device. But, no luck so far. And how would i draw the line to the second point from the first point?
This is the code that i have tried so far:
if (self.motionManager.deviceMotionAvailable)
{
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error) {
CATransform3D transform;
transform = CATransform3DMakeRotation(motion.attitude.pitch, 1, 0, 0);
transform = CATransform3DRotate(transform,motion.attitude.roll, 0, 1, 0);
transform = CATransform3DRotate(transform,motion.attitude.yaw, 0, 0, 1);
self.viewObject.layer.transform = transform;
}];
}
if (self.motionManager.deviceMotionActive)
{
/**
* Pulling gravity values from deviceMotion sensor
*/
CGFloat x = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.x];
CGFloat y = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.y];
CGFloat z = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.z];
CGFloat r = sqrtf(x*x + y*y + z*z);
/**
* Calculating device forward/backward title angle in degrees
*/
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f;
[self.lblTilForwardBackward setText:[#(tiltForwardBackward) stringValue]];
}
You have a lot of issues to resolve here. It isn't just a matter of adjusting for camera orientation as the height that the camera is being held at and position of the camera in the room are also changing. Even in MagicPlan, when the person turns around, the camera moves (rotates about the axis going through the person's head down to his feet).
There is quite a lot of algebra and rotation/translation matrix operations to work out. No one is going to do this for you. You'll have to figure it all out and derive it yourself (or look it up from old graphics text books).
I suggest doing something as straight forward and multi-step as possible (so you can debug each step along the way). Assume flat ground (indoor environment).
Get camera position/orientation/focal length from the first snapshot.
Figure out the touch point in real world Cartesian coordinates(start with video coordinates and translate via roll/pitch/yaw and ray traced projection to ground plane(using camera height).
From the focal length you can figure out the field of view and depth to center of field of view and using camera orientation and click distance from center of screen determine xyz offset from some origin (your feet maybe).
Determine and track camera position and orientation relative to that origin.
On second snapshot (or motion awake), figure out (center or touched point) distance from origin and exact xyz (as above).
Once you have those two points in xyz you can plot the line by taking the standard orthogonal projection onto the view plane. Clipping as needed in case original point is out of the FOV.
I receive the RotationMatrix from my CMMotionManager and want to calculate my phone's heading from it, e.g. 38 degrees North (and avoid CLLocationManager), in order to make a more accurate compass:
[motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical toQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *deviceMotion, NSError *error) {
CMRotationMatrix rm = deviceMotion.attitude.rotationMatrix;
// Get the heading.
motionHeading = M_PI + atan2(rm.m22, rm.m12);
motionHeading = motionHeading*180/M_PI;
However this only works when the phone is flat on the table. What should i use as formula to allow for all phone's positions in my hand, being in landscape mode, and the device looking above or under the horizon ?Thanks.
I finally used this answer that works pretty well in terms of movement stability.
I'm using coreMotion in my app to detect a motion of iOS device. I know how to get the different sensor's data from coreMotion. I am getting roll, pitch, and yaw data as follows from the deviceMotion:
float pitch = (180/M_PI)*self.manager.deviceMotion.attitude.pitch];
float roll = (180/M_PI)*self.manager.deviceMotion.attitude.roll];
float yaw = (180/M_PI)*self.manager.deviceMotion.attitude.yaw];
How can I use these data to detect a motion? What are the calculations that I need to do for this?
The attitude values need the device motion updates to be started as:
startDevicemotionUpdates
To monitor attitude values when device moves, use the attitude values to be displayed in labels:
[self.cmMotionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *devMotion, NSError *error) {
float pitch = (180/M_PI)*self.manager.devMotion.attitude.pitch];
float roll = (180/M_PI)*self.manager.devMotion.attitude.roll];
float yaw = (180/M_PI)*self.manager.devMotion.attitude.yaw];
self.rollLabel.text = [NSString stringWithFormat:#"%f", roll];
self.pitchLabel.text = [NSString stringWithFormat:#"%f",pitch];
self.yawLabel.text = [NSString stringWithFormat:#"%f",yaw];
}
Also it is better not to use roll, pitch and yaw(aka Euler Angles). Use Quaternion or Rotation matrices for better accuracy. Euler angles have the tendency to be in a situation called gimbal lock which results in unreliable data.
Please check this link for how to use quaternion, and convert that value to the roll, pitch and yaw values: SO link
I would like to know how many degrees is the iPhone leaning when being held upright - that is, if I point the iPhone up, I would like to know how many degrees it is looking up at? I think that I can use the gyroscope and core motion for this, and I think that it is pitch that I want, however, the pitch is a "rate of speed" in radians per second. I am not really interested in how quickly a user moved the iPhone, I am just interested in the leaning angle. So if I am pointing a phone up to take a picture, I would like to know the angle of pointing up - any ideas??
Thank you.
Gyro data is in radians per second, but what you are looking for is CMMotionManager.attitude property. It shows the attitude of the object in radians relative to a some frame of reference.
create class variable motionManager and init:
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.1f;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
[self processMotion:motion];
}];
process the updates, you are looking for pitch, but in this sample it will show you all three values so you can play around and decide what you need:
-(void)processMotion:(CMDeviceMotion*)motion {
NSLog(#"Roll: %.2f Pitch: %.2f Yaw: %.2f", motion.attitude.roll, motion.attitude.pitch, motion.attitude.yaw);
}
these are Euler Angles, you also have an option to get rotationMatrix, or quaternion format. Each with their own advantages and disadvantages.