252 lines
8.3 KiB
Markdown
252 lines
8.3 KiB
Markdown
# 板卡运动控制适配说明
|
||
|
||
本文档说明如何把当前 C++ Modbus RTU 协议库迁移到真实驱动板,并对接板卡已有的运动控制模块。
|
||
|
||
## 迁移结论
|
||
|
||
你不需要重写 Modbus 协议层。板卡侧主要实现 `MotionControlAdapter` 接口,然后在串口收齐一帧 RTU 数据后调用 `MotionControlBridge::process_request()`。
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
UartRx["UART/RS-485 收完整 RTU 帧"] --> Bridge["MotionControlBridge"]
|
||
Bridge --> Server["DeviceServer"]
|
||
Server --> Bank["RegisterBank"]
|
||
Bridge --> Adapter["MotionControlAdapter 接口"]
|
||
Adapter --> Motion["板卡已有运动控制模块"]
|
||
Motion --> Adapter
|
||
Bridge --> UartTx["UART/RS-485 发响应帧"]
|
||
```
|
||
|
||
## 需要实现的接口
|
||
|
||
接口定义在 `include/line_laser_modbus/motion_adapter.hpp`:
|
||
|
||
```cpp
|
||
class MotionControlAdapter {
|
||
public:
|
||
virtual ~MotionControlAdapter() = default;
|
||
|
||
virtual void on_mode_changed(WorkMode mode) = 0;
|
||
virtual void on_target_pose(const Pose6D& pose) = 0;
|
||
virtual void on_correction(const Pose6D& correction) = 0;
|
||
|
||
[[nodiscard]] virtual Pose6D current_pose() const = 0;
|
||
[[nodiscard]] virtual DeviceState current_state() const = 0;
|
||
};
|
||
```
|
||
|
||
每个函数的含义:
|
||
|
||
- `on_mode_changed()`:上位机写入 `0xD000` 模式命令字,且模式值和切换规则合法后调用。
|
||
- `on_target_pose()`:上位机写入 `0xD020 ~ 0xD02D` 目标示教位姿后调用。
|
||
- `on_correction()`:上位机写入 `0xD036 ~ 0xD043` 六轴纠偏量后调用。
|
||
- `current_pose()`:返回板卡当前实际位姿,桥接层会写入 `0xD00A ~ 0xD017`。
|
||
- `current_state()`:返回板卡当前状态字,桥接层会写入 `0xD001`。
|
||
|
||
## 推荐固件主循环
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant UART as UART_RS485
|
||
participant Main as 固件主循环
|
||
participant Bridge as MotionControlBridge
|
||
participant Adapter as 你的板卡适配器
|
||
participant Motion as 运动控制模块
|
||
|
||
Motion-->>Adapter: 更新当前位姿和状态
|
||
UART-->>Main: 收到完整 RTU 请求帧
|
||
Main->>Bridge: process_request(request)
|
||
Bridge->>Adapter: current_pose()
|
||
Bridge->>Adapter: current_state()
|
||
Bridge->>Bridge: 写入当前位姿和状态寄存器
|
||
Bridge->>Bridge: DeviceServer 处理读写请求
|
||
Bridge->>Adapter: 写请求成功后回调模式/目标/纠偏
|
||
Adapter->>Motion: 转发给板卡运动控制逻辑
|
||
Bridge-->>Main: response
|
||
Main-->>UART: response 非空则发送
|
||
```
|
||
|
||
伪代码:
|
||
|
||
```cpp
|
||
#include "line_laser_modbus/motion_adapter.hpp"
|
||
|
||
class BoardMotionAdapter final : public line_laser_modbus::MotionControlAdapter {
|
||
public:
|
||
void on_mode_changed(line_laser_modbus::WorkMode mode) override {
|
||
// 调用你的板卡模式切换函数,例如 motion_set_mode(...)
|
||
}
|
||
|
||
void on_target_pose(const line_laser_modbus::Pose6D& pose) override {
|
||
// 把目标示教位姿送入轨迹/插补/记录模块
|
||
}
|
||
|
||
void on_correction(const line_laser_modbus::Pose6D& correction) override {
|
||
// 把实时纠偏量送入跟踪控制模块
|
||
}
|
||
|
||
line_laser_modbus::Pose6D current_pose() const override {
|
||
// 从板卡轴控反馈读取当前实际 XYZABC
|
||
return {};
|
||
}
|
||
|
||
line_laser_modbus::DeviceState current_state() const override {
|
||
// 从板卡报警、运行、急停、标定等状态生成协议状态字
|
||
return line_laser_modbus::DeviceState::StandbyReady;
|
||
}
|
||
};
|
||
|
||
line_laser_modbus::DeviceServer server;
|
||
BoardMotionAdapter adapter;
|
||
line_laser_modbus::MotionControlBridge bridge(server, adapter);
|
||
|
||
void on_complete_rtu_frame(const line_laser_modbus::ByteVector& request) {
|
||
const auto response = bridge.process_request(request);
|
||
if (!response.empty()) {
|
||
uart_send(response.data(), response.size());
|
||
}
|
||
}
|
||
```
|
||
|
||
## 数据流
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
HostWriteMode["上位机写模式 0xD000"] --> ServerCheck["DeviceServer 校验模式合法性"]
|
||
ServerCheck --> AdapterMode["on_mode_changed(mode)"]
|
||
AdapterMode --> BoardMode["板卡切换运动模式"]
|
||
|
||
HostWriteTarget["上位机写目标位姿 0xD020"] --> BankTarget["RegisterBank 保存目标位姿"]
|
||
BankTarget --> AdapterTarget["on_target_pose(pose)"]
|
||
AdapterTarget --> BoardTarget["板卡轨迹/示教模块"]
|
||
|
||
HostWriteCorrection["上位机写纠偏量 0xD036"] --> BankCorrection["RegisterBank 保存纠偏量"]
|
||
BankCorrection --> AdapterCorrection["on_correction(correction)"]
|
||
AdapterCorrection --> BoardCorrection["板卡实时跟踪模块"]
|
||
|
||
BoardFeedback["板卡轴控反馈"] --> AdapterFeedback["current_pose/current_state"]
|
||
AdapterFeedback --> BridgePublish["MotionControlBridge 发布到寄存器"]
|
||
BridgePublish --> HostRead["上位机读取当前位姿/状态"]
|
||
```
|
||
|
||
## 模式和状态映射建议
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph WorkMode["WorkMode 模式命令"]
|
||
M0["0 待机复位"]
|
||
M1["1 系统标定"]
|
||
M2["2 焊前扫描示教"]
|
||
M3["3 在线全轴跟踪"]
|
||
M4["4 轨迹批量复现"]
|
||
M5["5 紧急停止"]
|
||
end
|
||
|
||
subgraph BoardAction["板卡动作"]
|
||
A0["清空轨迹/纠偏/停机待命"]
|
||
A1["执行标定流程"]
|
||
A2["接收并记录目标轨迹点"]
|
||
A3["启用实时纠偏叠加"]
|
||
A4["调用已保存标准轨迹"]
|
||
A5["断使能/急停制动"]
|
||
end
|
||
|
||
M0 --> A0
|
||
M1 --> A1
|
||
M2 --> A2
|
||
M3 --> A3
|
||
M4 --> A4
|
||
M5 --> A5
|
||
```
|
||
|
||
`current_state()` 建议从板卡真实状态生成:
|
||
|
||
- 无故障待命:`DeviceState::StandbyReady`
|
||
- 运动中:`DeviceState::MotionRunning`
|
||
- 示教完成:`DeviceState::TeachingComplete`
|
||
- 在线跟踪正常:`DeviceState::OnlineTrackingNormal`
|
||
- 任意报警:`DeviceState::Alarm`
|
||
- 标定完成:`DeviceState::CalibrationComplete`
|
||
- 急停触发:`DeviceState::EmergencyTriggered`
|
||
|
||
## 线程和中断注意事项
|
||
|
||
如果板卡上通信任务和运动控制任务不是同一个线程,需要处理共享数据同步。
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
UartTask["通信任务<br/>调用 MotionControlBridge"] --> Shared["共享运动数据快照"]
|
||
MotionTask["运动控制任务<br/>更新轴位姿和状态"] --> Shared
|
||
Shared --> Lock["互斥锁 / 临界区 / 双缓冲"]
|
||
```
|
||
|
||
建议:
|
||
|
||
- 不要在串口中断里直接执行复杂运动控制逻辑;中断只收字节,完整帧交给通信任务处理。
|
||
- `current_pose()` 和 `current_state()` 应尽量只读取快照,避免阻塞。
|
||
- `on_correction()` 如果运行在通信任务中,应把纠偏量写入线程安全队列或双缓冲,再由运动控制周期消费。
|
||
- 如果驱动板不支持 C++ 异常,当前实现仍可使用,因为协议和适配层没有依赖异常流程。
|
||
|
||
## 与现有代码的关系
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class MotionControlAdapter {
|
||
<<interface>>
|
||
+on_mode_changed(mode)
|
||
+on_target_pose(pose)
|
||
+on_correction(correction)
|
||
+current_pose() Pose6D
|
||
+current_state() DeviceState
|
||
}
|
||
|
||
class MotionControlBridge {
|
||
-DeviceServer server_
|
||
-MotionControlAdapter adapter_
|
||
+process_request(request)
|
||
+publish_feedback()
|
||
}
|
||
|
||
class DeviceServer {
|
||
+process_request(request)
|
||
+bank()
|
||
}
|
||
|
||
class RegisterBank {
|
||
+read(address, quantity)
|
||
+write(address, registers)
|
||
+set_current_pose(pose)
|
||
+set_state(state)
|
||
}
|
||
|
||
MotionControlBridge --> MotionControlAdapter
|
||
MotionControlBridge --> DeviceServer
|
||
DeviceServer --> RegisterBank
|
||
```
|
||
|
||
## Demo
|
||
|
||
`apps/adapter_demo.cpp` 提供了一个模拟板卡适配器:
|
||
|
||
- `DemoBoardMotion` 实现 `MotionControlAdapter`。
|
||
- 上位机 demo 写入在线跟踪模式、目标位姿和纠偏量。
|
||
- 桥接层把这些写请求回调给 `DemoBoardMotion`。
|
||
- 读取当前位姿时,桥接层先从 `DemoBoardMotion::current_pose()` 发布反馈,再返回 Modbus 响应。
|
||
|
||
构建后可运行:
|
||
|
||
```bash
|
||
cmake --build cpp/build --config Release
|
||
cpp/build/Release/adapter_demo.exe
|
||
```
|
||
|
||
## 最小迁移清单
|
||
|
||
- 实现一个继承 `MotionControlAdapter` 的板卡适配类。
|
||
- 在 UART/RS-485 层实现完整 RTU 帧接收和响应发送。
|
||
- 用 `MotionControlBridge::process_request()` 连接协议处理。
|
||
- 在 `on_mode_changed()` 中接入板卡模式切换。
|
||
- 在 `on_target_pose()` 中接入轨迹示教/记录模块。
|
||
- 在 `on_correction()` 中接入实时纠偏模块。
|
||
- 在 `current_pose()` 和 `current_state()` 中返回板卡真实反馈。
|