4、电子凸轮表设计:CAM Table的数据结构、位置点与速度点的映射、插值算法

好,咱们今天聊电子凸轮表。说白了,它就是电子凸轮的大脑。你想想看,机械凸轮靠物理轮廓决定运动轨迹,电子凸轮靠什么?靠这张表。我做了这么多年运动控制,见过太多工程师把精力全放在算法上,结果CAM Table没设计好,现场跑起来一塌糊涂。嗯,这里咱们得好好捋一捋。

4.1 CAM Table的数据结构

先说说数据结构。我个人习惯把CAM Table看作一张二维表,横轴是主轴位置,纵轴是从轴位置。但实际工程中,远不止这么简单。

一个标准的CAM Table,至少包含以下几列:

字段名 数据类型 说明
MasterPosition Float / Int32 主轴位置点(角度或脉冲数)
SlavePosition Float / Int32 从轴位置点
SlaveVelocity Float 从轴速度(可选,用于速度前馈)
SlaveAcceleration Float 从轴加速度(可选,用于高阶控制)

为什么要有速度和加速度?我刚开始做凸轮时,只存位置点,结果跑起来从轴抖得跟筛子似的。后来才明白,光有位置映射,控制器不知道你期望的速度变化率,全靠PID硬扛,不出问题才怪。

核心要点:CAM Table不仅仅是位置映射表,它应该包含完整的运动学信息。位置、速度、加速度,三者缺一不可。

另外,数据点的数量也很关键。点太少,插值精度不够;点太多,控制器内存吃不消。我一般建议:

  • 简单运动(如飞剪):32-64个点足够
  • 复杂曲线(如异形瓶灌装):128-256个点
  • 超高精度(如电子装配):512个点以上,但要注意扫描周期

4.2 位置点与速度点的映射

位置映射好理解,主轴转一圈,从轴走一个特定轨迹。但速度映射,很多人会搞混。

你想想看,电子凸轮的本质是什么?是主轴位置到从轴位置的函数关系。速度呢?是这个函数的一阶导数。也就是说,速度映射不是独立存在的,它由位置映射的斜率决定

举个例子:

// 假设主轴从0°到360°,从轴从0mm到100mm
// 如果匀速运动,速度就是 100mm / 360° = 0.278 mm/°
// 但如果中间有停顿,速度就得降到0

// 正确的做法:先定义位置曲线,再计算速度曲线
for (int i = 0; i < tableSize; i++)
{
    // 位置映射
    camTable[i].MasterPos = i * 360.0 / tableSize;
    camTable[i].SlavePos = positionFunction(camTable[i].MasterPos);
    
    // 速度映射(中心差分法)
    if (i == 0)
        camTable[i].SlaveVel = (camTable[1].SlavePos - camTable[0].SlavePos) / 
                               (camTable[1].MasterPos - camTable[0].MasterPos);
    else if (i == tableSize - 1)
        camTable[i].SlaveVel = (camTable[i].SlavePos - camTable[i-1].SlavePos) / 
                               (camTable[i].MasterPos - camTable[i-1].MasterPos);
    else
        camTable[i].SlaveVel = (camTable[i+1].SlavePos - camTable[i-1].SlavePos) / 
                               (camTable[i+1].MasterPos - camTable[i-1].MasterPos);
}

我的经验:速度映射最好在离线阶段计算好,不要放到控制器里实时算。我曾经在一个项目里图省事,让PLC实时差分算速度,结果扫描周期从1ms飙到3ms,整个系统都跟着遭殃。

4.3 插值算法:线性插值与样条插值

好了,表建好了,数据也填进去了。但主轴位置不可能刚好落在你定义的点上,怎么办?插值。

4.3.1 线性插值

线性插值最简单,也最常用。说白了,就是两点之间画直线。

