旋转编码器控制的另一种算法
操作原理
旋转编码器是通常将轴的角运动转换为数字输出信号的设备。有许多 Web 资源解释了这些设备的机制以及如何将它们连接到微型计算机,但本页并非完整的概述。这个想法是提出另一种算法来正确读取机械旋转编码器的输出。

原则上,常见的旋转编码器通过交替闭合两个开关来产生信号,如上图所示。根据连接,中性状态可以是高 (1) 或低 (0)。我们在这里假设中性状态为高的最常见连接。对于顺时针运动,第一个开关的动作先于第二个开关的动作,而逆时针运动则相反。如果我们将每个状态表示为二进制数,两位数字表示两个开关的状态,则中性状态表示为11, 顺时针旋转由状态链表示11→01→00→10→11逆时针旋转由状态链表示11→10→00→01→11.
开关弹跳
问题是机械电气开关不完善,所以会出现开关弹跳的现象。当切换开关时,触点必须从一个位置物理移动到另一个位置。当开关的组件进入新位置时,它们会机械弹跳,导致底层电路多次打开和关闭。得到的信号如上图所示。
这个问题可以通过硬件或软件来解决。在软件的情况下,这涉及到某个主观确定的保持时间,在此期间开关的活动被忽略。然而,在旋转开关的情况下,我们可以完全避免保持时间。由于这对开关经过四个连续的状态,我们原则上可以准确地定义操作何时完成。
算法
微处理器一共可以读出四种不同的状态,连续两次读出总共给出了十六种可能的转换,如上图所示。四个过渡代表没有移动,四个过渡代表向右四分之一移动,四个过渡代表向左四分之一移动,四个过渡代表理论上不可能的过渡。
让我们将一个完整的周期定义为一个以中性状态开始和结束的过程。通过分配 0 表示没有移动,+1 表示向右移动四分之一,-1 表示向左移动四分之一,我们得到 +4 的总和表示向右一个完整的周期,-4 的总和表示一个完整的循环循环向左,总和为 0 表示没有移动。特别是,每个单独的开关弹跳,无论是向左还是向右,总和为 0,对结果没有影响。此外,聚合和 4 的模数为我们提供了确切的状态。例如,如果模数为零,则当前状态为中性状态。
必须特别注意“不可能”的转换。由于技术上的不足,它们确实是可能的。如果我们将值 0 分配给不可能的转换,则具有恰好一个不可能转换的完整循环的总和是 -6、-2、+2 或 +6。要认识到整个循环包含一个(或多个)不可能的转换,我们必须为不可能的转换分配一个大于 10 的数字,以便整个循环的总和大于 4。但是,如果我们分配数字 14 ,我们不仅认识到不可能转换的存在,而且当且仅当达到中性状态时,聚合的模数和 4 再次为零。
执行
所有这些考虑因素都可以在以下 Python 脚本中观察到,该脚本旨在用于 Raspberry Pi。rotary函数首先更新,一个四位数的lrmem二进制变量,其中前两位表示前一个状态,后两位表示当前状态,本质上是最后一次转换。该列表TRANS包含每个转换的值,如下表所示。
lrmem | 转型 | TRANS | 评论 |
|---|---|---|---|
| 0b0000 | 00→00 | 0 | 没有动静 |
| 0b0001 | 00→01 | -1 | 向左移动 |
| 0b0010 | 00→10 | +1 | 向右移动 |
| 0b0011 | 00→11 | +14 | 不可能的运动 |
| 0b0100 | 01→00 | +1 | 向右移动 |
| 0b0101 | 01→01 | 0 | 没有动静 |
| 0b0110 | 01→10 | +14 | 不可能的运动 |
| 0b0111 | 01→11 | -1 | 向左移动 |
| 0b1000 | 10→00 | -1 | 向左移动 |
| 0b1001 | 10→01 | +14 | 不可能的运动 |
| 0b1010 | 10→10 | 0 | 没有动静 |
| 0b1011 | 10→11 | +1 | 向右移动 |
| 0b1100 | 11→00 | +14 | 不可能的运动 |
| 0b1101 | 11→01 | +1 | 向右移动 |
| 0b1110 | 11→10 | -1 | 向左移动 |
| 0b1111 | 11→11 | 0 | 没有动静 |
然后该rotary函数更新lrsum包含当前聚合的变量。当达到中性状态时,该功能相应地起作用。
树莓派的 Python 代码
import pigpiopi = pigpio.pi()# -1: left transition, +1: right transition, 0: no transition and 14: impossible transition
TRANS = [0, -1, 1, 14, 1, 0, 14, -1, -1, 14, 0, 1, 14, 1, -1, 0]
LEFT = 16
RIGHT = 20
PUSH = 21def rotary():global lrmemglobal lrsuml = pi.read(LEFT)r = pi.read(RIGHT)lrmem = (lrmem % 4)*4 + 2*l + rlrsum = lrsum + TRANS[lrmem]# encoder not in the neutral stateif(lrsum % 4 != 0): return(0)# encoder in the neutral stateif (lrsum == 4):lrsum=0return(1)if (lrsum == -4):lrsum=0return(-1)# lrsum > 0 if the impossible transitionlrsum=0return(0)pi.set_mode(LEFT, pigpio.INPUT)
pi.set_mode(RIGHT, pigpio.INPUT)
pi.set_mode(PUSH, pigpio.INPUT)
pi.set_pull_up_down(LEFT, pigpio.PUD_UP)
pi.set_pull_up_down(RIGHT, pigpio.PUD_UP)
pi.set_pull_up_down(PUSH, pigpio.PUD_UP)lrmem = 3
lrsum = 0
num = 0
print(num)while(True):res = rotary()if (res!=0):num=num + resprint(num)if(pi.read(PUSH)==0):break
请注意,不需要额外的电子组件,因为脚本使用内置的 Raspberry Pi 上拉电阻。
Arduino的C代码
#define LEFT 2
#define RIGHT 3
#define PUSH 4uint8_t lrmem = 3;
int lrsum = 0;
int num = 0;int8_t rotary()
{static int8_t TRANS[] = {0,-1,1,14,1,0,14,-1,-1,14,0,1,14,1,-1,0};int8_t l, r;l = digitalRead(LEFT);r = digitalRead(RIGHT);lrmem = ((lrmem & 0x03) << 2) + 2*l + r;lrsum = lrsum + TRANS[lrmem];/* encoder not in the neutral state */if(lrsum % 4 != 0) return(0);/* encoder in the neutral state */if (lrsum == 4){lrsum=0;return(1);}if (lrsum == -4){lrsum=0;return(-1);}/* lrsum > 0 if the impossible transition */lrsum=0;return(0);
}void setup()
{pinMode(LEFT, INPUT);pinMode(RIGHT, INPUT);pinMode(PUSH, INPUT);pinMode(LEFT, INPUT_PULLUP);pinMode(RIGHT, INPUT_PULLUP);pinMode(PUSH, INPUT_PULLUP);Serial.begin(9600);Serial.println(num, DEC);
}void loop()
{int8_t res;res = rotary();if (res!=0){num = num + res;Serial.println(num);}if (digitalRead(PUSH) == 0){Serial.println(num);delay(250);}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
