热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

触控设备手势唤醒的设计思路及其实现

本文讨论如何唤醒平板电脑等触控装置,无需接触设备,而是采用基本的手势识别及新颖的接近检测传感器。本文讨论了相关设计的物理布局、速度限制、检测门限、系统集成,以及人为因素的影响;给出了软件实时的

本文讨论如何唤醒平板电脑等触控装置,无需接触设备,而是采用基本的手势识别及新颖的接近检测传感器。本文讨论了相关设计的物理布局、速度限制、检测门限、系统集成,以及人为因素的影响;给出了软件实时的例程。

  厨房里的突发奇想

  如果做饭时使用触控设备,您可能会注意到按照设备列出的食谱烹饪并非想象得那么简单。技术达人(例如鄙人)走进厨房时,喜欢看着平板电脑或智能手机上的菜谱做饭。您可能会说:“好吧,这有什么难度?”由于屏幕始终开启会消耗很大电量,通常手持装置在1、2分钟后没有操作时将自动进入休眠状态。那么,当您需要参照食谱时,设备已进入休眠状态。此事,您面临两个选择:要么强制屏幕保持永久开启;要么用沾满食物的手开启装置,而在屏幕上留下斑斑油渍。当然,您可以在每次查看时把手清洗干净,但不断重复洗手、擦干即繁琐,又费水。

  我时常问自己:“怎样才能既不让屏幕始终开启,又不会弄脏装置?”实际上,有一种办法一举两得,即通过一个手势(不用接触屏幕)开启显示屏。听起来似乎很复杂,是吗?幸运的是,做起来可能比听起来容易一些。

  接近检测传感器

  许多触摸屏装置,尤其是智能手机,内部已经安装了红外(IR)接近检测传感器。这些传感器一般在通话期间自动打开/关闭屏幕,以避免意外操作手机的输入界面。这种传感器,加上精明的软件设计,就能实现利用一个手势唤醒装置的功能。

  基本的设计思路是:设备进入休眠状态时,触摸屏关闭,应用处理器处于低功耗模式,依靠接近检测传感器“观察”背景的变化,当接收到的信号足够大时,做出适当反应。这与接近检测传感器在通话期间关闭屏幕的功能几乎完全相同。只是,我们的应用对数据有了不同的解释。

  首先记录传感器在“正常”背景下的计数值,此时得到的数值可能为零,但实际设计中需要考虑系统失调(例如:散射或串扰)。然后将得到的数值设置为检测门限,当接收信号超过门限时触发中断或向应用处理器发送信号,以唤醒系统并打开屏幕。总体而言,这种方法非常简单、直观,可利用环境光检测器和IR接近检测传感器实现。

  本文介绍的方案采用MAX44000,接近检测的数据读取时间间隔可以设置在1.56ms至100ms (与环境光检测传感器轮流读取数据)。假设最大检测距离为10cm,LED的辐射角为±15°,那么,可以覆盖的面积大约为22cm2或跨距大约为5.35cm,只有该区域内的移动目标才能捕捉到。由此,能够以最慢(即最低功耗)的采样速度可靠检测的最快手势动作大约为0.53mps。在此,我们还假设传感器只需要采集到一次高于门限的信号,即可识别经过覆盖区域的目标。

  举手之劳...

  理论上讲,该方案的实施非常简单。当装置进入休眠模式时,将接近检测传感器置为环境扫描模式,并在检测到目标时发出中断信号,指示捕捉到超过预设门限的信号。可通过I2C接口轮询传感器的状态。不幸的是,这种方式会消耗过大功率,超出了大多数用户的预期。

  这也是接近检测传感器的设计重点,MAX44000传感器能够在许多方面摆脱应用处理器的干预,减轻处理器负荷(降低功耗)。 使能MAX44000的内部接近检测中断(寄存器0x01的第1位),可将唤醒门限写入内部寄存器(0x0B和0x0C)。当接近检测传感器的读数超过该门限时,触发中断标识置位,将MAX44000的/INT引脚置为低电平。当应用处理器检测到该引脚驱动为低电平时,可唤醒装置退出低功耗模式,并打开屏幕,或完成其它需要的动作。

  ...但不容忽视

  实际应用往往不如理论那么容易,非接触唤醒的具体实施并非只是简单地检测高于门限的信号。实际上,具体的设计需要考虑诸多因素。

  信号电平与电路布局

  最关键的考虑应该是触发唤醒条件的信号电平,需要在系统响应灵敏度与误报概率之间进行权衡。如果门限过低,则很容易检测到输入(手势工作),但会增大瞬态噪声或突发条件产生误报的概率。反之,过高的检测门限能够把误报概率降至几乎为零,但却只能检测到非常接近的目标,甚至对任何输入(即使您疯狂晃动手臂)都反应迟钝。

  解决这一问题的最佳方式是:首先降低系统噪声,可以通过光学方法或严谨的电路布局实现,降低的噪底有助于降低误报概率;其次,选择“平均”检测距离(例如:4cm至5cm)并利用参考目标测量信号,18%的灰板比较理想,但如果触摸屏上方安装了黑色玻璃,测量时也应该使用这样的玻璃,所测得的信号电平可以作为设置门限的最佳参考。通常可以遵循这样的原则:即将电平设置在满幅的8%至15%,即使电平发生变化。

  可以按照上述经验数据设置MAX44000传感器的接近检测门限寄存器,图1所示为信号强度随距离变化的关系曲线,采用18%灰板,驱动电流为100mA,传感器上方没有玻璃罩。蓝线为可以选择的唤醒门限。

  

  图1. MAX44000接近检测传感器信号强度随距离变化的关系曲线,采用18%灰板,100mA驱动电流,没有玻璃罩。


    噪声和低通滤波

  需要考虑噪声问题时,可利用低通滤波器处理信号;另外,MAX44000还有几个控制位可以用作触发中断标识之前的屏蔽,采用这种设置时,需要检测到一定数量超出门限的采样值时才会触发中断标示,能够在一定程度上降低噪声的影响。

  一种稍微复杂的方法是将传感器的读数储存在数据队列中,然后利用定制的FIR软件对其进行滤波处理。但这种方法需要提高接近检测传感器的采样速率,否则则会降低能够捕捉到的传感器可视范围内的手势动作速率,特别是把采样速率设置在100ms时。利用器件的控制位屏蔽检测时,速率可最多降低16倍(通常选择4x屏蔽即可)。

  手势速度

  手势动作的快慢是我们需要考虑的另一因素。最大速度取决于:1. 传感器的可视范围;2. 手与传感器之间的距离;3. 采样率;4. 检测门限。前两项很容易确定:传感器的检测角度,结合传感器与目标之间的距离,利用基本的三角形即可计算出传感器可视范围内目标的移动距离。例如,如果传感器的视角为30度,最大有效检测距离10cm,那么,传感器可视范围内允许的目标移动距离为5.35cm,覆盖面积大约为78cm2。直线距离结合采样率,即可决定速度限值。 具体地说,如果采样率为T,那么目标跨越可视区域的时间不得小于T。例如,如果T为100ms (MAX44000的最低采样速率),那么按照上例,理论上最大允许的速率为1mps (这实际上已经相当快了)。您可能希望捕获到多个采样值来确认触发唤醒,这样的话,会降低允许的速率下限。

  检测门限也影响最大允许速率。一般来说,门限越低,能够捕捉到的手势动作就越快。如上所述,应谨慎选择门限,以免产生误报。

      人为因素

  这种应用还会受到人手以及挥手动作等人为因素的影响。应通过一些案例确定一般大多数人的习惯,包括他们在屏幕前挥动手掌的速度以及与屏幕之间的距离,另外,是否戴手套也会产生一定的影响。不同的应用场合(不同装置)也会影响到设计需求,例如智能手机、平板电脑或汽车仪表盘,对存在具体的设计考虑。当然,设计过程中还应考虑用户界面和经验参数。

  最后,还要对真假手势做出判断,即装置需要判断接收到的信号是来自于一个手势动作,还是简单的装置移动(例如:放置在外套、口袋或背包中,或者是屏幕朝下放置)。单纯依靠上述检测原理,很难做出正确的“真伪”鉴别,除非在装置内提供更多的背景信息。关于这一问题的讨论超出了本文范围。

  设计中可以选择只有装置进入特定的应用程序时启动唤醒方案,也可以由用户手动操作使能。此外,许多此类装置都有一个加速度传感器,能够检测到屏幕是否背面朝下放置。如果用户手动将装置置于休眠模式,则可禁用该功能(例如关机状态)。

  设计实例

  为方便起见,本文附带了三段演示程序代码。第一段代码用于手动操作MAX44000的接近检测数据读取,概念上简单实现唤醒功能;第二段代码在第一段的基础上进行了扩展,增加了之前讨论的滤波功能;最后一段代码演示利用MAX44000中断唤醒触控装置。 示例代码1

  __interrupt void TimedInterrupt( void )

  {

  uint8 proximity_counts;

  ....

  ....

  if ( device_status == SLEEP_MODE )

  {

  // read one byte from register 0x16

  proximity_counts = read_i2c_register(MAX44000_ADDR,0x16,1);

  if (proximity_counts 》 WAKEUP_THRESHOLD)

  {

  device_status = WAKE_MODE;

  ...

  }

  else

  {

  // do whatever it is you need to in sleep mode

  ...

  ...

  }

  }

  ...

  ...

  }


