Python读取光学正交编码器的最高效算法求解
解决正交编码器计数丢失问题的优化方案
首先得说,你担心的计数丢失问题确实是正交编码器驱动里的核心痛点——轮询方式很容易因为主循环延迟错过脉冲,尤其是多电机同时运行时,效率和精度根本没法保证。咱们直接从你的CMotor类入手,给你优化出可靠的实现:
核心优化思路
- 放弃轮询,用GPIO边缘中断处理AB相脉冲,这样能保证每一个脉冲都被捕获
- 正交编码的方向判断逻辑要严谨,基于AB相的电平变化顺序区分正反转
- 多电机场景下,用类封装独立的中断回调,避免资源冲突
- 加入计数溢出保护(如果用的是固定位数的计数器)
完整优化后的CMotor类实现
import RPi.GPIO as GPIO class CMotor: def __init__(self, pin_a, pin_b, motor_id): self.PinA = pin_a self.PinB = pin_b self.MotorID = motor_id self.Count = 0 # 当前计数值 self.LastA = 0 self.LastB = 0 # 初始化GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(self.PinA, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(self.PinB, GPIO.IN, pull_up_down=GPIO.PUD_UP) # 绑定边缘中断,上升沿和下降沿都要捕获 GPIO.add_event_detect(self.PinA, GPIO.BOTH, callback=self._rotary_callback) GPIO.add_event_detect(self.PinB, GPIO.BOTH, callback=self._rotary_callback) def _rotary_callback(self, channel): # 读取当前AB相电平 current_a = GPIO.input(self.PinA) current_b = GPIO.input(self.PinB) # 正交编码器方向判断逻辑 # 正转:A相先变,B相滞后;反转:B相先变,A相滞后 if self.LastA != current_a: # A相发生变化 if current_a != current_b: self.Count += 1 else: self.Count -= 1 elif self.LastB != current_b: # B相发生变化 if current_b == current_a: self.Count += 1 else: self.Count -= 1 # 更新上次电平状态 self.LastA = current_a self.LastB = current_b def GetCount(self): # 线程安全的计数值读取(如果多线程访问的话可以加锁) return self.Count def ResetCount(self): # 重置计数 self.Count = 0 def Cleanup(self): # 释放GPIO资源 GPIO.remove_event_detect(self.PinA) GPIO.remove_event_detect(self.PinB) GPIO.cleanup((self.PinA, self.PinB))
关键细节说明
- 中断驱动:用
GPIO.add_event_detect监听AB相的所有边缘变化,相比轮询,能100%捕获每一个脉冲,完全避免计数丢失 - 方向判断:通过对比当前和上一次的AB相电平,严格按照正交编码的相位关系判断正反转,不会出现误判
- 多电机兼容:每个电机实例独立维护自己的引脚、计数和中断回调,多个电机同时运行时不会互相干扰
- 资源清理:加入
Cleanup方法,避免程序退出后GPIO资源泄漏
PD控制的配合建议
在你的PD控制循环里,只需要调用GetCount()获取当前位置即可,不用再处理计数逻辑:
# 示例:初始化三台电机 motor1 = CMotor(pin_a=23, pin_b=24, motor_id=1) motor2 = CMotor(pin_a=25, pin_b=8, motor_id=2) motor3 = CMotor(pin_a=7, pin_b=12, motor_id=3) # PD控制循环 try: while True: # 获取各电机当前位置 pos1 = motor1.GetCount() pos2 = motor2.GetCount() pos3 = motor3.GetCount() # 执行PD计算和电机驱动逻辑 # ... 你的PD控制代码 ... finally: # 退出时清理资源 motor1.Cleanup() motor2.Cleanup() motor3.Cleanup()
这样处理之后,你完全不用担心计数丢失的问题,定位精度能得到可靠保障,而且代码结构清晰,维护起来也方便。
内容的提问来源于stack exchange,提问作者Chino Picnik
相关产品推荐
相关产品推荐

