1. 项目概述
最近在调试一个基于STC32G12K128的多路舵机控制模块时,遇到了一个很有意思的问题:如何验证这个自制模块的输出精度是否足够可靠?毕竟在机器人控制、航模等应用中,舵机角度控制的精确度直接决定了整个系统的性能表现。我决定用光电旋转编码器来实测一下这个模块的线性特性,看看它和专业的DG1062信号发生器相比到底差多少。
2. 硬件准备与搭建
2.1 核心硬件选型
这次测试用到的几个关键硬件设备都很有讲究:
-
STC32G12K128主控:这是国产STC新一代32位8051内核MCU,主频最高35MHz,带硬件浮点运算单元。选择它主要是因为其PWM模块可以输出高精度的舵机控制信号,而且有丰富的定时器资源用于编码器接口。
-
增量式光电编码器:我选用的是600线AB相输出的型号,理论分辨率可以达到0.15度。这种非接触式测量方式比电位器式的角度传感器更耐用、精度更高。
-
测试舵机:为了具有代表性,我选择了常见的SG90微型舵机。它的工作电压4.8-6V,控制脉宽范围500-2500μs对应0-180度。
提示:编码器安装时要注意与舵机输出轴的同轴度,任何偏心都会引入测量误差。我使用3D打印了一个专用联轴器来确保两者严格同心。
2.2 电路连接要点
整个测试系统的信号流向是这样的:
code复制STC32 PWM输出 -> 舵机驱动电路 -> 舵机 -> 编码器 -> STC32编码器接口
几个关键连接细节:
- PWM信号线要尽量短,必要时加100Ω终端电阻防止反射
2.编码器AB相需要接上拉电阻(我用的是4.7kΩ)
3.所有数字地要单点共地,避免地环路干扰
4.舵机电源要单独走线,最好加1000μF以上的滤波电容
3. 软件实现细节
3.1 控制脉冲生成
STC32的PWM配置代码如下(关键参数已注释):
c复制void PWM_Init(void) {
PWMA_PS = 0; // 预分频器=1
PWMA_ARR = 59999; // 20ms周期(35MHz/(60000)=583.33Hz)
PWMA_CCR1 = 1500; // 初始1.5ms脉宽
PWMA_ENO |= 0x01; // 使能PWM输出
PWMA_BKR |= 0x80; // 主输出使能
}
这里ARR值设置为59999是为了得到标准的20ms舵机控制周期(35MHz/(59999+1)≈583.33Hz)。CCR1的值对应高电平时间,1500表示1.5ms。
3.2 编码器数据采集
STC32的定时器配置为编码器模式:
c复制void TIM4_Encoder_Init(void) {
T4T3M &= 0xFE; // 定时器4模式设置
T4T3M |= 0x06; // 编码器模式3(双沿计数)
T4H = 0; // 计数器高位清零
T4L = 0; // 计数器低位清零
T4T3M |= 0x08; // 定时器4使能
}
在编码器模式下,定时器会自动根据AB相信号的正交关系进行加减计数。读取T4L和T4H寄存器组合就能得到当前计数值。
4. 测试方法与数据分析
4.1 自动化测试流程
我编写了一个Python脚本来自动化整个测试过程,主要逻辑是:
- 通过UDP向STC32发送PWM脉宽指令
- 等待500ms让舵机稳定
- 读取编码器计数值
- 保存数据并绘制曲线
关键代码段:
python复制ddim = linspace(0.5, 2.5, 100) # 生成0.5-2.5ms的100个点
ndim = []
for d in ddim:
svsend('st f %5.2f\r'%d) # 发送脉宽指令
time.sleep(0.5) # 等待稳定
ispclearreceive()
ispsend() # 请求编码器数据
time.sleep(0.25)
strall = clipboard.paste().split("\r\n")
ss = strall[2].split(" ")
n = int(ss[0])*256+int(ss[1]) # 解析编码器值
if n > 0x7fff: n = n - 0x10000 # 处理负数
ndim.append(n)
tspsave("angle1", ddim=ddim, ndim=ndim) # 保存数据
4.2 实测数据解读
两次独立测试得到的曲线非常接近,这说明测量结果是可靠的。从数据中可以观察到几个关键特征:
-
整体线性度:在0.5-2.5ms脉宽范围内,角度变化呈现良好的线性关系,相关系数R²>0.999
-
局部非线性:在1.1ms和1.9ms附近存在明显的波动,两次测试中这些波动的位置基本重合
-
重复性误差:相同脉宽下,两次测量的角度最大偏差约3个编码器计数,对应实际角度约0.5度
将原始数据转换为角度值后的对比表:
| 脉宽(ms) | 编码器计数1 | 编码器计数2 | 角度1(°) | 角度2(°) |
|---|---|---|---|---|
| 0.50 | 575 | 580 | 86.25 | 87.00 |
| 1.00 | 300 | 298 | 45.00 | 44.70 |
| 1.50 | 0 | -2 | 0.00 | -0.30 |
| 2.00 | -300 | -302 | -45.00 | -45.30 |
| 2.50 | -575 | -578 | -86.25 | -86.70 |
5. 问题分析与优化建议
5.1 非线性来源探讨
经过多次验证,我认为这些非线性波动主要来自以下几个因素:
-
齿轮间隙:舵机内部的减速齿轮组存在微小的回程间隙,特别是在负载变化时会更明显
-
电位器非线性:虽然使用编码器测量,但舵机内部的位置反馈仍然是电位器,其线性度会影响闭环控制
-
PWM分辨率限制:35MHz时钟下,20ms周期时PWM最小步长约0.017μs,理论上足够但实际受限于MCU外设精度
5.2 性能优化方案
根据测试结果,我总结了几点优化建议:
- 增加软件校准:建立脉宽-角度查找表,对非线性段进行补偿
c复制// 示例校准代码
uint16_t PWM_Calibration(float angle) {
static const float calib_table[] = {
// 角度, 脉宽(us)
-90.0, 500,
-45.0, 1000,
0.0, 1500,
45.0, 2000,
90.0, 2500
};
// 插值计算...
}
-
改进供电质量:给舵机单独供电,并增加LC滤波,减少电压波动对控制精度的影响
-
机械减震措施:在舵机输出轴增加橡胶垫片,吸收微小振动
6. 实际应用验证
为了验证这个驱动模块的实际可用性,我将其应用到一个三自由度机械臂上。测试发现:
- 在慢速运动时,末端重复定位精度可达±0.8°
- 快速运动时由于惯性影响,精度会下降到±2.5°
- 通过增加运动平滑算法(如S曲线加减速),可以显著改善动态性能
一个实用的运动平滑算法实现:
c复制void SmoothMove(uint16_t target_ccr) {
static uint16_t current_ccr = 1500;
float step = (target_ccr > current_ccr) ? 5.0 : -5.0;
while(fabs(current_ccr - target_ccr) > 10) {
current_ccr += step;
PWMA_CCR1 = (uint16_t)current_ccr;
delay_ms(20);
step *= 0.95; // 逐步减小步长
}
PWMA_CCR1 = target_ccr;
}
经过一周的连续测试,这个自制舵机驱动模块表现稳定,完全可以替代专业的信号发生器用于一般的控制场合。虽然在极端精度要求下(如<0.5°)可能还需要进一步优化,但对于大多数机器人、航模应用已经绰绰有余了。