示例代码2

  // example interrupt function where this might be implemented

  __interrupt void TimedInterrupt( void )

  {

  uint8 proximity_counts;

  uint8 filtered_counts;

  ....

  ....

  if ( device_status == SLEEP_MODE )

  {

  // read one byte from register 0x16

  proximity_counts = read_i2c_register(MAX44000_ADDR,0x16,1);

  // weights[QUEUE_SIZE] contains the filter weights for the FIR filter

  // data_queue[QUEUE_SIZE] is a FIFO queue meant to be the input to the filter

  filtered_counts = fir_filter(proximity_counts,weights,data_queue);

  if (filtered_counts 》 WAKEUP_THRESHOLD)

  {

  device_status = WAKE_MODE;

  ...

  }

  else

  {

  // do whatever it is you need to in sleep mode

  ...

  ...

  }

  }

  ...

  ...

  }

  /**

  * fir_filter()

  *

  * Implements an FIR filter in the form

  * y = w[0]*x[0] + w[1]*x[1] + 。。.+ w[QUEUE_SIZE]*x[QUEUE_SIZE]

  *

  * Arguments:

  * uint8 input - newest datapoint taken (that is, x[0])

  * uint8 *weights - w[0]。。.w[QUEUE_SIZE]

  * uint8 *queue - the discrete sequence x[0]。。.x[QUEUE_SIZE]

  *

  * Returns:

  * The FIR-filtered output, y

  */

  uint8 fir_filter(uint8 input, uint8 *weights, uint8 *queue)

  {

  uint8 i;

  int sum = 0;

  // pop first entry in the queue, then

  // push new data into the last position

  push_into_queue(queue,input);

  // input is now x[0]

  for (i=0; i {

  sum += weights[i]*queue[i];

  }

  return (sum/QUEUE_SIZE);

  }


示例代码3

  // this handles hardware-level interrupts on the micro

  __interrupt void irq_handler( void )

  {

  ...

  // if the hardware interrupt came from the MAX44000 sensor

  // pulling its INT pin low

  if ( irq_source == MAX44000 )

  {

  // if the device is in sleep mode

  if (device_status == SLEEP_MODE)

  {

  device_status = WAKE_MODE; // wake up the device

  ...

  // reconfigure whatever else you need here as the system wakes up

  }

  // otherwise, handle it however it is you wish

  else

  {

  ...

  }

  }

  ...

  }

  /**

  * configure_max44000_for_sleep_mode()

  *

  * Sets up the MAX44000 to trigger a hardware interrupt when the proximity

  * counts go above some set threshold.

  *

  * Arguments:

  * uint8 upper_threshold - the set threshold (8-bit mode)

  *

  * Returns:

  * n/a

  */

  void configure_max44000_for_sleep_mode(uint8 upper_threshold)

  {

  uint8 max44000_thresh_registers[] = {0x0B,0x0C};

  uint8 max44000_upper_thresh[] = {0x40,0};

  max44000_upper_thresh[1] = upper_threshold;

  // do a consecutive write of 0 followed by upper_threshold to

  // registers 0xB and 0xC, respectively

  // MAX44000_ADDR is usually 0x94

  // interrupt will trigger only if proximity value is above the threshold

  write_i2c_register(MAX44000_ADDR,max44000_thresh_registers,

  max44000_upper_thresh,2);

  // write to bits 2 and 3 of register 0x0A here if you wish to set the

  // persist time to anything other than one sample

  // writes to register 0x01 to enable interrupts on the MAX44000

  max44000_enable_interrupt();

  return;

  }


转自:http://www.21ic.com/app/control/201207/131720.htm



推荐阅读
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • Redis底层数据结构之压缩列表的介绍及实现原理
    本文介绍了Redis底层数据结构之压缩列表的概念、实现原理以及使用场景。压缩列表是Redis为了节约内存而开发的一种顺序数据结构,由特殊编码的连续内存块组成。文章详细解释了压缩列表的构成和各个属性的含义,以及如何通过指针来计算表尾节点的地址。压缩列表适用于列表键和哈希键中只包含少量小整数值和短字符串的情况。通过使用压缩列表,可以有效减少内存占用,提升Redis的性能。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
author-avatar
军长长军765
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有