4. 关节编码器:增量式编码器、式编码器、关节角度读取与校准

四足机器人的每条腿,说白了就是一个多关节机械臂。要让机器人站稳、走路、跳跃,首先得知道每个关节转到了什么位置。这个任务,就交给编码器了。

我刚开始做四足机器人时,觉得编码器不就是测个角度嘛,能有多复杂?结果第一次让机器人站起来,腿抖得像筛糠一样。查了半天,发现是编码器零点没校准。嗯,从那以后我再也不敢小看这个环节了。

4.1 编码器的两种主流方案

关节编码器分两大类:增量式和式。它们各有各的脾气,选型时得想清楚。

特性 增量式编码器 式编码器
输出信号 脉冲序列(A、B、Z相) 数字编码(二进制、SSI、SPI)
断电记忆 不记忆位置 记忆位置
分辨率 取决于线数,可做很高 取决于位数,通常12-20位
上电行为 需要回零操作 直接读取当前角度
抗干扰能力 脉冲易受干扰 数字信号更稳定
成本 较低 较高

增量式编码器,我习惯叫它"脉冲计数器"。它输出A、B两路相位差90°的方波,通过判断哪个信号先跳变,就能知道电机是正转还是反转。还有一个Z相脉冲,每转一圈输出一次,用来做零点参考。

式编码器就省心多了。它内部有一圈格雷码盘,每个位置对应唯一的编码值。上电就能告诉你关节在什么角度,不需要回零。我在项目里用过BISS-C协议的式编码器,16位分辨率,一圈65536个位置,精度完全够用。

我的建议:如果做科研样机,预算有限,增量式编码器配合霍尔传感器做上电寻零,性价比很高。如果是量产产品,或者对安全性要求高(比如人形机器人),直接上式编码器,省掉很多麻烦。

4.2 关节角度读取实战

不管用哪种编码器,最终都要把原始信号换算成关节角度。这里我以STM32平台为例,讲讲具体怎么读。

4.2.1 增量式编码器读取

增量式编码器通常接在定时器的编码器模式引脚上。STM32的定时器自带硬件解码,不用自己写逻辑判断。

// 初始化定时器为编码器模式
void Encoder_Init(TIM_TypeDef* TIMx) {
    TIM_EncoderInterfaceConfig(TIMx, TIM_ENCODERMODE_TI12,
                               TIM_ICPOLARITY_RISING, TIM_ICPOLARITY_RISING);
    TIMx->ARR = 0xFFFF;  // 最大计数值
    TIM_Cmd(TIMx, ENABLE);
}

// 读取当前脉冲计数值
int16_t Encoder_GetCount(TIM_TypeDef* TIMx) {
    return (int16_t)TIM_GetCounter(TIMx);
}

// 换算为角度(假设编码器线数为500线,4倍频后每圈2000脉冲)
float Encoder_ToAngle(int16_t pulse_count) {
    float angle = (float)pulse_count / 2000.0f * 360.0f;
    return angle;
}

这里有个坑:计数值会溢出。定时器是16位的,最大65535。如果电机一直朝一个方向转,计数值会从65535翻到0,角度就跳变了。我一般用有符号的int16_t来读,这样溢出时自动变成负数,配合外部圈数计数器就能解决。

我曾经踩过的坑:增量式编码器上电时计数值是随机的。如果不做回零,机器人一上电就以为关节在0度,直接给一个很大的控制量,轻则电机过载,重则扫齿。所以,增量式编码器必须配合回零流程。

4.2.2 式编码器读取

式编码器一般通过SPI或SSI接口读取。以AS5048A为例,14位分辨率,SPI通信。

// 读取AS5048A角度值(14位)
uint16_t AS5048A_ReadAngle(void) {
    uint16_t raw_value;
    // 发送0xFFFF读取命令
    HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)0xFFFF, (uint8_t*)&raw_value, 2, 100);
    // 解析数据,去掉奇偶校验位
    raw_value = (raw_value >> 2) & 0x3FFF;
    return raw_value;
}

// 换算为角度
float AS5048A_ToDegree(uint16_t raw) {
    float angle = (float)raw / 16384.0f * 360.0f;
    return angle;
}

式编码器读取时要注意时序。SPI时钟频率不能太高,否则数据容易出错。我一般用1MHz,稳定可靠。

4.3 关节角度校准——让机器人认识"零位"