// 线性插值实现
float LinearInterpolate(float masterPos, CAM_POINT* table, int tableSize)
{
    // 找到主轴位置所在的区间
    int idx = 0;
    for (int i = 0; i < tableSize - 1; i++)
    {
        if (masterPos >= table[i].MasterPos && masterPos <= table[i+1].MasterPos)
        {
            idx = i;
            break;
        }
    }
    
    // 计算比例因子
    float ratio = (masterPos - table[idx].MasterPos) / 
                  (table[idx+1].MasterPos - table[idx].MasterPos);
    
    // 线性插值
    return table[idx].SlavePos + ratio * (table[idx+1].SlavePos - table[idx].SlavePos);
}

线性插值的优点:计算快,占用资源少。缺点呢?速度不连续。在数据点处,速度会突变,导致从轴产生冲击。

注意:如果CAM Table点数太少,线性插值会产生明显的速度跳变。我曾经在一个包装机上吃过这个亏,32个点做飞剪,切出来的包装袋边缘全是毛刺。后来加到128个点,问题才解决。

4.3.2 样条插值

样条插值就高级多了。它保证了一阶导数(速度)甚至二阶导数(加速度)的连续性。我常用的三次样条,说白了就是用三次多项式把相邻点连起来,同时保证连接处平滑过渡。

// 三次样条插值(简化版)
float CubicSplineInterpolate(float masterPos, CAM_POINT* table, int tableSize)
{
    // 这里假设已经计算好了样条系数 a[i], b[i], c[i], d[i]
    // 实际工程中,这些系数需要离线计算好
    
    int idx = FindInterval(masterPos, table, tableSize);
    float dx = masterPos - table[idx].MasterPos;
    
    // 三次样条公式:S(x) = a + b*dx + c*dx^2 + d*dx^3
    return a[idx] + b[idx]*dx + c[idx]*dx*dx + d[idx]*dx*dx*dx;
}

样条插值的优点:平滑,速度连续,加速度也连续。缺点:计算量大,而且容易出现过冲(overshoot)。

避坑指南:我曾经在一个高精度贴片机上用样条插值,结果在曲线拐弯处出现了过冲,从轴直接冲过了头,把元件贴歪了。后来加了边界条件约束,才把过冲压下去。所以,用样条插值一定要检查曲线是否单调,必要时加阻尼处理。

4.4 两种插值算法的选择策略

到底用线性还是样条?我个人的经验是:

  • 高速、低精度场景(如输送带同步):线性插值就够了,省CPU资源
  • 中速、中精度场景(如飞剪、追剪):线性插值+足够多的数据点
  • 低速、高精度场景(如电子装配、精密加工):样条插值,但要做好过冲防护
  • 超高速场景(如纺织、印刷):建议用线性插值+硬件加速,样条的计算延迟可能跟不上

下面这张图,是我自己总结的电子凸轮表设计流程,你看看就明白了:

电子凸轮表设计流程 1. 需求分析 2. 确定数据点数 3. 生成位置点 4. 计算速度/加速度 5. 选择插值算法 6. 仿真验证 关键决策点 • 点数少→线性插值 • 点数多→样条插值 • 高速→线性+硬件加速 • 高精度→样条+防过冲 • 速度映射离线计算 • 加速度可选但推荐 常见问题 1. 速度跳变→增加点数 2. 样条过冲→加约束 3. 内存不足→压缩点数 4. 扫描周期长→用线性

嗯,说到这儿,CAM Table的核心内容基本就这些了。数据结构要完整,位置速度映射要准确,插值算法要根据场景选。我这些年做过的项目,凡是出问题的,十有八九是这三块没做好。你只要把这三点吃透了,电子凸轮这块基本就稳了。

最后说一句:别迷信高级算法。我见过有人非要用五次样条,结果现场调了三天没调通,换成线性插值+128个点,半小时搞定。工具是死的,人是活的,选最合适的,别选最花哨的。


公众号:蓝海资料掘金营,微信deep3321