gsensor 电动牙刷2d和3d的区别别

后使用快捷导航没有帐号?
查看: 1787|回复: 3
Lis3dh这个IC的Gsensor太灵敏问题
大家好,我们这边在MT6577上调试Lis3dh这个IC的Gsensor,现在有这样的问题:gsensor太灵敏,在camera预览界面,此时马达震动或者手轻微抖动,OSD界面都会旋转,
请问大家碰到过没?怎么解决的?
旋转90度还是轻微旋转?
就是手机没有旋转,只是抖动了,但是预览界面上的osd就旋转了,就这样来回旋转。
davadpdw 发表于
就是手机没有旋转,只是抖动了,但是预览界面上的osd就旋转了,就这样来回旋转。
摄像头的osd 本来就是跟着手机转的呀, 应该是正常的。
你可以在设置界面转屏试下,看下灵敏度高不高。
太高的话可以把延时设置大一些。
论坛资料为网友自由上传,与本论坛无关。
Powered by
关注一牛微信我要问数码:2.5D玻璃是什么? - 我爱研发网
- R&D大本营
& 我要问数码:2.5D玻璃是什么?
我要问数码:2.5D玻璃是什么?
 日 腾讯数码            参与:2人 
  问:什么是2.5D玻璃?它有什么好的地方,让很多手机厂商都在使用它?
  答:从诺基亚N9开始,一个关于手机屏幕的词语逐渐被人们所熟知,它叫“2.5D玻璃”,这些年越来越多的手机开始使用这种边缘是弧形的玻璃作为屏幕盖板,从全球封卖的iPhone 6,到三星的Note 4,再到国产的像vivo、小米这样的厂商都在使用2.5D玻璃。可以说现阶段,2.5D玻璃是很多手机厂商的第一选择。
  那么很多人可能都会有这样的疑问,到底什么是2.5D玻璃,而它又好在哪?
  什么是2.5D玻璃?
  首先来让我们弄明白一个问题,什么是2.5D玻璃。我们传统意义上的玻璃是平的,无论你选择玻璃上任何一个点,它都应该是和玻璃上其他的点在同一个平面上。而2.5D玻璃和普通玻璃最大的区别在于这种玻璃尽管正面也是平的,但是在边缘的部分会向下凹陷成一个弧形,从下面这张图中你能很明显的发现两者的区别。
  和3D玻璃不同,2.5D最大的区别在于整个覆盖液晶面板的部分是平的,这样的好处是不会造成视觉差,你可以试着投过一个球型玻璃看手机屏幕,你会发现你必须垂直于屏幕才能看到正常图像,否则会变形。2.5D玻璃很好的解决了这个问题,覆盖在液晶面板上面的部分是平的,不会在侧着看屏幕的时候造成图像变形。
  那你是不是要问了,既然上面是平的,那四周干嘛吃饱了撑的非要做成弧形呢?别急,往下看。
  2.5D玻璃的好处
  为什么2.5D玻璃这么受欢迎,首要的原因是“颜值高”。以前用平面玻璃的问题是为了增强玻璃的扛撞击性,通常厂商的做法是将这块玻璃镶嵌在中框内,这样带来的问题是整块玻璃是低于中框的,加上屏幕的外边框,从视觉上看一款手机就会在屏幕边框部分被分为很多层,看上去相当凌乱。
  而2.5D玻璃的弧面边缘有效的解决了这个问题,2.5D玻璃的高度比中框高,因此整个屏幕好像是架在中框上的,看上去相当的饱满,而且也看不到所谓的外边框,从视觉上要比平面玻璃好很多。
  第二个好处是触感。2.5D玻璃几乎和中框是成180度对接的,因此在屏幕的边缘不想之前的手机是90度的,这样用手指摸上去的感觉并不会那么硌手。同时试想一下,当你单手持握手机的时候,手掌虎口部分和边框长时间接触,是有棱角的屏幕好还是没有棱角的屏幕好?答案显然是后者。
  2.5D玻璃目前所面临的一些小问题
  当然,尽管目前有很多手机都是使用2.5D玻璃作为屏幕的盖板,但是实际上还是有更多的手机使用传统的平面玻璃。为什么?原因很简单,2.5D玻璃的工艺相比平面玻璃更复杂,因此从成本角度讲,2.5D玻璃要比普通玻璃贵上一些。根据之前的数据显示,一块相同大小的2.5D玻璃,实际的成本价是一块普通玻璃的2-3倍。
  有很多人担心2.5D玻璃悬空在金属中框上对于屏幕的坚硬程度会不会有影响,实际上目前没有任何的数据可以证明2.5D玻璃要比普通玻璃更易碎,像康宁家的大猩猩三代和四代玻璃都有2.5D的版本出现,坚硬程度和普通平面玻璃无异。
  不过由于2.5D玻璃的成本要更高,因此如果你的手机屏幕碎裂,那么更换屏幕的价格肯定会更贵,加之目前手机的屏幕都是贴合设计的,因此屏幕一碎,很有可能你要支付超过手机一半的价格来更换它们。
  还有一个不算问题的问题。2.5D玻璃因为边缘是弧面的,因此配备2.5D玻璃的手机是不能好好贴膜的,你只能买个膜贴在屏幕平整的地方,比如上面这张图……
