蛋疼的小球 – 重力感应器小试牛刀

上周,网友“忧郁飞花”提了个建议,说是可以用手机的重力感应器来控制小车,这样就可以把手机变成遥控方向盘。不知道这位同学为何如此忧郁,也许“飞花”形容的是该同学的工资流逝速度吧,在此假惺惺的慰问并感谢一下。

言归正传,手机当方向盘的想法虽然好,细节却还需要仔细推敲,例如倒退,调节转速,波动过大等情况的处理。一个应该可行的方案是用仰角来调节转速,把转速扩展到负值来实现倒退。(这时候我眼前出现了幻觉:圈圈在屋里拿着手机乱晃,小爱在外面剧烈抽风….)

汗,遥控小车还是先缓缓吧,这几天做了一个非常蛋疼的小程序来测试重力感应器。这个程序只有一个小球,在屏幕上滚来滚去。我特地找了个金色的珍珠,阿弥陀佛,保佑程序作者大富大贵,提前退休。

使用的手机是HTC的钻石2代,操作系统是windows mobile 6.5

滚动的金色小球

滚动的金色小球

写这个程序之前,先复习下高中的物理知识。假如一个小球在某时刻 t1,位置坐标是(x1,y1),速度方向是(vx1,vy1),受到的重力加速度是(ax,ay),那么当时光流逝到 t2 时,该小球的位置和速度应该是多少(假设t1,t2间隔很短,期间加速度没有变化)。其实x,y是矢量合成,咱们以x分量为例进行计算:

首先根据加速度的定义,vx2=vx1+ax*(t2-t1),注意ax可能为负值,所以速度方向也可能会改变。
x2的坐标则可以用x2=x1+(t2-t1)*(vx1+vx2)/2 来计算,即位移等于平均速度乘以时间。

然后是考虑碰壁的情况,以x轴负临界值为例,在Δt时间之后,发现x轴坐标已经小于0(我这里按质点做假设,实际上应该是小于小球半径的时候就碰壁了),这时候的速度大多数情况下也应该是负值(不解释了)。这时候假设小球的弹性系数是f,那么反弹回来的速度分量,应该是vx2′=-vx2*f ;而x坐标,可以估算为把负值那部分反射回来,再乘弹性系数,即x2′=-x2*f

如果程序写成这样,会发现大多数情况下,这个小球像模像样的蹦跶着,但是在接近停止的时候会突然跳的更高,永远停不下来。这个是因为临界值处理的不好,如果我们的Δt无限小,这个公式是正确的,但是真正计算时,Δt只能根据手机的承受能力设定(我设置的是50ms),这就会发生下面的情况:

边界条件超出合理范围

边界条件超出合理范围

当x1非常小,而Δt比较大的时候,x2的绝对值已经远超过x1,即使乘上弹性系数仍然大于x1。这就造成了接近壁面时,小球弹起的高度反而比原来更高。最后我加上了一些懒汉修正,当高度较低时,直接把速度和位置全设置成0。

物理复习完了,剩下的事情就简单了。其实就是找个图片,然后用一个timer定时,每隔一段时间更新一下(x,y)坐标即可。看看最终的效果视频:

下面贴一段大家可能有兴趣的“核心代码”:

  1. // http://www.diy-robots.com  
  2. // apply physics to a ball  
  3. Ball ApplyDevicePhysics(GVector gVector, Ball ball, int millisecondsElapsed)  
  4. {  
  5.     int maxX = ClientSize.Width - ball.Radius;  
  6.     int maxY = ClientSize.Height - ball.Radius;  
  7.     double t = millisecondsElapsed / 1000.0;  
  8.  
  9.     #region calculate x values  
  10.     gVector.X *= frictionForce;  
  11.     double v1 = ball.Velocity.X;  
  12.     double x1 = ball.Position.X;  
  13.     ball.Velocity.X = v1 + gVector.X * t;  
  14.     ball.Position.X = x1 + v1 * t + 0.5 * gVector.X * t * t;  
  15.     if (ball.Position.X < ball.Radius)  
  16.     {  
  17.         ball.Position.X = ball.Radius;  
  18.         if (ball.Velocity.X < 0)  
  19.         {  
  20.             ball.Velocity.X *= (-elasticRation);  
  21.             if (gVector.X < 0 && (-ball.Velocity.X / gVector.X) < (t * 2 * elasticRation))  
  22.             {  
  23.                 ball.Velocity.X = 0;  
  24.             }  
  25.         }  
  26.     }  
  27.     if (ball.Position.X > maxX)  
  28.     {  
  29.         ball.Position.X = maxX;  
  30.         if (ball.Velocity.X > 0)  
  31.         {  
  32.             ball.Velocity.X *= (-elasticRation);  
  33.             if (gVector.X > 0 && (-ball.Velocity.X / gVector.X) < (t * 2 * elasticRation))  
  34.             {  
  35.                 ball.Velocity.X = 0;  
  36.             }  
  37.         }  
  38.     }  
  39.     #endregion  
  40.  
  41.     #region calculate y values  
  42.     gVector.Y *= frictionForce;  
  43.     v1 = ball.Velocity.Y;  
  44.     double y1 = ball.Position.Y;  
  45.     ball.Velocity.Y = v1 + gVector.Y * t;  
  46.     ball.Position.Y = y1 + v1 * t + 0.5 * gVector.Y * t * t;  
  47.     if (ball.Position.Y < ball.Radius)  
  48.     {  
  49.         ball.Position.Y = ball.Radius;  
  50.         if (ball.Velocity.Y < 0)  
  51.         {  
  52.             ball.Velocity.Y *= (-elasticRation);  
  53.             if (gVector.Y < 0 && (-ball.Velocity.Y / gVector.Y) < (t * 2 * elasticRation))  
  54.             {  
  55.                 ball.Velocity.Y = 0;  
  56.             }  
  57.         }  
  58.     }  
  59.     else if (ball.Position.Y > maxY)  
  60.     {  
  61.         ball.Position.Y = maxY;  
  62.         if (ball.Velocity.Y > 0)  
  63.         {  
  64.             ball.Velocity.Y *= (-elasticRation);  
  65.             if (gVector.Y > 0 && (-ball.Velocity.Y / gVector.Y) < (t * 2 * elasticRation))  
  66.             {  
  67.                 ball.Velocity.Y = 0;  
  68.             }  
  69.         }  
  70.     }  
  71.     #endregion  
  72.     return ball;  
  73. }  

实验证明,HTC手机的重力感应器还是挺灵敏的,我设置的时间是50毫秒。因为暂时不方便加工小爱,接下来我打算试着做一个简易的两轮平衡小车。



对 “蛋疼的小球 – 重力感应器小试牛刀” 的 12 条 评论

  1. aLai 说:

    赞,期待后续

  2. 老七 说:

    太先进了。。。

  3. 忧郁飞花 说:

    小时候收集水浒卡。。林冲的一个绝招叫忧郁飞花,于是就用了~~~PS:我还没工作呢。。下半年开始读研

  4. SEVEN 说:

    我是来看手机的,哈哈哈

  5. 狂热软件 说:

    哦,这个重力感应我体验过了,以前在索爱的W910里就有,很酷!

  6. DeepBlue 说:

    这个想法很棒,其实我也有个差不多的想法,就是采用Android的手机通过蓝牙来和单片机通信,这样只要编写一个手机应用程序,就可以用手机来控制你想控制的东西

    • 是啊,手机是个好东西,相当于一个超级开发板:蓝牙+Wifi+CPU+内存+摄像头+重力感应器+触摸屏+麦克风+GPS…..

      可惜我这个是windows mobile,慢而且不稳定,Android应该会好很多

  7. 手指明月 说:

    崇拜!看齐啊!看齐!

  8. YouXi结S 说:

    源码能不能给分我呀
    提前谢谢了
    可以的话发邮箱

  9. 云飞 说:

    我也做过类似的东西,请问您使用的是哪个GSensor程序库,官方是没有公开的,我找了二、三个,都不太一样。

发表评论

可以使用下列 XHTML 标签:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>