设备中的三自由度Orientation Sensor就是一个可以识别设备相对于地面,绕x、y、z轴转动角度的感应器(自己的理解,不够严谨)。智能手机,平板电脑有了它,可以实现很多好玩的应用,比如说指南针等。

我们可以用一个磁场感应器(magnetic sensor)来实现。

磁场感应器是用来测量磁场感应强度的。一个3轴的磁sensor IC可以得到当前环境下X、Y和Z方向上的磁场感应强度,对于Android中间层来说就是读取该感应器测量到的这3个值。当需要时,上报给上层应用程序。磁感应强度的单位是T(特斯拉)或者是Gs(高斯),1T等于10000Gs。

先来看看android定义的坐标系,在/hardware/libhardware/include/hardware/sensors.h中有个图。

图中表示设备的正上方是y轴方向,右边是x轴方向,垂直设备屏幕平面向上的是Z轴方向,这个很重要。因为应用程序就是根据这样的定义来写的,所以我们报给应用的数据要跟这个定义符合。还需要清楚磁sensor芯片贴在板上的坐标系。我们从芯片读出数据后要把芯片的坐标系转换为设备的实际坐标系。除非芯片贴在板上刚好跟设备的x、y、z轴方向刚好一致(去感谢你的硬件工程师吧)。

Orientation Sensor的实现是根据磁场感应强度的3个值计算出另外3个值。当需要时,我们计算出这3个值上报给应用程序,Orientation Sensor的功能就实现了。

这3个值具体含义和计算方法是:

1. azimuth 方位角:就是绕z轴转动的角度,0度=正北,(假设Y轴指向地磁正北方,直升机正前方的方向如下图)

90度=正东,

180度=正南,

270度=正西。

求x和y方向的磁感应强度的反正切,就可以得到方位角(算法看后面poll函数中的代码)。要实现指南针,只需要这个就可以了(不考虑设备非水平的情况);

2. pitch 仰俯:绕X轴转动的角度 (-180<=pitch<=180), 如果设备水平放置,前方向下俯就是正,如图:

前方向上仰就是负值;

求磁sensor的y和z反正切可得到此角度值。

3. roll 滚转:绕Y轴转动(-90<=roll<=90),向左翻滚是正值

向右翻滚是负值;

求z和x的反正切可得到此值。

sensors.h中还定义了其他各种sensor。要实现的就是这两个:

#define SENSOR_TYPE_MAGNETIC_FIELD      2

#define SENSOR_TYPE_ORIENTATION         3

在/hardware/sensors/sensors.cpp 中添加对MAGNETIC_FIELD和ORIENTATION 的支持

