设备中的三自由度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. java RSA签名

    try{ //1初始化秘钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); key ...

  2. phpmyadmin登陆提示#2002 无法登录 MySQL 服务器和设置自增

    看看mysql启动没有,结果是mysql服务没有启动,找了半天,是这个原因,那就右键计算机->管理->服务->启动mysql服务 设置自增:在显示出来的一行字段定义中把浏览器的滚动条 ...

  3. 如何给report自定义page number

    问题描述: report在设置分页后会自动分页,但是有默认的page number,现在的问题是有时default page number不能满足我们的需求,此时就需要自定义page number. ...

  4. Ubuntu 11.10开启root用户登陆

    以管理员身份运行 #sudo gedit /etc/lightdm/lightdm.conf 将里面改成 "autologin-user=root" 就可以以root用户登录了

  5. [欢度国庆]为什么我们今天还要学习和使用C++?(转载)

    在各种新的开发语言层出不穷的今天,在Java和C#大行其道今天,我们为什么还要学习和使用C++?现在学习C++将来有用吗?学习C++要花费那么多时间和精力,这一切都值得吗?现在学习C++有钱途吗? 这 ...

  6. linux笔记2.20

    用户相关:  /etc/passwd  用户信息 /etc/shadow  密码信息 /etc/group  组信息 添加用户:   useradd   -u -g 修改用户: usermod   - ...

  7. iOS中常用的四种数据持久化方法简介

    iOS中常用的四种数据持久化方法简介 iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data 1.属性列表涉及到的主要类:NSUserDefaults,一般 ...

  8. hibernate3 无法查询中文问题

    在查询中文时  hql语句在生成的语句中把中文显示为乱码 则在hibernate配置文件中加入: <property name="hibernate.query.factory_cla ...

  9. HDOJ 1326 Box of Bricks(简单题)

    Problem Description Little Bob likes playing with his box of bricks. He puts the bricks one upon ano ...

  10. linux内存管理子系统

    一.Linux内存管理模型 1.虚拟地址与物理地址的映射 2.物理地址的分配二.虚拟地址与物理地址的映射 1.虚拟地址空间分布 32位处理器有32根地址总线,可访问4G的物理空间.其中有0-3G为用户 ...