xcode 如何创建新的 CMAttitude 参考系以使重力位于 Y 轴上
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8663507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create a new CMAttitude Reference Frame to make the gravity be on the Y axis
提问by Pochi
I want to be able to change the Device Motion Manager Reference frame (for the gyro) so that I have my gravity vector on the Y axis.
我希望能够更改设备运动管理器参考系(用于陀螺仪),以便我的重力矢量位于 Y 轴上。
Usually when you start Device Motion Manager Updates you will only have the z axis of the phone aligned with gravity.
通常,当您启动 Device Motion Manager Updates 时,您只会让手机的 z 轴与重力对齐。
You can change this to use the magnetometer to make the x axis be aligned with either the magnetic or true north pole. With this I have my X axis Pointing north and my Z axis pointing down.
您可以更改此设置以使用磁力计使 x 轴与磁北极或真北极对齐。有了这个,我的 X 轴指向北方,Z 轴指向下方。
What I want to do is have my Y axis (negative) pointing down (so that its aligned with the gravity) and also have my X axis pointing the true magnetic pole.
我想要做的是让我的 Y 轴(负)指向下方(使其与重力对齐),并使我的 X 轴指向真正的磁极。
The result I want is such that when have my phone standing still in a vertical (portrait) orientation the right of the phone will be aligned to the north pole and all my readings (roll, pitch, yaw) will read as 0. Then with this if i rotate my phone on the X axis the pitch will change, and if I rotate around the Y axis the yaw will change.
我想要的结果是,当我的手机以垂直(纵向)方向静止不动时,手机的右侧将与北极对齐,并且我的所有读数(滚动、俯仰、偏航)都将读取为 0。然后使用如果我在 X 轴上旋转手机,俯仰会改变,如果我绕 Y 轴旋转,偏航会改变。
So far I know i can set my own reference frame if I multiply by the inverse of the attitude a previously stored attitude, (like i could set my phone in this orientation MANUALLY, save that attitude and simply keep multiplying the new attitude by the inverse of this stored one and all my readings will be exactly like the ones i want).
到目前为止,我知道我可以设置自己的参考系,如果我乘以先前存储的姿态的倒数,(就像我可以手动将手机设置在这个方向上,保存该姿态,然后继续将新姿态乘以倒数其中存储了一个,我所有的读数都将与我想要的完全一样)。
But setting it manually is not an option, so how do i make this programatically?
但是手动设置它不是一个选项,那么我如何以编程方式进行设置?
I don't think there is a function to create my own attitude reference frame, or if at least there was a function to multiply the attitude by a rotation matrix then i could probably solve this. (because i would just multiply all the attitude by a 90 degrees change in the pitch).
我不认为有一个函数可以创建我自己的姿态参考系,或者如果至少有一个函数可以将姿态乘以旋转矩阵,那么我可能可以解决这个问题。(因为我只是将所有的姿态乘以 90 度的俯仰变化)。
I hope I explained myself clearly,
我希望我解释清楚了,
I will appreciate any suggestions. thanks
我将不胜感激任何建议。谢谢
PD: These are the iPhone Orientation coordinates:
PD:这些是 iPhone 方向坐标:
回答by Orbitus007
Pseudo code:
伪代码:
- start device motion updates
- start a camera preview in the background ;)
- capture the current gravity reading from the device as a CMAcceleration... once you have gravity store it in a local variable.
- Then you have to take the 2 vectors and get the angle in between them, in this case the device gravity of (0,0,-1) and the real gravity vector...
- we then turn theta into thetaPrime... a transform that matches the CoreMotion reference orientation
- Setup a timer to animate....
- during animation get the inverse of the rotationMatrix of motionManager's deviceMotion property.
- Apply the Transformations in the correct order to reflect the device's current attitude (yaw,pitch,roll in eulerian mode or the devices quaternion rotation... 3 different ways to say the same thing basically)
- 启动设备动作更新
- 在后台启动相机预览;)
- 从设备捕获当前重力读数作为 CMAcceleration ...一旦你将重力存储在局部变量中。
- 然后你必须取 2 个向量并得到它们之间的角度,在这种情况下是 (0,0,-1) 的设备重力和真实的重力向量......
- 然后我们将 theta 转换为 thetaPrime……一个与 CoreMotion 参考方向匹配的变换
- 设置一个计时器来制作动画....
- 在动画期间获取motionManager 的deviceMotion 属性的rotationMatrix 的倒数。
- 以正确的顺序应用变换以反映设备的当前姿态(欧拉模式下的偏航、俯仰、滚动或设备四元数旋转......基本上说同一件事的 3 种不同方式)
Here is the code:
这是代码:
- (void) initMotionCapture
{
firstGravityReading = NO;
referenceAttitude = nil;
if (motionManager == nil)
{
self.motionManager = [CMMotionManager new];
}
motionManager.deviceMotionUpdateInterval = 0.01;
self.gravityTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0
target:self
selector:@selector(getFirstGravityReading)
userInfo:nil repeats:YES];
}
- (void) getFirstGravityReading
{
CMAcceleration currentGravity;
CMDeviceMotion *dm = motionManager.deviceMotion;
referenceAttitude = dm.attitude;
currentGravity = dm.gravity;
[motionManager startDeviceMotionUpdates];
if (currentGravity.x !=0 &&
currentGravity.y !=0 && currentGravity.z !=0)
{
NSLog(@"Gravity = (%f,%f,%f)",
currentGravity.x, currentGravity.y, currentGravity.z);
firstGravityReading = YES;
[gravityTimer invalidate];
self.gravityTimer = nil;
[self setupCompass];
}
}
- (void) setupCompass
{
//Draw your cube... I am using a quartz 3D perspective hack!
CATransform3D initialTransform = perspectiveTransformedLayer.sublayerTransform;
initialTransform.m34 = 1.0/-10000;
//HERE IS WHAT YOU GUYS NEED... the vector equations!
NSLog(@"Gravity = (%f,%f,%f)",
currentGravity.x, currentGravity.y, currentGravity.z);
//we have current gravity vector and our device gravity vector of (0, 0, -1)
// get the dot product
float dotProduct = currentGravity.x*0 +
currentGravity.y*0 +
currentGravity.z*-1;
float innerMagnitudeProduct = currentGravity.x*currentGravity.x +
currentGravity.y + currentGravity.y +
currentGravity.z*currentGravity.z;
float magnitudeCurrentGravity = sqrt(innerMagnitudeProduct);
float magnitudeDeviceVector = 1; //since (0,0,-1) computes to: 0*0 + 0*0 + -1*-1 = 1
thetaOffset = acos(dotProduct/(magnitudeCurrentGravity*magnitudeDeviceVector));
NSLog(@"theta(degrees) = %f", thetaOffset*180.0/M_PI);
//Now we have the device angle to the gravity vector (0,0,-1)
//We must transform these coordinates to match our
//device's attitude by transforming to theta prime
float theta_deg = thetaOffset*180.0/M_PI;
float thetaPrime_deg = -theta_deg + 90; // ThetaPrime = -Theta + 90 <==> y=mx+b
NSLog(@"thetaPrime(degrees) = %f", thetaOffset*180.0/M_PI);
deviceOffsetRotation =
CATransform3DMakeRotation((thetaPrime_deg) * M_PI / 180.0, 1, 0, 0);
initialTransform = CATransform3DConcat(deviceOffsetRotation, initialTransform);
perspectiveTransformedLayer.sublayerTransform = initialTransform;
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:1/60.0
target:self
selector:@selector(tick)
userInfo:nil
repeats:YES];
}
- (void) tick
{
CMRotationMatrix rotation;
CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
CMAttitude *attitude = deviceMotion.attitude;
if (referenceAttitude != nil)
{
[attitude multiplyByInverseOfAttitude:referenceAttitude];
}
rotation = attitude.rotationMatrix;
CATransform3D rotationalTransform = perspectiveTransformedLayer.sublayerTransform;
//inverse (or called the transpose) of the attitude.rotationalMatrix
rotationalTransform.m11 = rotation.m11;
rotationalTransform.m12 = rotation.m21;
rotationalTransform.m13 = rotation.m31;
rotationalTransform.m21 = rotation.m12;
rotationalTransform.m22 = rotation.m22;
rotationalTransform.m23 = rotation.m32;
rotationalTransform.m31 = rotation.m13;
rotationalTransform.m32 = rotation.m23;
rotationalTransform.m33 = rotation.m33;
rotationalTransform =
CATransform3DConcat(deviceOffsetRotation, rotationalTransform);
rotationalTransform =
CATransform3DConcat(rotationalTransform,
CATransform3DMakeScale(1.0, -1.0, 1.0));
perspectiveTransformedLayer.sublayerTransform = rotationalTransform;
}
回答by Nitin
You can change the reference frame used by a CMAttitude instance. To do that, cache the attitude object that contains that reference frame and pass that as the argument to multiplyByInverseOfAttitude:. The attitude argument receiving the message is changed to represent the change in attitude from that reference frame.
您可以更改 CMAttitude 实例使用的参考系。为此,缓存包含该参考系的姿态对象并将其作为参数传递给 multiplyByInverseOfAttitude:。接收消息的姿态参数被改变以表示从该参考系开始的姿态变化。
To see how this might be useful, consider a baseball game where the user rotates the device to swing. Normally, at the beginning of a pitch, the bat would be at some resting orientation. After that, the bat would be rendered at an orientation determined by how the device's attitude had changed from where it was at the start of a pitch.
要了解这如何有用,请考虑用户旋转设备进行挥杆的棒球比赛。通常,在球场开始时,球棒会处于某个静止方向。在那之后,球棒将在一个方向上被渲染,这个方向取决于设备的姿态如何从它在球场开始时的位置变化。
-(void) startPitch {
// referenceAttitude is an instance variable
referenceAttitude = [motionManager.deviceMotion.attitude retain];
}
}
- (void)drawView {
CMAttitude *currentAttitude = motionManager.deviceMotion.attitude;
[currentAttitude multiplyByInverseOfAttitude: referenceAttitude];
// render bat using currentAttitude .....
[self updateModelsWithAttitude:currentAttitude];
[renderer render];
}
}
For More Look at below link that same thing that you want.
如需更多信息,请查看下面的链接,了解您想要的相同内容。