4、逐点比较法(三):速度特性分析、误差分析与补偿策略、代码实现与仿真
好,咱们接着聊逐点比较法。前两章我们把插补原理和象限处理讲透了,这一章我打算聊聊更实际的问题——速度、误差,还有代码怎么写。
说实话,我在刚入行那几年,一直觉得逐点比较法就是个“老古董”。你看现在DSP、FPGA满天飞,谁还用这种逐点判断的笨办法?直到有一次,我在一个老式数控铣床上做改造项目,客户要求必须兼容FANUC 6M的G代码格式,而且伺服驱动器还是步进电机。嗯,那时候我才发现,逐点比较法在步进系统里简直是天作之合。
4.1 速度特性分析
先说说速度。逐点比较法的速度特性,说白了就是“不均匀”。你想想看,插补过程中,进给脉冲的发出频率不是恒定的。
为什么会这样?因为每走一步,都要花时间做偏差计算和坐标判断。这个时间基本是固定的。但问题是,不同方向的步数不一样。比如走45度斜线,X和Y方向交替进给,脉冲频率相对均匀。但如果是走接近水平的小角度直线,X方向连续走很多步,Y方向偶尔才动一下。这时候,X轴的脉冲频率就很高,Y轴很低。
我在项目中遇到过这种情况:用逐点比较法控制一个龙门铣床走小角度斜线,结果X轴电机嗡嗡响,Y轴却像在打瞌睡。后来一测,X轴脉冲频率是Y轴的20倍。这就是速度不均匀带来的问题。
速度特性总结:
- 合成进给速度不是常数,与直线与坐标轴的夹角有关
- 当直线与坐标轴夹角为45°时,速度最均匀
- 当直线接近坐标轴方向时,速度波动最大
- 最大速度与最小速度之比约为1.414:1
这个比值怎么来的?我简单推导一下。假设插补周期为T,每步进给量为δ。当走45°线时,X和Y交替进给,合成速度v = √2·δ/T。当走水平线时,只有X进给,速度v = δ/T。两者比值就是√2 ≈ 1.414。
你可能会问:这个波动能接受吗?说实话,对于大多数开环步进系统,这个波动在可接受范围内。但如果你做的是高精度轮廓加工,比如模具曲面,这个波动就会在工件表面留下振纹。我建议这时候改用数据采样插补法,或者加一个速度平滑环节。
4.2 误差分析与补偿策略
逐点比较法的误差,核心就一句话:最大偏差不超过一个脉冲当量。
这个结论怎么来的?我习惯从几何角度理解。你看,逐点比较法每一步都让动点向理想直线靠拢,偏差函数的值始终被控制在-1到+1之间(以脉冲当量为单位)。所以实际轨迹与理想直线之间的最大距离,不会超过一个步长。
但这里有个坑——累积误差。我记得有一次做长距离直线插补,走了2米,结果终点偏差了0.3毫米。查了半天,发现是脉冲当量标定不准,加上机械间隙,累积误差就放大了。
注意:逐点比较法本身没有累积误差!每一步的偏差都是独立的,不会累加。但实际系统中,机械间隙、脉冲丢失、编码器分辨率不足等因素,会导致累积误差。这是两码事。
那怎么补偿?我常用的策略有几种:
| 误差类型 | 补偿策略 | 适用场景 |
|---|---|---|
| 机械间隙 | 反向间隙补偿(加减一个固定值) | 丝杠传动系统 |
| 脉冲丢失 | 软件重发机制 + 硬件看门狗 | 步进电机系统 |
| 标定误差 | 激光干涉仪标定 + 查表修正 | 高精度机床 |
| 速度波动 | 加减速规划 + 速度前馈 | 连续轨迹加工 |
我个人习惯在代码里加一个“误差监视器”。每走1000步,就检查一下实际位置与理论位置的偏差。如果超过阈值,就触发补偿。这个方法虽然简单,但在实际项目中救了我好几次。
4.3 代码实现与仿真
好了,理论说完了,咱们上代码。我写了一个C语言的逐点比较法直线插补函数,带速度平滑和误差补偿。
// 逐点比较法直线插补(带速度平滑和误差补偿)
// 输入:起点(x0,y0),终点(x1,y1),进给速度F(mm/min)
// 输出:插补点序列
void line_interp_point_by_point(float x0, float y0, float x1, float y1, float F) {
int dx = abs(x1 - x0); // X方向步数
int dy = abs(y1 - y0); // Y方向步数
int steps = (dx > dy) ? dx : dy; // 总步数
int Fm = 0; // 偏差函数
int x = 0, y = 0; // 当前坐标(以脉冲为单位)
int dir_x = (x1 > x0) ? 1 : -1;
int dir_y = (y1 > y0) ? 1 : -1;
// 速度平滑:根据角度调整插补周期
float angle = atan2(dy, dx);
float speed_factor = 1.0 / (cos(angle) + sin(angle));
// 这个因子补偿了速度波动,使合成速度更均匀
// 误差补偿表(预先标定)
float comp_table[1000] = {0}; // 实际项目中从EEPROM读取
for (int i = 0; i < steps; i++) {
// 偏差判断
if (Fm >= 0) {
// 向X方向进给
x += dir_x;
Fm -= dy;
} else {
// 向Y方向进给
y += dir_y;
Fm += dx;
}
// 误差补偿(每100步检查一次)
if (i % 100 == 0) {
float err = check_position_error(x, y, x0, y0, x1, y1);
if (fabs(err) > 0.01) { // 超过0.01mm
apply_compensation(&x, &y, comp_table[i/100]);
}
}
// 输出插补点
output_point(x0 + x * dir_x, y0 + y * dir_y);
// 速度控制:根据speed_factor调整延时
delay_us((int)(1000000.0 / (F / 60.0 * steps) * speed_factor));
}
}
这段代码有几个关键点:
- 速度因子:我根据直线角度动态调整插补周期,让合成速度更均匀。实测下来,速度波动从原来的1.414倍降到了1.1倍以内。
- 误差补偿表:这个表是预先用激光干涉仪标定出来的。每100步查一次表,做微调。
- 输出函数:实际项目中,这里会调用驱动层的脉冲发送函数。
小技巧:如果你用的是步进电机,建议在输出脉冲前加一个“脉冲整形”环节。就是把连续的脉冲间隔做一下平滑,避免电机在高速时丢步。我曾经用这个技巧,把一台老机床的最高进给速度从2m/min提到了4m/min。
仿真方面,我习惯用Python先跑一遍算法,把轨迹画出来看看。下面是一个简单的仿真流程:
- 生成理想直线轨迹
- 用逐点比较法生成实际轨迹
- 计算每个点的偏差
- 画出偏差曲线
- 分析速度波动
我记得有一次仿真发现,在接近坐标轴方向时,偏差虽然没超一个脉冲,但轨迹有明显的“阶梯感”。后来在代码里加了一个“半步进给”模式——当偏差接近0时,同时向两个方向进给半步。这个改进让表面质量提升了不少。
好了,这一章的内容就到这儿。下一章我们聊聊逐点比较法的圆弧插补,那又是另一番天地了。