简单的说一下怎样添加,下面的代码不完整,请参考/sdk/emulator/sensors/sensors_qemu.c

  1. //加入需要的宏定义
  2. #define  ID_BASE           SENSORS_HANDLE_BASE
  3. #define  ID_ACCELERATION   (ID_BASE+0)
  4. #define  ID_MAGNETIC_FIELD (ID_BASE+1)
  5. #define  ID_ORIENTATION (ID_BASE+2)
  6. #define S_HANDLE_ACCELEROMETER      (1<<ID_ACCELERATION)
  7. #define S_HANDLE_MAGNETIC_FIELD           (1<<ID_MAGNETIC_FIELD)
  8. #define S_HANDLE_ORIENTATION                 (1<<ID_ORIENTATION)
  9. #define SENSORS_NUM 4
  10. #define SUPPORTED_SENSORS  ((1<<NUM_SENSORS)-1)
  11. //在 sensor_t sensors_list[] 中添加两个sensor的信息,
  12. //这些只是一些Sensor的信息,应用程序可以获取到。
  13. #ifdef MAGNETIC_FIELD
  14. {
  15. name       : "XXX 3-axis Magnetic field sensor",
  16. vendor    : "XXX company",
  17. version    : 1,
  18. handle     : S_HANDLE_MAGNETIC_FIELD,
  19. type       : SENSOR_TYPE_MAGNETIC_FIELD,
  20. maxRange   : 600.0f,//最大范围
  21. resolution : 30.0f,//最小分辨率
  22. power      : 6.7f,//这个不太懂
  23. },
  24. #endif
  25. #ifdef ORIENTATION
  26. {
  27. name: "XXX Orientation sensor",
  28. vendor: "XXX company",
  29. version: 1,
  30. handle: S_HANDLE_ORIENTATION,
  31. type: SENSOR_TYPE_ORIENTATION,
  32. maxRange: 360,
  33. resolution: 0.1,
  34. power: 20,
  35. },
  36. #endif
  37. //定义一个结构来保存orientation的信息
  38. static struct orientation{
  39. float azimuth;
  40. float pitch;
  41. float roll;
  42. }orientation;
  43. //在 control__open_data_source()函数中打开设备
  44. static native_handle_t*
  45. control__open_data_source(struct sensors_control_device_t *dev)
  46. {
  47. SensorControl*  ctl = (void*)dev;
  48. native_handle_t* handle;
  49. int fd_m = open (MAGNETIC_DATA_DEVICE, O_RDONLY);
  50. LOGD ("Open Magnetic Data source: %d, %d/n", fd_m, errno);
  51. if (fd_m>= 0)
  52. {
  53. dev->fd[ID_MAGNETIC_FIELD] = dup(fd_m);
  54. }
  55. return handle;
  56. }
  57. //实现数据的打开和关闭函数
  58. static int
  59. data__data_open(struct sensors_data_device_t *dev, native_handle_t* handle)
  60. {
  61. struct sensors_data_context_t *dev;
  62. dev = (struct sensors_data_context_t *)device;
  63. for(int i=0 ;i<SENSORS_NUM; i++)
  64. {
  65. dev->fd[i] = dup(handle->data[i]);
  66. }
  67. native_handle_close(handle);
  68. native_handle_delete(handle);
  69. return 0;
  70. }
  71. static int
  72. data__data_close(struct sensors_data_device_t *dev)
  73. {
  74. struct sensors_data_context_t *dev;
  75. dev = (struct sensors_data_context_t *)device;
  76. for(int i=0 ;i<SENSORS_NUM; i++)
  77. {
  78. if (dev->fd[i] >= 0)
  79. {
  80. close(dev->fd[i]);
  81. }
  82. dev->fd[i] = -1;
  83. }
  84. return 0;
  85. }
  86. //最关键的poll函数
  87. static int
  88. data__poll(struct sensors_data_device_t *dev, sensors_data_t* values)
  89. {
  90. SensorData*  data = (void*)dev;
  91. int fd = data->events_fd;
  92. //判断设备是否打开
  93. if(dev->fd[ID_MAGNETIC_FIELD] < 0)
  94. {
  95. LOGD("In %s dev[%d] is not open!/n",__FUNCTION__ ,ID_MAGNETIC_FIELD);
  96. return -1;
  97. }
  98. pollfd pfd[SENSORS_NUM] =
  99. {
  100. //省略其他sensor代码
  101. {
  102. fd: dev->fd[ID_MAGNETIC_FIELD],
  103. events: POLLIN,
  104. revents: 0
  105. },
  106. //省略其他sensor代码
  107. };
  108. int err = poll (pfd, SENSORS_NUM, s_timeout);
  109. unsigned int  mask = SUPPORTED_SENSORS;
  110. static unsigned int poll_flag=0;
  111. if(poll_flag==0)
  112. {
  113. poll_flag = mask;
  114. }
  115. //省略其他sensor
  116. if(poll_flag&(1<<ID_MAGNETIC_FIELD))
  117. {
  118. if((pfd[ID_MAGNETIC_FIELD].revents&POLLIN) == POLLIN)
  119. {
  120. char rawData[6];
  121. err = read (dev->fd[ID_MAGNETIC_FIELD], &rawData, sizeof(rawData));
  122. if(err<0)
  123. {
  124. LOGE("read magnetic field ret:%d errno:%d/n", err, errno);
  125. return err;
  126. }
  127. struct timespec t;
  128. clock_gettime(CLOCK_REALTIME, &t);
  129. data->time = timespec_to_ns(&t);
  130. data->sensor = SENSOR_TYPE_MAGNETIC_FIELD;
  131. data->magnetic.status = SENSOR_STATUS_ACCURACY_HIGH;
  132. //上报的数据单位要转换成 uTesla
  133. data->magnetic.x = ( (rawData[1] << 8 ) | rawData[0])/ MAGNETIC_CONVERT;
  134. data->magnetic.y = ( (rawData[3] << 8 ) | rawData[2])/ MAGNETIC_CONVERT;
  135. data->magnetic.z = ( (rawData[5] << 8 ) | rawData[4])/ MAGNETIC_CONVERT;
  136. //把陀螺仪需要的数据计算出来,用atan2(),头文件要加上#include <math.h>
  137. float azimuth = atan2(  (float)(data->magnetic.x ),(float)(data->magnetic.y) );
  138. if(azimuth<0)
  139. {
  140. azimuth = 360 - fabs(azimuth*180/PI);
  141. }
  142. else
  143. {
  144. azimuth = azimuth*180/PI;
  145. }
  146. orientation.azimuth = 360-azimuth;
  147. //rotation around the X axis.+180~-180 degree
  148. orientation.pitch = atan2( (float)(data->magnetic.y ),(float)(data->magnetic.z)
  149. )*180/PI;
  150. //rotation around the Y axis +90~-90 degree
  151. float roll = atan2( (float)(data->magnetic.x ),(float)(data->magnetic.z) )
  152. *180/PI;
  153. if (roll > 90)
  154. {
  155. roll = -(180.0-roll);
  156. }
  157. else if (roll < -90)
  158. {
  159. roll = 180 + roll;
  160. }
  161. orientation.roll =  roll;
  162. }
  163. return S_HANDLE_MAGNETIC_FIELD;
  164. }
  165. if(poll_flag&(1<<ID_MAGNETIC_FIELD))
  166. {
  167. //数据已经计算好了直接上报就行
  168. struct timespec t;
  169. clock_gettime(CLOCK_REALTIME, &t);
  170. data->time = timespec_to_ns(&t);
  171. data->sensor = SENSOR_TYPE_ORIENTATION;
  172. data->orientation.azimuth = orientation.azimuth;
  173. data->orientation.pitch = orientation.pitch;
  174. data->orientation.roll = orientation.roll;
  175. poll_flag &= ~(1<<ID_ORIENTATION);
  176. return S_HANDLE_ORIENTATION;
  177. }
  178. }