&&微博关注:&&微信关注:admin_52RD
已有0位网友发表了看法 
新闻导航 Navigation
1611111110109950332520121211843893767232423042281221918931807
精彩评论 Comment
52RD网友:台湾人小气抠门,怪不得别人跳槽。台湾的大公司其实效率还是蛮低的,一点屁事跑完流程黄花菜都凉了,另外大陆员工基本有天花板,不敢任用…52RD网友:从配置上来说,比华强北的512+4,200+500,Q屏上了一个档次,所以比华强北的200多的价格应该要高些,考虑额外加了红外遥控,硬件和软件成…52RD网友:是不是通信行业猝死的人最多啊,某些无良公司把行业都搞坏了。用命换来这样的产品值么?电子产品更新换代那么快,转眼就没有痕迹,可是自…52RD网友:张斌是我从前的老同事,在一起共事的时间就勤勤恳恳对工作认真负责,是个特别认真的手机设计人才,后来去了闻泰,加班变本加厉地压在工程…wetblanket:指纹sensor如果仅立足于跟着三星和apple走,对于手机只能是过客。关键是对于多平台的软件应用才是发展的机遇。目前指纹sensor和gsensor发…
特别推荐 Recommend5700人阅读
Unity 3D(10)
让世界旋转起来:
  游戏世界的主摄像机就是我们在游戏里的“眼睛”,为了让“眼睛”能够与手持 iPad 的我们保持协调,跟随着我们自己转身而转动,好像我们自己就站在游戏世界里用自己的眼睛观察游戏世界一样,这里就需要解决几个数学问题。
  既然要旋转主摄像机,那么首先需要找到旋转的参照,因为我们旋转主摄像机的目的是为了观察游戏世界,那么这个参照自然选取游戏世界坐标系。主摄像机的 +Z 轴朝向就是我们通过 iPad 屏幕观察游戏世界时“眼睛”的观察方向,因此,只要我们能够找到主摄像机 +Z 轴方向向量与游戏世界坐标系下某个固定方向向量之间的角度关系,我们就可以通过 Quaternion(四元数,Unity 3D 里用四元数来描述空间旋转)来将主摄像机旋转到我们所期望的任意方位。
先有重力:
  上一节里说到重力感应的方向分量,iPad 接受到的重力输入是永远指向真实世界重力方向的,那么,假如我们创造的是一个正常的游戏世界的话,重力输入的方向其实就是游戏世界的 -Y 坐标轴方向,那么其反方向就是 +Y 轴方向。顺理成章,我们旋转主摄像机所需要的“游戏世界坐标系下某个固定方向向量”就是这个重力输入方向了。但是在计算主摄像机 +Z 轴方向与重力输入方向之间角度关系之前,还需要一个小换算,因为重力输入方向是以 iPad 屏幕坐标系度量的,而 iPad 屏幕坐标系与主摄像机自身坐标系之间 Z 轴是反向的,所以需要进行一次坐标变换,将重力输入方向向量从
iPad 屏幕坐标系变换到主摄像机坐标系。变换方法很简单:
Vector3 g = new Vector3(Input.acceleration.x, Input.acceleration.y, -Input.acceleration.z);
  现在我们可以计算主摄像机 +Z 轴方向向量(0, 0, 1)与 g 之间的夹角,进而计算出主摄像机 +Z 轴方向向量变换到游戏世界坐标系下时的方向向量。这一扒拉计算需要用到球坐标系,寻找球坐标系坐标(r, θ, φ)对应直角坐标系下坐标点的步骤为:从原点出发,向 +Z 方向前进 r,然后依据右手定则,大拇指指向 +Y 轴方向,向其他四指的握转方向旋转&θ 角度,再依据右手定则,大拇指指向 +Z 轴方向,向其他四指握转方向旋转&φ 角度。如下图:
