双电机龙门同步控制方案

核心问题

双侧驱动龙门在长时间运行后,横梁会发生扭斜甚至卡死。

根本原因:左右两侧负载不均、机械阻力差异、编码器标定差异 → 两侧速度不一致 → 同步误差累积 → 横梁变形

图表 代码
flowchart TD
    A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
    B --> C[同步误差累积]
    C --> D[横梁扭斜或卡死]
    
    style A fill:#FFE4E1,stroke:#DC143C
    style D fill:#FFE4E1,stroke:#DC143C
flowchart TD
    A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
    B --> C[同步误差累积]
    C --> D[横梁扭斜或卡死]
    
    style A fill:#FFE4E1,stroke:#DC143C
    style D fill:#FFE4E1,stroke:#DC143C
flowchart TD
    A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
    B --> C[同步误差累积]
    C --> D[横梁扭斜或卡死]
    
    style A fill:#FFE4E1,stroke:#DC143C
    style D fill:#FFE4E1,stroke:#DC143C
flowchart TD
    A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
    B --> C[同步误差累积]
    C --> D[横梁扭斜或卡死]
    
    style A fill:#FFE4E1,stroke:#DC143C
    style D fill:#FFE4E1,stroke:#DC143C

核心方案:交叉耦合控制

一个核心公式

同步误差 e_sync = (x1 - x2) - sync_offset
同步修正 sync_trim = Kps · e_sync + Kds · (v1 - v2)

左轴命令 = x_ref + common_trim - sync_trim
右轴命令 = x_ref + common_trim + sync_trim

其中:

  • common_trim = PID(e_avg) — 整体位置误差修正,推整根横梁前进
  • sync_offset — 回零时测得的左右机械零位固定差值
  • Kps、Kds — 同步环 P/D 参数

工作原理

场景左轴动作右轴动作效果
左轴偏快减小命令增大命令拉回一致
右轴偏快增大命令减小命令拉回一致

像"虚拟弹簧":同步误差为正时,左轴被拉慢、右轴被推快,自动消除扭斜。