写好后可以用一个叫做sensorlist的程序先测试一下,看报上去的数据是否正常。然后可以试试一个叫做Pacific Navy Fighter 的游戏来爽一爽了。

由于涉及到很多方面的内容,错误难免,敬请指正。

=====================

补充:我之前搞错了,我以为Orientation Sensor就叫做陀螺仪。谢谢syz85。

之后我又看了一下Android中对陀螺仪(gyroscope Sersor)的定义,是指在上述定义的x y x三个方向的转速,单位是radians/second,正负遵循右手规则。

想一想实现应该也不难,把从Orientation Sensor两次得到的 x y z角度变化除以时间就可以得到转速。

(其实真正的陀螺仪包括Orientation Sensor和gyroscope Sersor这两个功能)

但转速对于手机或其他消费类电子有意义是什么呢?谁指点我一下,gyroscope 会带来什么好玩的应用。

Android设备中实现Orientation Sensor(图)兼谈陀螺仪的更多相关文章

  1. bat如何创建多级文件夹(在android设备中)

    在android设备中要创建多个或者多级文件夹时,手动去创建费时费力(有点傻),一个bat文件就能很好的实现这个功能. 1.首先创建同级多个文件夹且在该文件夹下生成一个文件 @echo off ech ...

  2. 通过ADB命令行卸载或删除你的Android设备中的应用(转载)

    转自:http://mytiankong.com/?p=11755 如果你对你的Android设备在与命令行的交互间有一定的兴趣,那你可能想学习一些使用ADB卸载设备中已安装应用的技巧.为了使这种方法 ...

  3. 从Android设备中提取内核和逆向分析

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/57074695 一.手机设备环境 Model number: Nexus 5 OS ...

  4. 在桌面chrome中调试android设备中的web页面

    准备工作 1, 桌面版chrome 2, Android设备(安装有chrome浏览器) 3, Android-sdk Android-sdk安装及设置 SKD安装 从http://developer ...

  5. 获取Android设备的方向,Sensor和SensorManager实现手机旋转角度

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1009/425.html 带有g-sensor的Android设备上可通过API ...

  6. 浅析Android设备中grep命令处理流程

    2017-04-18   概述     在TV开发板中,可以在串口中直接使用grep命令.这是因为在/system/bin/下有一个'grep'链接.这个链接指向'/system/bin/toolbo ...

  7. Android Studio中绘制simpleUML类图详细说明及使用

    一.Android Studio中安装simpleUML 1.下载simpleUML jar包 地址为:http://plugins.jetbrains.com/  搜索 simpleUMLCE 2. ...

  8. 从android设备中提取内核

    背景 CVE-2013-2597 是高通 msm_acdb 设备驱动的一个 copy_from_user 栈溢出,利用要用到ROP.f101的漏洞利用介绍中,用到几处Gadgets,显然要根据acdb ...

  9. 通过adb命令在Android设备中执行Java命令, 并调用so文件。

    一.难点一:无法复制so文件到/system/lib或者/vendor/lib下,提示只读 解决方法: 2.使用android device monitor放库进入到 /system/lib出现只读权 ...