主摄像机 +Z 轴方向向量与 g 的夹角就是 π-θ,换言之 θ=π-夹角。现在这里出现了一个问题:θ 是知道了,那&φ 在哪?非常遗憾,如果我们用来定位的依据只有重力输入方向这一项数据的话,无法确定 φ。其实 θ 就是“天顶角”,也就是俯仰角,φ 是“方位角”,重力方向只能帮助我们确定俯仰角,方位角它是无能为力的,这需要借助罗盘或者陀螺仪。这在后文中会进一步解说,现在我们只需要先给 φ 定一个定值即可,比如 240度。
  仔细观察上图中的坐标系,又会发现一个问题:这个坐标系与我们使用的游戏世界坐标系长的不一样啊?没错,这个坐标系的 +Z 轴是游戏世界坐标系的 +Y 轴,它的 +Y 轴是游戏世界坐标系的 +Z 轴,刚好 +Z 和 +Y 轴调了一个个儿。没有关系,只需要将 x,y,z 的计算公式调整一下就行:
x = r sinθ cosφ
y = r cosθ
z = r sinθ sinφ
&代码也很简单:
using UnityE
using System.C
public class GSensorContoller : MonoBehaviour
private Transform myTransform =
private float x = 0.0f;
private float y = 0.0f;
private float z = 0.0f;
private Vector3
private float theta = 0.0f;
private float phi = 0.0f;
void Awake()
myTransform =
// Use this for initialization
void Start()
// Update is called once per frame
void Update()
Vector3 g = new Vector3(Input.acceleration.x, Input.acceleration.y, -Input.acceleration.z);
theta = Mathf.PI - Mathf.PI * Vector3.Angle(g, Vector3.forward) / 180.0f;
phi = Mathf.PI * 240.0f / 180.0f;
x = Mathf.Sin(theta) * Mathf.Cos(phi);
y = Mathf.Cos(theta);
z = Mathf.Sin(theta) * Mathf.Sin(phi);
Quaternion targetRotation = Quaternion.LookRotation(new Vector3(x, y, z), Vector3.up);
myTransform.rotation = targetR
void OnGUI()
GUI.Box(new Rect(5, 5, 500, 25), String.Format(&G
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, g.x, g.y, g.z));
GUI.Box(new Rect(5, 35, 500, 25), String.Format(&theta:{0:0.000},phi:{1:0.000}&, theta, phi));
GUI.Box(new Rect(5, 65, 500, 25), String.Format(&Camera face
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, x, y, z));
GUI.Box(new Rect(5, 95, 500, 25), String.Format(&Camera rotate x:{0:0.000},y:{1:0.000},z:{2:0.000}&,
myTransform.rotation.eulerAngles.x, myTransform.rotation.eulerAngles.y, myTransform.rotation.eulerAngles.z));
在这段代码的作用下,已经能够让游戏里的主摄像机响应 iPad 的方位变化,虽然只是俯仰方向。但是在折腾 iPad 的时候会发现,只有抬起和放下 iPad 让俯仰角发生变化时,游戏画面会有响应,而像手握方向盘那样旋转 iPad 时,游戏画面保持不变,这看上去就比较怪异。我们期望的是:像手握方向盘那样旋转 iPad 时,画面向反方向旋转,不管 iPad 转成什么样,画面里的游戏世界始终与手持 iPad 的人保持相对静止。这一点,即便是只有重力输入,也能做到,只需要在 iPad 旋转时,将主摄像机向反方向绕
Z 轴旋转即可,空间中绕 Z 轴的旋转被称为 Roll,所以——
Let's ROLL:
  首先需要知道的是 iPad 到底旋转了多少角度,这同样需要找一个相对固定不变的参照。我们同样可以利用重力输入方向向量,只不过这一次需要的不只是一个向量,而是重力输入向量与屏幕坐标系 Z 轴形成的平面。因为这里处理的情况是 iPad 绕屏幕坐标系的 Z 轴旋转,因此 Z 轴方向和重力输入方向都可以认为是恒定不变的,也就是说由这两个方向向量决定的平面 GOZ 是不变的。那么 iPad 旋转的角度就是屏幕坐标系 Z 轴方向向量和 Y 轴方向向量决定的平面 YOZ 与参照平面 GOZ 的夹角。
  然后我们需要做的就是数学。希望看到这里的时候你还能够回想起高中和大学学过的空间解释几何。两个平面之间的夹角大小等于两个平面法向量之间的夹角大小。一个直角坐标方程为 ax + by + cz + d = 0 的平面,其法向量之一就是 (a, b, c)。经过不共线三点 (x0, y0, z0) (x1, y1. z1) (x2, y2. z2) 的平面的直角坐标方程可以通过下图的行列式求得:
至于如何求两向量的夹角,可以利用 Unity 3D 提供的函数:Vector3.Angle():
static&function&Angle (from : Vector3, to : Vector3) :&float
Description
Returns the angle in degrees between&from&and&to.
也可以自己用下图的公式求解:
平面 GOZ 可以认为它经过三个点 Z(0, 0, 1),O(0, 0, 0),G(x1, y1, z1),通过行列式计算得到平面的直角坐标方程 y1x - x1y = 0,其法向量为 (y1, -x1, 0);平面 YOZ 可以认为它经过三个点 Z(0, 0, 1),O(0, 0, 0),Y(0, 1, 0),通过行列式计算得到平面的直角坐标方程 x = 0,其法向量为 (1, 0, 0)。进一步算得 cosα = y1 / sqrt(y1 * y1 + (-x1) * (-x1))。这里 α 的取值范围是
[0, π],而我们求两个平面的夹角取值范围应该是 [0, π/2],所以我们想要的是 α = arccos(|y1 / sqrt(y1*y1 + x1 * x1)|)。接下来转换为代码:
using UnityE
using System.C
public class GSensorContoller : MonoBehaviour
private Transform myTransform =
private float x = 0.0f;
private float y = 0.0f;
private float z = 0.0f;
private Vector3
private float theta = 0.0f;
private float phi = 0.0f;
private float turnAngle = 0.0f;
void Awake()
myTransform =
// Use this for initialization
void Start()
// Update is called once per frame
void Update()
Vector3 g = new Vector3(Input.acceleration.x, Input.acceleration.y, -Input.acceleration.z);
theta = Mathf.PI - Mathf.PI * Vector3.Angle(g, Vector3.forward) / 180.0f;
phi = Mathf.PI * 240.0f / 180.0f;
x = Mathf.Sin(theta) * Mathf.Cos(phi);
y = Mathf.Cos(theta);
z = Mathf.Sin(theta) * Mathf.Sin(phi);
Quaternion targetRotation = Quaternion.LookRotation(new Vector3(x, y, z), Vector3.up);
float temp = Mathf.Sqrt(g.x * g.x + g.y * g.y);
if (temp != 0) turnAngle = Mathf.Acos(Mathf.Abs(g.y) / temp);
else turnAngle = 0.0f;
Quaternion targetTurn = Quaternion.AngleAxis(- turnAngle, Vector3.forward);
myTransform.rotation = targetRotation * targetT
void OnGUI()
GUI.Box(new Rect(5, 5, 500, 25), String.Format(&G
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, g.x, g.y, g.z));
GUI.Box(new Rect(5, 35, 500, 25), String.Format(&theta:{0:0.000},phi:{1:0.000},turn:{2:0.000}&, theta, phi, turnAngle));
GUI.Box(new Rect(5, 65, 500, 25), String.Format(&Camera face
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, x, y, z));
GUI.Box(new Rect(5, 95, 500, 25), String.Format(&Camera rotate x:{0:0.000},y:{1:0.000},z:{2:0.000}&,
myTransform.rotation.eulerAngles.x, myTransform.rotation.eulerAngles.y, myTransform.rotation.eulerAngles.z));
上面这段代码当我们手持 iPad 在屏幕坐标系 +X 轴一侧旋转时一切正常,但到了 -X 轴一侧时却出现了问题——摄像机转向反了。这是因为主摄像机的转向也是有方向的,虽然在 -X 侧也是 GOZ 面和 YOZ 面夹角增大,但转向却与 +X 侧是相反的。要解决这个问题有多种办法,可以在计算两平面夹角时采用转角而不是夹角,更简单的办法是在 -X 侧和 +X 侧时分别对角度的正负做修正。代码调整如下:
using UnityE
using System.C
public class GSensorContoller : MonoBehaviour
private Transform myTransform =
private float x = 0.0f;
private float y = 0.0f;
private float z = 0.0f;
private Vector3
private float theta = 0.0f;
private float phi = 0.0f;
private float turnAngle = 0.0f;
void Awake()
myTransform =
// Use this for initialization
void Start()
// Update is called once per frame
void Update()
Vector3 g = new Vector3(Input.acceleration.x, Input.acceleration.y, -Input.acceleration.z);
theta = Mathf.PI - Mathf.PI * Vector3.Angle(g, Vector3.forward) / 180.0f;
phi = Mathf.PI * 240.0f / 180.0f;
x = Mathf.Sin(theta) * Mathf.Cos(phi);
y = Mathf.Cos(theta);
z = Mathf.Sin(theta) * Mathf.Sin(phi);
Quaternion targetRotation = Quaternion.LookRotation(new Vector3(x, y, z), Vector3.up);
float temp = Mathf.Sqrt(g.x * g.x + g.y * g.y);
if (temp != 0) turnAngle = Mathf.Acos(Mathf.Abs(g.y) / temp);
else turnAngle = 0.0f;
turnAngle = turnAngle * 180.0f / Mathf.PI * (g.x & 0.0f ? 1.0f : -1.0f);
Quaternion targetTurn = Quaternion.AngleAxis(- turnAngle, Vector3.forward);
myTransform.rotation = targetRotation * targetT
void OnGUI()
GUI.Box(new Rect(5, 5, 500, 25), String.Format(&G
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, g.x, g.y, g.z));
GUI.Box(new Rect(5, 35, 500, 25), String.Format(&theta:{0:0.000},phi:{1:0.000},turn:{2:0.000}&, theta, phi, turnAngle));
GUI.Box(new Rect(5, 65, 500, 25), String.Format(&Camera face
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, x, y, z));
GUI.Box(new Rect(5, 95, 500, 25), String.Format(&Camera rotate x:{0:0.000},y:{1:0.000},z:{2:0.000}&,
myTransform.rotation.eulerAngles.x, myTransform.rotation.eulerAngles.y, myTransform.rotation.eulerAngles.z));
转向基本上正确了,但是画面抖动得很厉害。仔细观察重力输入的数值可以发现,数值在小数点后第二位之后很不稳定,即便是将 iPad 静置数值的变化也不会停止,这就导致后续计算的转角也频繁发生变化,摄像机抖动。要解决这个问题可以从两个方向出发,第一是降低对重力输入取值的精度,只有当重力输入数值该变量达到一定程度时才更新重力方向数值;第二是在摄像机旋转过程中加入线性插值,减缓摄像机旋转的速度,虽然这样会导致摄像机的反应延时,但同时能平稳摄像机的旋转。这两种方法互补干扰,可以同时采用:
using UnityE
using System.C
public class GSensorContoller : MonoBehaviour
public float RotateSpeed = 1.0f;
private Transform myTransform =
private float x = 0.0f;
private float y = 0.0f;
private float z = 0.0f;
private Vector3
private float theta = 0.0f;
private float phi = 0.0f;
private float turnAngle = 0.0f;
void Awake()
myTransform =
// Use this for initialization
void Start()
// Update is called once per frame
void Update()
Vector3 g1 = new Vector3(Input.acceleration.x, Input.acceleration.y, -Input.acceleration.z);
if (Vector3.Angle(g, g1) & 1.0f) g = g1;
theta = Mathf.PI - Mathf.PI * Vector3.Angle(g, Vector3.forward) / 180.0f;
phi = Mathf.PI * 240.0f / 180.0f;
x = Mathf.Sin(theta) * Mathf.Cos(phi);
y = Mathf.Cos(theta);
z = Mathf.Sin(theta) * Mathf.Sin(phi);
Quaternion targetRotation = Quaternion.LookRotation(new Vector3(x, y, z), Vector3.up);
float temp = Mathf.Sqrt(g.x * g.x + g.y * g.y);
if (temp != 0) turnAngle = Mathf.Acos(- g.y / temp);
else turnAngle = 0.0f;
turnAngle = turnAngle * 180.0f / Mathf.PI * (g.x & 0.0f ? 1.0f : -1.0f);
Quaternion targetTurn = Quaternion.AngleAxis(- turnAngle, Vector3.forward);
myTransform.rotation = Quaternion.Slerp(myTransform.rotation, targetRotation * targetTurn, Time.deltaTime * rotateSpeed);
void OnGUI()
GUI.Box(new Rect(5, 5, 500, 25), String.Format(&G
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, g.x, g.y, g.z));
GUI.Box(new Rect(5, 35, 500, 25), String.Format(&theta:{0:0.000},phi:{1:0.000},turn:{2:0.000}&, theta, phi, turnAngle));
GUI.Box(new Rect(5, 65, 500, 25), String.Format(&Camera face
x:{0:0.000},y:{1:0.000},z:{2:0.000}&, x, y, z));
GUI.Box(new Rect(5, 95, 500, 25), String.Format(&Camera rotate x:{0:0.000},y:{1:0.000},z:{2:0.000}&,
myTransform.rotation.eulerAngles.x, myTransform.rotation.eulerAngles.y, myTransform.rotation.eulerAngles.z));
上面代码中判断当两次重力输入方向的夹角超过 1 度时才更新重力输入数据,而且通过球形插值来平滑摄像机的转动。现在感觉就好多了,虽然当 iPad 转过天顶和脚底这两个特殊位置时摄像机的旋转与预想的有出入,但基本上达成了我们的目的。更细致的调整可以放在加入了罗盘数据输入之后由罗盘数据替代重力感应数据完成转角的计算。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:56657次
排名:千里之外
原创:15篇
评论:15条
(1)(8)(1)(1)(4)1269人阅读
android技术(127)
gsensor(7)
/bpasser/archive//2214517.html
带有g-sensor的Android设备上可通过API获取到设备的运动加速度,应用程序通过一些假设和运算,可以从加速度计算出设备的方向
获取设备运动加速度的基本代码是:
SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sm.registerListener(new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) {
float[] values = event.
float ax = values[0];
float ay = values[1];
float az = values[2];
// TODO Have fun with the acceleration components...
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
SendorEventListener 通过 SendorEvent 回调参数获得当前设备在坐标系x、y、z轴上的加速度分量。SensorEvent 的 api doc 中定义了这里使用的坐标系为:
我暂且称之为“设备坐标系”吧,设备坐标系是固定于设备的,与设备的方向(在世界坐标系中的朝向)无关
精确地说,Sensor Event 所提供的加速度数值,是设备以地球为参照物的加速度减去重力加速度的叠加后的值。我是这样理解的:当以重力加速度g向地面作自由落体运动时,手机处于失重状态,g-sensor以这种状态作为加速度的0;而当手机处于静止状态(相对于地面)时,为了抵御自由落体运动的趋势,它有一个反向(向上)的g的加速度。因此,得出一个结论:当设备处于静止或者匀速运动状态时,它有一个垂直地面向上的g的加速度,这个g投影到设备坐标系的x、y、z轴上,就是SensorEvent 提供给我们的3个分量的数值。在“设备处于静止或者匀速运动状态”的假设的前提下,可以根据SensorEvent所提供的3个加速度分量计算出设备相对于地面的方向
前面所提到的“设备的方向”是一个含糊的说法。这里我们精确地描述设备方向为:以垂直于地面的方向为正方向,用设备坐标系x、y、z轴与正方向轴之间的夹角Ax、Ay、Az来描述设备的方向,如下图所示。可以看出,设备还有一个自由度,即:绕着正方向轴旋转,Ax、Ay、Az不变。但Ax、Ay、Az的约束条件,对于描述设备相对于正方向轴的相对位置已经足够了。如果需要完全约束设备相对于地面的位置,除了正方向轴外,还需要引入另一个参照轴,例如连接地球南、北极的地轴(如果设备上有地磁强度Sensor,则可满足该约束条件)
Ax、Ay、Az的范围为[0, 2*PI)。例如,当Ay=0时,手机y轴竖直向上;Ay=PI时,手机y轴向下;Ay=PI/2时,手机水平、屏幕向上;Ay=3*PI/2时,手机水平、屏幕向下
根据3D矢量代数的法则,可知:
Gx=g*cos(Ax)Gy=g*cos(Ay)Gz=g*cos(Az)g^2=Gz^2+Gy^2+Gz^2
因此,根据Gx、Gy、Gz,可以计算出Ax、Ay、Az
在x-y平面上的2D简化
当Ax、Ay确定时,Az有两种可能的值,二者相差PI,确定了设备屏幕的朝向是向上还是向下。大多数情况下,我们只关心Ax、Ay(因为程序UI位于x-y平面?),而忽略Az,例如,Android的屏幕自动旋转功能,不管使用者是低着头看屏幕(屏幕朝上)、还是躺在床上看(屏幕朝下),UI始终是底边最接近地心的方向
那么我们设Gx与Gy的矢量和为g'(即:g在x-y平面上的投影),将计算简化到x-y 2D平面上。记y轴相对于g'的偏角为A,以A来描述设备的方向。以逆时针方向为正,A的范围为[0, 2*PI)
g'^2=Gx^2+Gy^2Gy=g'*cos(A)Gx=g'*sin(A)
g'=sqrt(Gx^2+Gy^2)A=arccos(Gy/g')
由于arccos函数值范围为[0, PI];而A&PI时,Gx=g'*sin(A)&0,因此,根据Gx的符号分别求A的值为:
当Gx&=0时,A=arccos(Gy/g')当Gx&0时,A=2*PI-arccos(Gy/g')
注意:由于cos函数曲线关于直线x=n*PI 对称,因此arccos函数的曲线如果在y轴方向[0, 2*PI]范围内补全的话,则关于直线y=PI对称,因此有上面当Gx&0时的算法
考虑应用程序的屏幕旋转
前面计算出了Android设备的“物理屏幕”相对于地面的旋转角度,而应用程序的UI又相对于“物理屏幕”存在0、90、180、270度4种可能的旋转角度,要综合考虑进来。也就是说:
UI相对于地面的旋转角度=物理屏幕相对于地面的旋转角度-UI相对于物理屏幕的旋转角度
Android应用获取屏幕旋转角度的方法为:
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degree= 90 *
float rad = (float)Math.PI / 2 *
根据上面的算法,我写了一个“不倒翁”的Demo,当设备旋转时,不倒翁始终是站立的。软件市场上不少“水平尺”一类的应用,其实现原理应该是与此相同的
Activity实现了SensorEventListener,并且注册到SensorManager。同时设置屏幕方向固定为LANDSCAPE:
private GSensitiveView gsV
private SensorM
public void onCreate(Bundle savedInstanceState) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
super.onCreate(savedInstanceState);
gsView = new GSensitiveView(this);
setContentView(gsView);
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
protected void onDestroy() {
sm.unregisterListener(this);
super.onDestroy();
当g-sensor数据变化时的回调如下。这里就是根据我们前面推论的算法计算出UI旋转的角度,并且调用GSensitiveView.setRotation()方法通知View更新
public void onSensorChanged(SensorEvent event) {
if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) {
float[] values = event.
float ax = values[0];
float ay = values[1];
double g = Math.sqrt(ax * ax + ay * ay);
double cos = ay /
if (cos & 1) {
} else if (cos & -1) {
double rad = Math.acos(cos);
if (ax & 0) {
rad = 2 * Math.PI -
int uiRot = getWindowManager().getDefaultDisplay().getRotation();
double uiRad = Math.PI / 2 * uiR
rad -= uiR
gsView.setRotation(rad);
GSensitiveView是扩展ImageView的自定义类,主要是根据旋转角度绘制图片:
private static class GSensitiveView extends ImageView {
private double
public GSensitiveView(Context context) {
super(context);
BitmapDrawable drawble = (BitmapDrawable) context.getResources().getDrawable(R.drawable.budaow);
image = drawble.getBitmap();
paint = new Paint();
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
double w = image.getWidth();
double h = image.getHeight();
Rect rect = new Rect();
getDrawingRect(rect);
int degrees = (int) (180 * rotation / Math.PI);
canvas.rotate(degrees, rect.width() / 2, rect.height() / 2);
canvas.drawBitmap(image, //
(float) ((rect.width() - w) / 2),//
(float) ((rect.height() - h) / 2),//
public void setRotation(double rad) {
rotation =
invalidate();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2562816次
积分:22073
积分:22073
排名:第246名
原创:370篇
转载:104篇
评论:291条
(2)(6)(1)(2)(1)(1)(3)(1)(1)(3)(5)(4)(4)(5)(1)(4)(10)(5)(3)(8)(2)(2)(6)(4)(9)(5)(5)(16)(6)(20)(24)(18)(6)(8)(5)(1)(15)(4)(19)(35)(35)(21)(18)(21)(7)(2)(21)(31)(38)}

我要回帖

更多关于 高德地图2d和3d区别 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信