上篇讲了编码器的原理——增量式和绝对式的区别、信号处理、机械安装、选型考虑。今天讲编码器在实际项目中的应用:怎么用编码器测速、测距,以及怎么在闭环控制中使用编码器数据。
面试时候被问"编码器怎么用",很多人会说"计数脉冲算角度"。但面试官想听的是更工程化的内容:怎么从脉冲算出速度和距离?速度估计有哪些方法?编码器数据怎么用在PID控制里?里程计怎么和IMU融合?
从编码器到速度和距离
编码器的基本输出是脉冲计数。从脉冲计数到物理量(角度、角速度、线速度、距离)的转换需要知道几个参数:编码器分辨率(counts per revolution, CPR)、减速比(gear ratio)、轮子半径。
import numpy as np class EncoderOdometry: """编码器里程计""" def __init__(self, cpr, gear_ratio, wheel_radius, wheel_base): self.cpr = cpr # 编码器分辨率(counts/rev) self.gear_ratio = gear_ratio # 减速比(电机转/轮子转) self.wheel_radius = wheel_radius # 轮子半径(m) self.wheel_base = wheel_base # 左右轮间距(m) # 每个脉冲对应的轮子转角(rad) self.rad_per_count = (2 * np.pi) / (cpr * gear_ratio) self.left_count = 0 self.right_count = 0 self.x = 0.0 # 机器人位置 self.y = 0.0 self.theta = 0.0 # 机器人朝向 def update(self, left_delta, right_delta, dt): """更新里程计 left_delta, right_delta: 左右轮编码器增量(counts) dt: 时间间隔(s) """ # 转换为轮子转角 dtheta_left = left_delta * self.rad_per_count dtheta_right = right_delta * self.rad_per_count # 转换为轮子线位移 dl = dtheta_left * self.wheel_radius dr = dtheta_right * self.wheel_radius # 差速运动学 d_center = (dl + dr) / 2 d_theta = (dr - dl) / self.wheel_base # 更新位姿(用中心角度近似) self.theta += d_theta self.x += d_center * np.cos(self.theta - d_theta/2) self.y += d_center * np.sin(self.theta - d_theta/2) return self.x, self.y, self.theta这段代码是差速机器人里程计的核心。每个控制周期(比如10ms),读取左右轮编码器的增量,转换成线位移和角位移,然后积分得到机器人的位姿。
编码器参数的实际标定
里程计代码看着简单,但里面的参数——轮子半径、轮距、编码器分辨率——如果不准,算出来的位姿就是错的。很多新人拿到编码器就直接写代码,忽略了标定这一步,结果里程计跑起来误差巨大。
轮子半径的标定方法:在地上画一条1米长的直线,让机器人沿直线走,编码器记录走过的距离。如果编码器显示走了1.05米,说明轮子半径参数偏大,需要按比例缩小。反复几次,直到编码器距离和实际距离一致。这个方法简单但有效,精度能到毫米级。
轮距(wheel_base)的标定更关键,因为它直接影响角度估计。方法很简单:让机器人原地旋转360度,比较编码器估计的旋转角度和实际角度。如果编码器说转了370度但实际只转了360度,说明轮距参数偏小。调整轮距参数直到旋转角度匹配。
def calibrate_wheel_radius(encoder_distance, actual_distance, initial_radius): """根据直线行走测试校准轮子半径""" return initial_radius * (actual_distance / encoder_distance) def calibrate_wheel_base(encoder_angle, actual_angle, initial_base): """根据原地旋转测试校准轮距""" return initial_base * (encoder_angle / actual_angle)还有一个容易忽略的问题:编码器的零位对齐。增量式编码器上电后不知道当前位置,需要找一个参考点。常见做法是用一个霍尔传感器或者光电开关作为归零信号。机器人每次启动时,先慢速旋转到归零位置,然后清零编码器计数。如果归零位置没对准,后续的里程计数据全部会有偏差。
实际项目中,我一般会让机器人每次上电后执行一个"初始化舞蹈"——先左右各转一圈,找到归零信号,然后原地转几圈确认编码器读数一致性。这个过程大概需要10秒,但能保证每次启动的初始状态是可靠的。
编码器数据的滤波处理
编码器数据虽然精度高,但也不是完美的。实际使用中需要做适当的滤波。
速度估计中的量化噪声是个常见问题。低速时,编码器每个采样周期可能只有几个脉冲,量化误差很大。前面提到的M法在低速时精度差,T法在高速时精度差,M/T法结合了两者优势但实现更复杂。实际项目中,如果速度范围不大,用M法加一个简单的滑动平均滤波就够了。
另一个常见问题是编码器数据的突变。电磁干扰可能导致偶尔出现一个异常的脉冲计数,导致速度估计出现尖峰。处理方法很简单:设置一个速度变化率上限,如果相邻两个周期的速度变化超过这个上限,就认为是异常值,用上一次的合法值替代。
面试中怎么聊
面试官问编码器应用,按这个顺序回答:先说从脉冲到物理量的转换(角度、速度、距离),再说速度估计方法(M法、T法、M/T法),然后说闭环控制中的使用(PID反馈),最后说里程计的局限性和多传感器融合。
如果面试官追问"编码器里程计和IMU里程计怎么融合",你可以说:用EKF,状态量包括位置(x,y)、朝向θ、线速度v、角速度ω。编码器提供v和ω的观测(通过差速运动学),IMU提供ω的直接观测(陀螺仪),两者互补。编码器的短期精度好但长期漂移,IMU的短期精度高但长期漂移(陀螺仪零偏),融合后可以得到短期和长期都可靠的位姿估计。
如果面试官追问"怎么处理轮子打滑",你可以说:轮子打滑时编码器数据不可靠,可以通过检测编码器估计速度和IMU估计速度的差异来判断是否打滑。如果差异超过阈值,降低编码器数据的权重(在EKF中增大编码器的观测噪声协方差)。更高级的方案是用滑模观测器或者自适应滤波来处理。
如果面试官追问"工业场景下编码器有什么特别的注意事项",补充一点:工业AGV对编码器的可靠性要求很高,通常会用双编码器冗余设计——每个轮子装两个编码器,互相校验。如果一个编码器数据异常(比如和另一个差异过大),系统自动切换到单编码器模式并报警。另外,工业环境振动大,编码器的机械安装要用防振联轴器,信号线要用工业级的屏蔽线缆,接头用M12航空插头而不是普通的杜邦线。这些细节在面试中说出来,会让面试官觉得你有实际工程经验。
再补充一个编码器里程计校准的实战技巧。很多团队在调里程计参数时只关注轮径和轮距,忽略了编码器的计数方向。有一次我们新装了一台机器人,发现里程计的朝向和实际相反——机器人往前走,odom报告的yaw角在减小而不是增大。排查了半天最后发现是左轮编码器的A/B相序接反了,导致计数方向反了。解决办法是在MCU的编码器配置里把计数模式从"上升沿+下降沿"改成"上升沿-下降沿"。这种低级错误在新硬件调试时很常见,建议每次装完编码器后先手动转一下轮子,看计数方向是否正确,再做后续标定。
下一篇讲力/力矩传感器——机器人"触觉"的实现方案。从运动传感器转到力觉传感器,这是机器人交互能力的关键。
如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。
「机器人软件开发面试·从入门到精通」连载系列上一篇:第159篇 编码器原理——增量式和绝对式的区别与选型 下一篇预告:第161篇 力/力矩传感器——机器人"触觉"的实现方案
有任何问题欢迎评论区留言,我会尽量回复。