随机推荐

  1. div中实现居中

    今天纠结了大半天的居中,把学到的先记录下来,还没完全弄清楚,发现网上原创的技术贴并不算多,大多都是相互转载.(ps.先安利一个大神的帖集,昨天才发现的,内容丰富,语言,呃...很幽默,一般都是图文并茂 ...

  2. python代码合并

    http://www.baidu.com/s?wd=python%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6&rsv_bp=0&ch=&tn=mon ...

  3. (转)TortoiseSVN使用简介

    TortoiseSVN使用简介 TortoiseSVN使用简介 2009-04-24 来源:dev.idv.tw 1 安装及下载client 端 2 什么是SVN(Subversion)? 3 为甚么 ...

  4. ImageView的学习

    学习安卓时我还是习惯看懂手册,虽然是英文但是可以获得的东西必然也是更多的,否则自己只能停留在拾人牙缝的水平,虽然我是初学,但是还是分享一些自己的学习过程及方法. 从手册中我们看以知道,ImageVie ...

  5. Redhat Enterprise 5.4下安装配置Oracle 11g R2详细过程

    1.Linux环境配置准备 环境:Linux:Redhat Enterprise 5.4,DB:Oracle 11g R2 X64,Oracle安装到/home/oralce_11目录下. 配置过程如 ...

  6. PHP中的定界符格式

    <?php //nowdoc(单引号定界符) //ABC可以是任合内容,放在单引号中 $c=<<<'ABC' 这里可以是任合内容 我是历的苛夺基 本原则叶落归根在运 输费艰难田 ...

  7. Linux程序设计笔记

    使用size命令查看二进制文件时,结果并不一定和预测占用内存大小一致,因为可能存在内存对齐,导致内存字节数比实际的更多 在C语言中,字符串常量存放在text segment中,在C++中却是存储在da ...

  8. 工作中用到的Jquery特效

    jQuery缓慢弹出下拉tab导航 http://sc.chinaz.com/jiaoben/130811578701.htm

  9. 实现单实例多线程安全API问题

    前阵子写静态lib导出单实例多线程安全API时,出现了CRITICAL_SECTION初始化太晚的问题,之后查看了错误的资料,引导向了错误的理解,以至于今天凌晨看到另一份代码,也不多想的以为singl ...

  10. python内置函数(4)

    12.pow:      >>> 2**10 1024 >>> pow(2,10) 1024 13.repr():忽略.. 14.reversed():反转.. 1 ...