转载请注明出处
作者:小马
M8支持重力感应, 本人研究了一把m8的重力感应, 写点小经验.
1 物理知识
重力感应的原理是利用重力加速度,而重力加速度是加速度的一种, 比如一辆汽车在水平的路面上加速行驶, 它在水平方向是有一个加速度的, 而竖直方向的加速度为0(虽然受到重力加速度,但是被抵消了),重力加速度是个常量(在某固定一地点),通常取9.8. 这个加速度是由地球重力产生的。一个自由落体的物体(理论环境),它的加速度为9.8.等于重力常量, 而在斜坡上下落的物体(假如不考虑摩擦力, 在手机应用中也是如此),它的加速度要小于9.8(只是9.8的一个分量).基于重力感应的应用程序,往往都需要计算物体在某一时刻的位移, 由加速度计算位移很简单,有公式,时间也容易得到.
M8的sdk里有获取各个坐标(x,y,z)的重力加速度分量的API, 有了这些函数,就可以开发出各种相关的应用了.要注意重力加速度坐标是以手机本身为参考点的,理解这一点很重要, 拿m8举个例子, 把手机平放在桌面上, 它的重力加速度坐标系如下:
Z轴倒没问题, x轴和y轴的坐标倒是让人想不明白, 跟窗口坐标系刚好反过来, 很容易造成误解,想不明白mz这样设计是基于什么考虑的.
2 硬件原理
iphone虽然不是第一个使用重力感应的手机,却把它发挥到极致, 并引领了一股风潮, 对于手机用户来说,是一次体验上的革命.
不过,我只能说手机的发展速度实在是太快, 重力感应刚兴起,iphone4已经把电子陀螺仪这个东东用在了手机上, 下一个时代可能就是电子陀螺仪了, 有了这个东东,精确计算手机的运动轨迹就不是问题。这是题外话。
M8是通过一个加速度传感器,检测手机上三个坐标方向的加速度,原理应该是通过不同的角度电信号的不同,通过AD转换,输出数字信号。 这只是本人根据一般加速度传感器的原理做的猜想, 因为没有查到m8所用加速度传感器的芯片型号, 无法找到它的datasheet.
重力加速度传感器的量程单位一般是 g, 也就是重力加速度常量,m8支持18mg至1g之间的量程范围, 1 = 18mg, 56=1g, g = 9.8, 支持56阶的加速度精度.举个例子,如果你的m8水平的放在桌面上, 理论上得到的z轴的加速度值应该为-56, 而x,y轴上为0, 但是实际的数据跟上述会有小的偏差,这个一般可以忽略. 正常情况下,加速度值的绝对值不会超过56, 如果超出这个值,有可能是突然的动作,比如剧烈摇晃手机.一般用到加速度原理的手机应用,我们只考虑重力加速度带来的影响效果, 不考虑一般的加速度(这方面的应用也有,比如摇晃手机取消操作,切歌等).
3 软件设计
M8用的是ce6.0, 而CE6或win mobile本身没有官方的重力感应API接口, 当手机硬件支持的情况下, 要想在上述两个系统上实现重力感应操作, 只有两种方法,一是有些手机厂商自己做了应用层的API(比如魅族,三星也有),二是在驱动级别通过底层的指令直接操作.
下面就来做一个简单的应用, 这个应用是在m8 sdk中重力感应例程基础上修改的, 这个sdk里的例程问题比较多,
第一, x轴,y轴搞反了(这可是个大问题).
第二,用两个按钮分别展示x轴和y轴的平面运动轨迹,这个想法实个人认为比较失败, 应该是用一个按钮展示在一个平面上(x轴和y轴)的运行轨迹比较直观一点吧.
第三,运动轨迹误差太大, sdk里每隔10ms位移加或减去一个固定值(在加速 度一定的情况下), 学过物理的都知道,如果位移匀速变化,加速度为应该是0,速度是匀速, 用sdk的程序实际运行,发现手机在一定的角度时,按钮滑动的速度确实感觉不到明显的变化, 也证实了这一点.这种重力感应的应用, 误差是允许的,但这种误差就有点不真实了.
由加速度和位移的公式可知,当加速度固定时(比如手机固定倾斜一个角度), 位移是加速增加,它跟时间和加速度这两个分量都是成正比的, 比如, 手机倾斜一个角度, 我们模拟一个小球从手机的顶端滑动到底部, 应该可以明显看出越往底部速度越快, 这个效果才更加真实.
当然, 对于简单的应用,没必要按照公式来计算位移, 我们可以开启一个定时器,每隔10ms更新一次位移(就像sdk中的做法), 关键代码如下:
//开启acc功能 MzAccOpen(); //获取XYZ轴加速度值 1 == 18mg, 56 == 1g //注意以下几点, //1 要先调用MzAccGetXYZ,再调用 //MzAccGetX/MzAccGetY/MzAccGetZ后,才能获得正确的值 //2 获得的加速度值范围是-55~+55, 绝对值起大,加速度越大. // MzAccGetXYZ(&m_XAxis, &m_YAxis, &m_ZAxis); MzAccGetX(&m_XAxis); //获得Y轴加速度 MzAccGetY(&m_YAxis); MzAccGetZ(&m_ZAxis); RECT rRect = MzGetWorkArea(); m_XButton.SetPos(0, 0, 150, 100); AddUiWin(&m_XButton); m_nMaxXPos = RECT_WIDTH(rRect) - m_XButton.GetWidth(); m_nMinXPos = 0; m_nMaxYPos = RECT_HEIGHT(rRect) - m_XButton.GetHeight(); m_nMinYPos = 0; SetTimer(m_hWnd, 10, 10, NULL); //设置初始的速度和位置 m_XPos = m_nMinXPos; m_YPos = m_nMinYPos; m_YSpeed = 0; m_XSpeed = 0; m_nCount = 0; return TRUE; } virtual void OnTimer(UINT_PTR nIDEvent) { switch(nIDEvent) { case 10: { //获得X轴加速度 MzAccGetX(&m_XAxis); //获得Y轴加速度 MzAccGetY(&m_YAxis); m_nCount++; if ( (m_nCount%10) == 0) { m_XSpeed += 2; m_YSpeed += 2; } if (IsXDirectionChange(m_XAxis)) { m_YSpeed = 0; } if (IsYDirectionChange(m_YAxis)) { m_XSpeed = 0; } //计算控件的Y位置,注意用的是加X轴的坐标 if (m_XAxis < 0) { m_XAxis = -m_XAxis; m_YPos = m_YPos + m_YSpeed + m_XAxis; } else { m_YPos = m_YPos - m_YSpeed - m_XAxis; } if (m_YPos < m_nMinYPos)//最底端 { m_YSpeed = 0;//注意这里要重置,防止溢出 m_YPos = m_nMinYPos; } if (m_YPos > m_nMaxYPos)//滑动到最顶端 { m_YPos = m_nMaxYPos; m_YSpeed = 0; //SetTimer(m_hWnd, 11, 10, NULL); } //计算控件的X位置 if (m_YAxis < 0) { m_YAxis = - m_YAxis; m_XPos = m_XPos - m_XSpeed - m_YAxis; } else { m_XPos = m_XPos + m_XSpeed + m_YAxis; } if (m_XPos < m_nMinXPos) { m_XSpeed = 0; m_XPos = m_nMinXPos; } if (m_XPos > m_nMaxXPos) { m_XSpeed = 0; m_XPos = m_nMaxXPos; } //重新设定控件的位置 m_XButton.SetPos(m_XPos, m_YPos, 150, 100); Invalidate(); //UpdateWindow(); } break; default: break; } } };
上述代码均在定时器处理函数中, 注释写得比较详细了, 要说明一下IsXDirectionChange和IsYDirectionChange两个函数, 这两个函数可以判断加速度方向是否更改, 如果更改, 把m_XSpeed置0, 这个也是为了逼近真实的效果. 试想一下, 当小球滑动的时候, 手机突然改变方向, 小球运动的速度应该是要从0开始了.
把程序放在小8里运跑一下, 效果还不错(可以在这人基础上做平衡球游戏了, 呵呵),截个图。