图表 代码
flowchart TB
    subgraph "误差计算"
        e_avg["整体误差 e_avg
x_ref - (x1+x2)/2"] e_sync["同步误差 e_sync
(x1-x2) - sync_offset"] end subgraph "控制器" PID["整体 PID
common_trim = PID(e_avg)"] SYNC["同步 P+D
sync_trim = Kps·e_sync + Kds·(v1-v2)"] end subgraph "输出" cmdL["左轴 = x_ref + common - sync"] cmdR["右轴 = x_ref + common + sync"] end e_avg --> PID --> cmdL e_avg --> PID --> cmdR e_sync --> SYNC --> cmdL e_sync -->|"反向"| cmdR style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style PID fill:#90EE90,stroke:#2E8B57 style SYNC fill:#90EE90,stroke:#2E8B57 style cmdL fill:#87CEEB,stroke:#4682B4 style cmdR fill:#87CEEB,stroke:#4682B4
flowchart TB
    subgraph "误差计算"
        e_avg["整体误差 e_avg
x_ref - (x1+x2)/2"] e_sync["同步误差 e_sync
(x1-x2) - sync_offset"] end subgraph "控制器" PID["整体 PID
common_trim = PID(e_avg)"] SYNC["同步 P+D
sync_trim = Kps·e_sync + Kds·(v1-v2)"] end subgraph "输出" cmdL["左轴 = x_ref + common - sync"] cmdR["右轴 = x_ref + common + sync"] end e_avg --> PID --> cmdL e_avg --> PID --> cmdR e_sync --> SYNC --> cmdL e_sync -->|"反向"| cmdR style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style PID fill:#90EE90,stroke:#2E8B57 style SYNC fill:#90EE90,stroke:#2E8B57 style cmdL fill:#87CEEB,stroke:#4682B4 style cmdR fill:#87CEEB,stroke:#4682B4
flowchart TB
    subgraph "误差计算"
        e_avg["整体误差 e_avg
x_ref - (x1+x2)/2"] e_sync["同步误差 e_sync
(x1-x2) - sync_offset"] end subgraph "控制器" PID["整体 PID
common_trim = PID(e_avg)"] SYNC["同步 P+D
sync_trim = Kps·e_sync + Kds·(v1-v2)"] end subgraph "输出" cmdL["左轴 = x_ref + common - sync"] cmdR["右轴 = x_ref + common + sync"] end e_avg --> PID --> cmdL e_avg --> PID --> cmdR e_sync --> SYNC --> cmdL e_sync -->|"反向"| cmdR style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style PID fill:#90EE90,stroke:#2E8B57 style SYNC fill:#90EE90,stroke:#2E8B57 style cmdL fill:#87CEEB,stroke:#4682B4 style cmdR fill:#87CEEB,stroke:#4682B4
flowchart TB
    subgraph "误差计算"
        e_avg["整体误差 e_avg<br/>x_ref - (x1+x2)/2"]
        e_sync["同步误差 e_sync<br/>(x1-x2) - sync_offset"]
    end
    
    subgraph "控制器"
        PID["整体 PID<br/>common_trim = PID(e_avg)"]
        SYNC["同步 P+D<br/>sync_trim = Kps·e_sync + Kds·(v1-v2)"]
    end
    
    subgraph "输出"
        cmdL["左轴 = x_ref + common - sync"]
        cmdR["右轴 = x_ref + common + sync"]
    end
    
    e_avg --> PID --> cmdL
    e_avg --> PID --> cmdR
    e_sync --> SYNC --> cmdL
    e_sync -->|"反向"| cmdR
    
    style e_avg fill:#FFDAB9,stroke:#FF8C00
    style e_sync fill:#FFDAB9,stroke:#FF8C00
    style PID fill:#90EE90,stroke:#2E8B57
    style SYNC fill:#90EE90,stroke:#2E8B57
    style cmdL fill:#87CEEB,stroke:#4682B4
    style cmdR fill:#87CEEB,stroke:#4682B4

为什么有效

对比:传统方案 vs 交叉耦合

传统方案交叉耦合方案
左右命令始终相同动态调整
能否感知位置差异❌✅
能否修正负载偏差❌✅
同步误差累积会不会

传统方案给左右轴发相同命令 → 控制器看不到实际差异 → 无法修正 → 误差累积

交叉耦合方案通过反馈计算同步误差 → 动态调整两侧命令 → 实时修正 → 误差收敛


系统架构

图表 代码
flowchart TB
    subgraph "规划层 (50-200Hz)"
        planner[轨迹规划]
    end
    
    subgraph "同步层 (500-2000Hz)"
        sync_ctrl[交叉耦合同步控制器]:::sync
    end
    
    subgraph "伺服层"
        subgraph "左轴"
            L1[位置环] --> L2[速度环] --> L3[电流环]
        end
        subgraph "右轴"
            R1[位置环] --> R2[速度环] --> R3[电流环]
        end
    end
    
    subgraph "执行层"
        motorL[左电机] & motorR[右电机]
    end
    
    subgraph "反馈层"
        encL[左编码器] & encR[右编码器]
    end
    
    planner -->|"x_ref, v_ref"| sync_ctrl
    sync_ctrl -->|pos_cmd_left| L1
    sync_ctrl -->|pos_cmd_right| R1
    
    L3 --> motorL
    R3 --> motorR
    
    motorL -.->|位置反馈| encL
    motorR -.->|位置反馈| encR
    
    encL -->|"x1, v1"| sync_ctrl
    encR -->|"x2, v2"| sync_ctrl
    
    classDef sync fill:#90EE90,stroke:#2E8B57,stroke-width:3px
flowchart TB
    subgraph "规划层 (50-200Hz)"
        planner[轨迹规划]
    end
    
    subgraph "同步层 (500-2000Hz)"
        sync_ctrl[交叉耦合同步控制器]:::sync
    end
    
    subgraph "伺服层"
        subgraph "左轴"
            L1[位置环] --> L2[速度环] --> L3[电流环]
        end
        subgraph "右轴"
            R1[位置环] --> R2[速度环] --> R3[电流环]
        end
    end
    
    subgraph "执行层"
        motorL[左电机] & motorR[右电机]
    end
    
    subgraph "反馈层"
        encL[左编码器] & encR[右编码器]
    end
    
    planner -->|"x_ref, v_ref"| sync_ctrl
    sync_ctrl -->|pos_cmd_left| L1
    sync_ctrl -->|pos_cmd_right| R1
    
    L3 --> motorL
    R3 --> motorR
    
    motorL -.->|位置反馈| encL
    motorR -.->|位置反馈| encR
    
    encL -->|"x1, v1"| sync_ctrl
    encR -->|"x2, v2"| sync_ctrl
    
    classDef sync fill:#90EE90,stroke:#2E8B57,stroke-width:3px
flowchart TB
    subgraph "规划层 (50-200Hz)"
        planner[轨迹规划]
    end
    
    subgraph "同步层 (500-2000Hz)"
        sync_ctrl[交叉耦合同步控制器]:::sync
    end
    
    subgraph "伺服层"
        subgraph "左轴"
            L1[位置环] --> L2[速度环] --> L3[电流环]
        end
        subgraph "右轴"
            R1[位置环] --> R2[速度环] --> R3[电流环]
        end
    end
    
    subgraph "执行层"
        motorL[左电机] & motorR[右电机]
    end
    
    subgraph "反馈层"
        encL[左编码器] & encR[右编码器]
    end
    
    planner -->|"x_ref, v_ref"| sync_ctrl
    sync_ctrl -->|pos_cmd_left| L1
    sync_ctrl -->|pos_cmd_right| R1
    
    L3 --> motorL
    R3 --> motorR
    
    motorL -.->|位置反馈| encL
    motorR -.->|位置反馈| encR
    
    encL -->|"x1, v1"| sync_ctrl
    encR -->|"x2, v2"| sync_ctrl
    
    classDef sync fill:#90EE90,stroke:#2E8B57,stroke-width:3px
flowchart TB
    subgraph "规划层 (50-200Hz)"
        planner[轨迹规划]
    end
    
    subgraph "同步层 (500-2000Hz)"
        sync_ctrl[交叉耦合同步控制器]:::sync
    end
    
    subgraph "伺服层"
        subgraph "左轴"
            L1[位置环] --> L2[速度环] --> L3[电流环]
        end
        subgraph "右轴"
            R1[位置环] --> R2[速度环] --> R3[电流环]
        end
    end
    
    subgraph "执行层"
        motorL[左电机] & motorR[右电机]
    end
    
    subgraph "反馈层"
        encL[左编码器] & encR[右编码器]
    end
    
    planner -->|"x_ref, v_ref"| sync_ctrl
    sync_ctrl -->|pos_cmd_left| L1
    sync_ctrl -->|pos_cmd_right| R1
    
    L3 --> motorL
    R3 --> motorR
    
    motorL -.->|位置反馈| encL
    motorR -.->|位置反馈| encR
    
    encL -->|"x1, v1"| sync_ctrl
    encR -->|"x2, v2"| sync_ctrl
    
    classDef sync fill:#90EE90,stroke:#2E8B57,stroke-width:3px

推荐频率:电流环 10-20kHz | 速度环 2-5kHz | 同步环 500Hz-2kHz


关键实现细节

1. sync_offset 的获取

sync_offset 是回零后测得的固定差值,而非默认值 0。

图表 代码
flowchart TD
    start([开始回零]) --> L[左轴独立回零]
    L --> saveL[记录 left.home_offset]
    saveL --> R[右轴独立回零]
    R --> saveR[记录 right.home_offset]
    saveR --> align[移动到机械对齐位置]
    align --> calc[计算 sync_offset = x1 - x2]
    calc --> clear[统一清零内部坐标]
    clear --> done([完成])
    
    style calc fill:#FFA07A,stroke:#CD5C5C
    style start fill:#90EE90,stroke:#32CD32
    style done fill:#90EE90,stroke:#32CD32
flowchart TD
    start([开始回零]) --> L[左轴独立回零]
    L --> saveL[记录 left.home_offset]
    saveL --> R[右轴独立回零]
    R --> saveR[记录 right.home_offset]
    saveR --> align[移动到机械对齐位置]
    align --> calc[计算 sync_offset = x1 - x2]
    calc --> clear[统一清零内部坐标]
    clear --> done([完成])
    
    style calc fill:#FFA07A,stroke:#CD5C5C
    style start fill:#90EE90,stroke:#32CD32
    style done fill:#90EE90,stroke:#32CD32
flowchart TD
    start([开始回零]) --> L[左轴独立回零]
    L --> saveL[记录 left.home_offset]
    saveL --> R[右轴独立回零]
    R --> saveR[记录 right.home_offset]
    saveR --> align[移动到机械对齐位置]
    align --> calc[计算 sync_offset = x1 - x2]
    calc --> clear[统一清零内部坐标]
    clear --> done([完成])
    
    style calc fill:#FFA07A,stroke:#CD5C5C
    style start fill:#90EE90,stroke:#32CD32
    style done fill:#90EE90,stroke:#32CD32
flowchart TD
    start([开始回零]) --> L[左轴独立回零]
    L --> saveL[记录 left.home_offset]
    saveL --> R[右轴独立回零]
    R --> saveR[记录 right.home_offset]
    saveR --> align[移动到机械对齐位置]
    align --> calc[计算 sync_offset = x1 - x2]
    calc --> clear[统一清零内部坐标]
    clear --> done([完成])
    
    style calc fill:#FFA07A,stroke:#CD5C5C
    style start fill:#90EE90,stroke:#32CD32
    style done fill:#90EE90,stroke:#32CD32

为什么不能设 sync_offset = 0:

  • 机械安装、限位差异、编码器零位差异会造成固定偏差
  • 若设为 0,控制器会把这些固定偏差当成交同步误差持续修正
  • 结果:横梁被持续扭斜、电机相互对抗、发热抖动

工程建议:

  • 使用二次回零(fast + slow)提高测量重复性
  • 回零方向要一致
  • 若 |sync_offset| 超阈值,判定机械异常

2. 编码器数据流

图表 代码
flowchart LR
    subgraph "左轴"
        rawL[原始读数] --> offsetL["- home_offset
* scale"] --> x1[物理位置 x1] x1 --> diffL["差分/dt
+ 滤波器"] --> v1[速度 v1] end subgraph "右轴" rawR[原始读数] --> offsetR["- home_offset
* scale"] --> x2[物理位置 x2] x2 --> diffR["差分/dt
+ 滤波器"] --> v2[速度 v2] end subgraph "误差计算" x_ref[目标位置] --> e_avg["e_avg = x_ref - (x1+x2)/2"] x1 & x2 --> e_sync["e_sync = (x1-x2) - sync_offset"] v1 & v2 --> v_diff["v_diff = v1 - v2"] end style rawL fill:#E6E6FA,stroke:#8A2BE2 style rawR fill:#E6E6FA,stroke:#8A2BE2 style x1 fill:#87CEEB,stroke:#4682B4 style x2 fill:#87CEEB,stroke:#4682B4 style v1 fill:#87CEEB,stroke:#4682B4 style v2 fill:#87CEEB,stroke:#4682B4 style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style v_diff fill:#FFDAB9,stroke:#FF8C00
flowchart LR
    subgraph "左轴"
        rawL[原始读数] --> offsetL["- home_offset
* scale"] --> x1[物理位置 x1] x1 --> diffL["差分/dt
+ 滤波器"] --> v1[速度 v1] end subgraph "右轴" rawR[原始读数] --> offsetR["- home_offset
* scale"] --> x2[物理位置 x2] x2 --> diffR["差分/dt
+ 滤波器"] --> v2[速度 v2] end subgraph "误差计算" x_ref[目标位置] --> e_avg["e_avg = x_ref - (x1+x2)/2"] x1 & x2 --> e_sync["e_sync = (x1-x2) - sync_offset"] v1 & v2 --> v_diff["v_diff = v1 - v2"] end style rawL fill:#E6E6FA,stroke:#8A2BE2 style rawR fill:#E6E6FA,stroke:#8A2BE2 style x1 fill:#87CEEB,stroke:#4682B4 style x2 fill:#87CEEB,stroke:#4682B4 style v1 fill:#87CEEB,stroke:#4682B4 style v2 fill:#87CEEB,stroke:#4682B4 style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style v_diff fill:#FFDAB9,stroke:#FF8C00
flowchart LR
    subgraph "左轴"
        rawL[原始读数] --> offsetL["- home_offset
* scale"] --> x1[物理位置 x1] x1 --> diffL["差分/dt
+ 滤波器"] --> v1[速度 v1] end subgraph "右轴" rawR[原始读数] --> offsetR["- home_offset
* scale"] --> x2[物理位置 x2] x2 --> diffR["差分/dt
+ 滤波器"] --> v2[速度 v2] end subgraph "误差计算" x_ref[目标位置] --> e_avg["e_avg = x_ref - (x1+x2)/2"] x1 & x2 --> e_sync["e_sync = (x1-x2) - sync_offset"] v1 & v2 --> v_diff["v_diff = v1 - v2"] end style rawL fill:#E6E6FA,stroke:#8A2BE2 style rawR fill:#E6E6FA,stroke:#8A2BE2 style x1 fill:#87CEEB,stroke:#4682B4 style x2 fill:#87CEEB,stroke:#4682B4 style v1 fill:#87CEEB,stroke:#4682B4 style v2 fill:#87CEEB,stroke:#4682B4 style e_avg fill:#FFDAB9,stroke:#FF8C00 style e_sync fill:#FFDAB9,stroke:#FF8C00 style v_diff fill:#FFDAB9,stroke:#FF8C00
flowchart LR
    subgraph "左轴"
        rawL[原始读数] --> offsetL["- home_offset<br/>* scale"] --> x1[物理位置 x1]
        x1 --> diffL["差分/dt<br/>+ 滤波器"] --> v1[速度 v1]
    end
    
    subgraph "右轴"
        rawR[原始读数] --> offsetR["- home_offset<br/>* scale"] --> x2[物理位置 x2]
        x2 --> diffR["差分/dt<br/>+ 滤波器"] --> v2[速度 v2]
    end
    
    subgraph "误差计算"
        x_ref[目标位置] --> e_avg["e_avg = x_ref - (x1+x2)/2"]
        x1 & x2 --> e_sync["e_sync = (x1-x2) - sync_offset"]
        v1 & v2 --> v_diff["v_diff = v1 - v2"]
    end
    
    style rawL fill:#E6E6FA,stroke:#8A2BE2
    style rawR fill:#E6E6FA,stroke:#8A2BE2
    style x1 fill:#87CEEB,stroke:#4682B4
    style x2 fill:#87CEEB,stroke:#4682B4
    style v1 fill:#87CEEB,stroke:#4682B4
    style v2 fill:#87CEEB,stroke:#4682B4
    style e_avg fill:#FFDAB9,stroke:#FF8C00
    style e_sync fill:#FFDAB9,stroke:#FF8C00
    style v_diff fill:#FFDAB9,stroke:#FF8C00
步骤公式说明
位置转换x1 = (raw1 - home_offset) * scale绝对编码器直接输出位置
速度估计v1 = (x1 - x1_prev) / dt建议使用滤波器降噪
整体误差e_avg = x_ref - (x1+x2)/2龙门整体偏离目标的程度
同步误差e_sync = (x1 - x2) - sync_offset左右两侧相互偏离程度

3. 安全保护

触发条件动作
|e_sync| > SYNC_LIMIT停机进入 FAULT 状态
未完成回零禁止联动运行
急停按下立即停机并锁存故障

4. 状态机

图表 代码
stateDiagram-v2
    [*] --> IDLE
    IDLE --> HOMING: 使能启动
    HOMING --> READY: 回零完成
    READY --> RUNNING: 收到运动指令
    RUNNING --> READY: 运动完成
    
    HOMING --> FAULT: 回零异常
    READY --> FAULT: 同步超限/急停
    RUNNING --> FAULT: 同步超限/急停
    
    FAULT --> READY: 故障复位
    
    note right of IDLE: 未使能,等待启动
    note right of HOMING: 独立回零→记录偏移→清零
    note right of READY: 等待运动指令
    note right of RUNNING: 同步控制+安全监控
    note right of FAULT: 锁存故障,仅能复位
stateDiagram-v2
    [*] --> IDLE
    IDLE --> HOMING: 使能启动
    HOMING --> READY: 回零完成
    READY --> RUNNING: 收到运动指令
    RUNNING --> READY: 运动完成
    
    HOMING --> FAULT: 回零异常
    READY --> FAULT: 同步超限/急停
    RUNNING --> FAULT: 同步超限/急停
    
    FAULT --> READY: 故障复位
    
    note right of IDLE: 未使能,等待启动
    note right of HOMING: 独立回零→记录偏移→清零
    note right of READY: 等待运动指令
    note right of RUNNING: 同步控制+安全监控
    note right of FAULT: 锁存故障,仅能复位
stateDiagram-v2
    [*] --> IDLE
    IDLE --> HOMING: 使能启动
    HOMING --> READY: 回零完成
    READY --> RUNNING: 收到运动指令
    RUNNING --> READY: 运动完成
    
    HOMING --> FAULT: 回零异常
    READY --> FAULT: 同步超限/急停
    RUNNING --> FAULT: 同步超限/急停
    
    FAULT --> READY: 故障复位
    
    note right of IDLE: 未使能,等待启动
    note right of HOMING: 独立回零→记录偏移→清零
    note right of READY: 等待运动指令
    note right of RUNNING: 同步控制+安全监控
    note right of FAULT: 锁存故障,仅能复位
stateDiagram-v2
    [*] --> IDLE
    IDLE --> HOMING: 使能启动
    HOMING --> READY: 回零完成
    READY --> RUNNING: 收到运动指令
    RUNNING --> READY: 运动完成
    
    HOMING --> FAULT: 回零异常
    READY --> FAULT: 同步超限/急停
    RUNNING --> FAULT: 同步超限/急停
    
    FAULT --> READY: 故障复位
    
    note right of IDLE: 未使能,等待启动
    note right of HOMING: 独立回零→记录偏移→清零
    note right of READY: 等待运动指令
    note right of RUNNING: 同步控制+安全监控
    note right of FAULT: 锁存故障,仅能复位

调参顺序

图表 代码
flowchart LR
    S1[Step 1
单轴调参] --> S2[Step 2
关闭同步环] S2 --> S3[Step 3
开启同步环] S3 --> S4[Step 4
工况测试] S1 --- t1a[位置环PID] S1 --- t1b[速度环PID] S2 --- t2a[设 sync_trim=0] S2 --- t2b[验证双轴独立运行] S3 --- t3a[从小值增加Kps] S3 --- t3b[加入Kds抑制振荡] S4 --- t4a[空载测试] S4 --- t4b[偏载测试] S4 --- t4c[长时间测试] style S1 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S2 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S3 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S4 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
flowchart LR
    S1[Step 1
单轴调参] --> S2[Step 2
关闭同步环] S2 --> S3[Step 3
开启同步环] S3 --> S4[Step 4
工况测试] S1 --- t1a[位置环PID] S1 --- t1b[速度环PID] S2 --- t2a[设 sync_trim=0] S2 --- t2b[验证双轴独立运行] S3 --- t3a[从小值增加Kps] S3 --- t3b[加入Kds抑制振荡] S4 --- t4a[空载测试] S4 --- t4b[偏载测试] S4 --- t4c[长时间测试] style S1 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S2 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S3 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S4 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
flowchart LR
    S1[Step 1
单轴调参] --> S2[Step 2
关闭同步环] S2 --> S3[Step 3
开启同步环] S3 --> S4[Step 4
工况测试] S1 --- t1a[位置环PID] S1 --- t1b[速度环PID] S2 --- t2a[设 sync_trim=0] S2 --- t2b[验证双轴独立运行] S3 --- t3a[从小值增加Kps] S3 --- t3b[加入Kds抑制振荡] S4 --- t4a[空载测试] S4 --- t4b[偏载测试] S4 --- t4c[长时间测试] style S1 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S2 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S3 fill:#87CEEB,stroke:#4682B4,stroke-width:2px style S4 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
flowchart LR
    S1[Step 1<br/>单轴调参] --> S2[Step 2<br/>关闭同步环]
    S2 --> S3[Step 3<br/>开启同步环]
    S3 --> S4[Step 4<br/>工况测试]
    
    S1 --- t1a[位置环PID]
    S1 --- t1b[速度环PID]
    
    S2 --- t2a[设 sync_trim=0]
    S2 --- t2b[验证双轴独立运行]
    
    S3 --- t3a[从小值增加Kps]
    S3 --- t3b[加入Kds抑制振荡]
    
    S4 --- t4a[空载测试]
    S4 --- t4b[偏载测试]
    S4 --- t4c[长时间测试]
    
    style S1 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
    style S2 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
    style S3 fill:#87CEEB,stroke:#4682B4,stroke-width:2px
    style S4 fill:#87CEEB,stroke:#4682B4,stroke-width:2px

常见问题:

  • 高频振荡、互相拉扯 → Kps 过大
  • 长行程慢慢跑偏 → Kps 过小
  • 同步环不生效 → 单轴速度环太慢
  • 持续修正虚假误差 → 编码器标定不一致

适用场景

  • 双电机驱动的龙门模组
  • 双丝杆同步直线平台
  • 左右两侧各有独立编码器反馈的同步伺服系统

本方案不绑定硬件:控制逻辑与平台无关,仅需替换以下硬件接口:

  • read_encoder() — 编码器读取
  • estimate_velocity() — 速度估计
  • foc_current_control() — 电流环
  • emergency_stop() — 急停处理

参考代码

  • pseudocode.md — 伪代码,便于理解逻辑
  • code/ — C 语言参考实现

相关文档

如果这篇文章对你有帮助,欢迎请我喝杯咖啡 ☕
Cygnus Yang 支付宝支付宝
Cygnus Yang 微信微信