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.
Related
Based on device movement i need to set the frames to the view. I had tried using core motion, there I'm getting x, y, z values, but unable to get device moved distance based on those coordinates.
[self.manager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
xValue = motion.userAcceleration.x;
yValue = motion.userAcceleration.y;
zValue = motion.userAcceleration.z;
}];
What happens is you can try to calculate this distance but you'll have too much error, see this video.
You didn't state what you're really trying to do, so if you trying to create the parallax effect you can use UIInterpolatingMotionEffect.
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.
In my app I need to display a horizontal line on the screen, so I've decided to turn to CMMotionManager. Here's my code, it's quite straightforward:
if ([_motionManager isDeviceMotionAvailable]) {
if ([_motionManager isDeviceMotionActive] == NO) {
[_motionManager setDeviceMotionUpdateInterval:0.1];
[_motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXTrueNorthZVertical toQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error){
CMQuaternion quat = _motionManager.deviceMotion.attitude.quaternion;
double yaw = asin(2*(quat.x*quat.z - quat.w*quat.y));
// drawing line stuff.
}];
}
} else {
NSLog(#"Motion not avaliable!");;
}
Eveything works fine when I put my phone in upward portrait mode. But since the asin method gives me only a number in range of (-pi/2,+pi/2), I can't draw a perfectly horizontal line if I hold my phone in landscape mode, or upside-down portrait mode, the line just stuck at angle of around -pi/2 or pi/2, and won't keep rotating.
So, how can I fix this, is there any more general way to handle quaternion? Thanks in advance!
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);
}];
}
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.