校准是编码器应用中最容易被忽视、但最关键的环节。你想想看,编码器装在电机轴上,但电机轴和关节输出轴之间还有减速器、连杆。编码器读到的角度,和关节实际角度之间,存在一个固定的偏移量。

校准的目的,就是找到这个偏移量,把它补偿掉。

4.3.1 机械零位标定

我常用的方法很简单:把机器人每条腿手动掰到标准站立姿态,然后记录此时每个编码器的读数,作为零位偏移。

// 零位偏移结构体
typedef struct {
    float hip_offset;   // 髋关节零位偏移(度)
    float thigh_offset; // 大腿关节零位偏移
    float calf_offset;  // 小腿关节零位偏移
} JointOffset_t;

JointOffset_t joint_offset[4]; // 四条腿

// 标定函数:在标准姿态下调用一次
void CalibrateZeroPosition(void) {
    for(int i = 0; i < 4; i++) {
        joint_offset[i].hip_offset   = ReadHipAngle(i);
        joint_offset[i].thigh_offset = ReadThighAngle(i);
        joint_offset[i].calf_offset  = ReadCalfAngle(i);
    }
    // 保存到Flash
    SaveOffsetToFlash(joint_offset);
}

// 实际使用时减去偏移
float GetActualAngle(int leg_id, JointType_t joint) {
    float raw_angle = ReadRawAngle(leg_id, joint);
    float offset = joint_offset[leg_id].hip_offset; // 举例
    return raw_angle - offset;
}

小技巧:标定时可以用一个水平仪或者角度尺,确保每条腿的关节真的在标准位置。我见过有人凭感觉掰,结果四条腿零位不一致,机器人走起来一瘸一拐的。

4.3.2 软件滤波与异常处理

编码器读数偶尔会跳变。尤其是增量式编码器,线缆屏蔽不好时,一个脉冲干扰就能让角度突变几度。机器人控制器如果直接拿这个突变值去算力矩,关节会猛地抖一下。

我一般加一个简单的限幅滤波:

// 限幅滤波
float AngleFilter(float current_angle, float last_angle, float max_delta) {
    float delta = current_angle - last_angle;
    if(delta > max_delta) {
        return last_angle + max_delta;
    } else if(delta < -max_delta) {
        return last_angle - max_delta;
    } else {
        return current_angle;
    }
}

// 使用示例
float last_angle = 0.0f;
float current_angle = ReadEncoder();
float filtered_angle = AngleFilter(current_angle, last_angle, 5.0f); // 每周期最大变化5度
last_angle = filtered_angle;

这个滤波器很简单,但很实用。最大变化量要根据机器人的运动速度来设。四足机器人跑步时,关节角速度可能达到每秒几百甚至上千度,限幅值设得太小反而会限制正常运动。

4.4 知识结构总览

下面这张图把编码器相关的核心逻辑串起来了,方便你对照理解。

关节编码器知识结构 增量式编码器 式编码器 A/B/Z 三相信号 需回零操作 分辨率高、成本低 数字编码输出 上电即知位置 抗干扰、精度稳定 关节角度读取 机械零位标定 记录偏移量并补偿 限幅滤波 剔除异常跳变值 输出:稳定、准确的关节角度

4.5 避坑指南与经验总结

做了这么多年机器人,编码器相关的坑我踩过不少。挑几个典型的说说:

  • 线缆屏蔽:增量式编码器的脉冲信号很容易被电机驱动器的PWM干扰。我建议用双绞屏蔽线,屏蔽层单端接地。曾经有一次,我偷懒用了普通排线,结果机器人一走路,编码器读数就乱跳,排查了两天才找到原因。
  • 上电顺序:式编码器虽然不用回零,但上电瞬间的SPI通信可能出错。我习惯在控制器初始化完成后,延时50ms再读第一次角度,确保编码器内部已经稳定。
  • 机械安装:编码器与电机轴的连接要牢固。我用过顶丝固定的编码器,振动几次就松了,角度读数开始漂移。后来改用弹簧夹紧式,再没出过问题。
  • 多圈处理:如果关节可以连续旋转(比如髋关节),增量式编码器需要记录圈数。我一般用一个32位变量,高16位记圈数,低16位记当前计数值,这样能覆盖±32767圈的范围。

核心要点:编码器是机器人的"眼睛"。眼睛看不清,腿脚再灵活也没用。选型时想清楚要不要断电记忆,调试时老老实实做零位标定,运行时加一层滤波保护。这三步做到位,关节角度数据基本就稳了。