双电机龙门同步控制方案
目录
核心问题
双侧驱动龙门在长时间运行后,横梁会发生扭斜甚至卡死。
根本原因:左右两侧负载不均、机械阻力差异、编码器标定差异 → 两侧速度不一致 → 同步误差累积 → 横梁变形
图表
代码
flowchart TD
A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
B --> C[同步误差累积]
C --> D[横梁扭斜或卡死]
style A fill:#FFE4E1,stroke:#DC143C
style D fill:#FFE4E1,stroke:#DC143Cflowchart TD
A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
B --> C[同步误差累积]
C --> D[横梁扭斜或卡死]
style A fill:#FFE4E1,stroke:#DC143C
style D fill:#FFE4E1,stroke:#DC143Cflowchart TD
A[负载不均/阻力差异/标定差异] --> B[左右速度不一致]
B --> C[同步误差累积]
C --> D[横梁扭斜或卡死]
style A fill:#FFE4E1,stroke:#DC143C
style D fill:#FFE4E1,stroke:#DC143Cflowchart 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:#4682B4flowchart 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:#4682B4flowchart 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:#4682B4flowchart 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:3pxflowchart 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:3pxflowchart 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:3pxflowchart 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:#32CD32flowchart 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:#32CD32flowchart 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:#32CD32flowchart 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:#FF8C00flowchart 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:#FF8C00flowchart 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:#FF8C00flowchart 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:2pxflowchart 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:2pxflowchart 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:2pxflowchart 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 语言参考实现
相关文档
- 无刷电机与有刷电机对比分析.md — 针对龙门同步控制场景的电机选型分析
如果这篇文章对你有帮助,欢迎请我喝杯咖啡 ☕
支付宝
微信