项目概述
国家重点研发项目子课题:冷阴极 X 射线源球壳钛合金焊缝检测装置的运动控制系统(下位机部分)。
装置通过 X 射线源与平板探测器沿弧形轨道协同扫查,对球壳钛合金焊缝成像检测。下位机负责对 X 射线源、探测器的协同控制与远程控制:配合 QT 上位机调整射线源与探测器的速度与动作,完成单段焊缝、截面焊缝两种检测模式,并实时上报系统状态。
系统组成

整机实拍:左侧为提升立柱与弧形齿轮轨道,右侧为探测器载滑车,地面为多路禾川伺服驱动器与供电线缆。
软件分层与模块
软件分为两层:APP 应用层承载对外协议、运动语义与任务调度;驱动层提供寄存器级能力(Modbus 协议模板与 CRC、电机寄存器配置序列、OLED/菜单/按键、FLASH 参数存储)。
| 模块 | 职责 |
|---|---|
app_conf.h | 全部可调参数集中管理:协议常量、超时、限幅、软限位、任务栈与优先级、RS485 开关 |
host_proto.h/.c | RS485/USART6 二进制协议栈:TX DMA + 环形缓冲 + 组包 CRC;RX IDLE+DMA + 环形缓冲 + 字节状态机解析;命令分发回调;心跳时间戳;收发统计 |
motion_api.h/.c | 统一运动控制接口:轴状态机(IDLE/READY/MOVING/FAULT)、方向(速度符号)/速度(±3000 限幅)/动作时序(松闸稳定→运动→清速减速→抱闸)、编码器事务三重校验、软限位、安全关断收敛入口、故障复位 |
dev_task.h/.c | 四大逻辑任务:motionTask(动作执行+ACK)、statusTask(50ms 状态轮询+100ms 状态帧)、monitorTask(心跳超时/故障沿上报/IWDG 喂狗点)、装配函数 APP_TasksInit() |
软件架构
数据流(下行指令 → 上行状态)
任务划分
| 任务 | CMSIS 优先级 | 栈 | 阻塞点 | 职责 |
|---|---|---|---|---|
| parseTask | AboveNormal | 2KB | 线程标志(50ms 超时) | 协议解析(状态机+CRC) |
| motionTask | High | 2KB | 消息队列(100ms 超时) | 动作执行 + 定时动作截止判定 |
| statusTask | Normal | 2KB | osDelay(50) | 编码器轮询 + 状态反馈 |
| monitorTask | Normal | 1.5KB | osDelay(100) | 心跳超时/故障沿/安全关断触发 |
| oledShowMenuTask | Low | 512B | 事件标志 | 菜单显示(按键扫描定时器 10ms) |
| defaultTask | Normal | 512B | osDelay(1000) | LED 心跳 |
两个关键的执行体设计:
- 协议解析与动作执行分离:解析出指令后经 8 深消息队列投递给 motionTask 按序执行,队列削峰保证解析节奏不受动作耗时拖累;
- 编码器轮询放在任务而非软件定时器中:定时器回调与任务可能并发访问同一 Modbus 通道,统一到任务上下文并配合轴级互斥锁后,「请求-应答」事务被彻底串行化。
任务优先级设计依据
优先级不是按“功能重要性”排的,而是按三条工程准则:
- 截止期与后果:动作执行(清速/抱闸/安全关断)有硬时序且后果不可逆 → 最高应用优先级;
- 阻塞特性:事件驱动型任务平时全部阻塞在同步原语上,高优先级只影响唤醒延迟,不产生 CPU 争抢;
- 依赖方向:数据流上游不能被下游长时间阻塞(parse 喂 motion 的队列;status 的编码器数据被 motion 的轴锁依赖)。
完整优先级谱系(NVIC 中断优先级与任务优先级是两套量纲,勿混):
| |
几个关键取舍:
- 为什么 motion(38) > parse(31):命令队列已削峰(8 深),解析晚 1–2ms 不影响正确性;反之若 parse 更高,解析长帧期间到达的停止/安全命令会被推迟执行——安全动作的执行权高于协议的解析权。
- status/monitor 同级 Normal:两者都是周期型软实时(50ms/100ms),判定窗口(软限位/心跳 500ms)远大于调度延迟;它们持锁时间短(单次 Modbus 事务约 6ms),motion 高优先级被轴锁阻塞时由互斥量优先级继承临时提升持锁任务,消除优先级反转。
- 关中断快照 vs 互斥量:跨任务读轴状态用
__disable_irq()短临界区(µs 级、无反转风险);锁只用于必须串行化的 Modbus 事务(同一 UART 的请求-应答配对)。 - 一个已知限制:定时器守护任务优先级为 CubeMX 默认值 2,低于所有应用任务——当前成立的条件是所有任务都有周期阻塞点,定时器总能在间隙运行;若未来把安全逻辑放进软件定时器(如限位去抖),必须提高该优先级。
- Realtime(47) 刻意留空:为硬实时扩展预留(5–20ms 节拍的位置环、限位 EXTI 的紧急处理链)。
轴状态机
任何异常都收敛到单一安全关断入口 MOTION_SafeShutdown():清速 → 等 150ms(驱动器 100ms 减速斜坡完成,避免高速抱闸冲击)→ 断抱闸继电器 → 锁定 FAULT + 故障码 → monitorTask 沿检测上报。恢复必须显式故障复位指令。

上位机帧协议
帧格式
| |
- 双帧头 A5 5A:误同步率 1/65536;A5/5A 位翻转丰富,示波器上可辨;
- SEQ 序号:下行主机自增(ACK 回显 → 去重/丢帧检测),上行设备独立自增;
- LEN 显式变长:状态机据此收数,天然支持命令扩展;
- CRC16 覆盖 SEQ..DATA(不含帧头与自身),算法与 Modbus 完全一致(同一张查表),全协议栈一套 CRC;
- 帧开销 7 字节;载荷中可能出现假帧头,由 CRC 兜底丢弃(更高可靠场景可选 COBS 变长编码)。
命令集
| CMD | 方向 | 载荷 | 语义 |
|---|---|---|---|
| 0x01 | 下行 | 轴号 + 开关 | 抱闸控制 |
| 0x02 | 下行 | 轴号 + rpm(int16) | 速度指令(符号=方向,±3000 限幅) |
| 0x03 | 下行 | 轴号 + rpm + 时长(100ms 单位) | 定时动作(动作时序演示) |
| 0x04 | 下行 | 轴号(0xFF=全轴) | 停止 |
| 0x05 | 下行 | seq 回显 | 心跳(200ms 周期,不回 ACK) |
| 0x06 | 下行 | 轴号(0xFF=全部) | 故障复位 |
| 0x07 | 下行 | 无 | 立即请求状态帧(回 0x82) |
| 0x81 | 上行 | 回显命令/序号 + status | 命令应答(OK/BUSY/REJECT/BAD_ARG) |
| 0x82 | 上行 | 状态字×2 + 双轴速度 + 故障码 | 周期状态帧(100ms) |
| 0x8F | 上行 | 故障码 + 轴号 + 保留 | 故障上报(故障沿触发一次) |
实例帧(CRC 实算,可直接用于联调)
| 帧 | 字节 |
|---|---|
| 轴1 速度 -500rpm | A5 5A 01 03 02 01 FE 0C 55 D7 |
| 轴2 800rpm 转 3.0s | A5 5A 02 04 03 02 20 03 1E FD CE |
| 心跳 | A5 5A 03 01 05 5A D2 CB |
| 状态帧(READY/MOVING,sp1=1500,fault=0x0001) | A5 5A 10 08 82 01 02 DC 05 00 00 01 00 97 A8 |
| 故障帧(驱动器无应答,轴1) | A5 5A 11 05 8F 02 00 01 00 00 E2 F4 |
解析鲁棒性(7 态状态机 + 三重防护)
- LEN > 64 → 丢弃回找头(防超长帧打爆缓冲);
- 帧内字节间隔 > 50ms → 强制回找头(防半帧卡死);
- CRC 失败 → 丢当前帧回找头(假同步自愈);RX 环满 → 丢最新并计数上报统计。
接收链路硬件侧使用 UART 中断 + DMA 不定长接收,以 IDLE 总线空闲事件判帧:
| |
ISR 只做批量搬移入环形缓冲(SPSC 免锁)+ 置线程标志,解析全部在 parseTask 完成——ISR 保持极短是实时性的关键:若在中断里做逐字节处理甚至打印,9600 波特率下一帧即可阻塞几十 ms。
统一运动控制 API
对上层屏蔽多电机与 Modbus 细节,全部运动能力收敛为一组 MOTION_* 接口:
| API | 语义 |
|---|---|
MOTION_InitAll() | 伺服参数写入 + 原点捕获 + 松闸(任务上下文) |
MOTION_SetSpeed(ax, rpm) | 方向=符号;自动先松闸再设速 |
MOTION_MoveTimed(ax, rpm, dur) | 速度 + 截止期(定时动作) |
MOTION_Stop(ax) | 清速 → 150ms 减速等待 → 抱闸 |
MOTION_Brake(ax, open) | 手动抱闸(运动中拒绝) |
MOTION_Tick() | 定时动作到期判定(100ms 粒度) |
MOTION_PollFeedback() | 双轴编码器轮询 + 无应答计数 + 软限位 |
MOTION_GetSnapshot(ax, &s) | 状态/速度/位置/故障原子快照 |
MOTION_SafeShutdown(ax, fault) | 安全关断唯一入口 |
MOTION_FaultReset(ax) | FAULT → READY:重初始化 + 重捕获原点 + 松闸 |
底层由一组寄存器级驱动接口支撑:
| |
Modbus RTU 驱动器接口
| 功能码 | 用途 |
|---|---|
| 0x03 | 读保持寄存器(状态/速度回收) |
| 0x06 | 写单寄存器(速度设定等) |
| 0x10 | 写多寄存器(批量参数下发) |
- 帧结构:
地址(1B) + 功能码(1B) + 数据(NB) + CRC16(2B,低字节在前) - CRC16 参数:多项式 0x8005(反射 0xA001)、初值 0xFFFF,实现查表法与逐位法两种等价算法——上位机协议栈的 CRC 直接复用同一张表
- 实例帧:
01 06 03 03 05 DC→ CRC =7B 47 - 错误应答识别:功能码 + 0x80
- 编码器读事务做功能码 + 长度 + CRC 三重校验,连续无应答即触发单轴安全关断
异常保护体系
| 保护 | 触发条件 | 动作 |
|---|---|---|
| 心跳丢失 | 500ms 无任何合法帧(首帧后生效) | 全轴 SafeShutdown + 0x8F 上报 |
| 驱动器失联 | 连续 3 次编码器读无应答/校验失败 | 单轴 SafeShutdown |
| 软限位 | 位置超出 ±170°(可配)且运动中 | 单轴 SafeShutdown |
| 硬限位 | EXTI 接口预留(硬件常闭开关已具备) | 后续接入 SafeShutdown 链 |
| CRC 错帧 | 校验失败 | 丢帧 + 收发统计 |
| IWDG | 喂狗点已置于 monitorTask(启用为编译开关) | 复位兜底 |
| 抱闸 failsafe | 硬件特性:断电即抱死 | 物理兜底 |
关于伺服三环控制的工程结论
电流环/速度环/位置环要不要在 MCU 里做?——不需要,这是伺服系统的标准级联结构:
- 位置环(软件,按曲线节拍)→ 速度环(驱动器硬件闭环)→ 电流环(驱动器硬件闭环,µs 级带宽);
- 若强行在 MCU 做三环:RS485 半双工往返 + RTOS 任务抖动,内环带宽根本撑不起来,反而劣化性能;
- 本项目 MCU 只做位置外环:协议与运动接口预留位置指令扩展,速度/电流环交给禾川驱动器。
轨迹跟踪业务层的实现方案:双电机按 FLASH 中的 1000 点位置/速度曲线做 S 曲线协同跟踪——位置 PID + 速度前馈 + 双电机交叉耦合补偿,位置环输出直接写驱动器速度寄存器;上位机经 LoRa 链路完成曲线分块下传(块号 ACK 隐含重传)、误差曲线回读、前馈/耦合系数在线整定与点动控制;限位开关走 EXTI 硬件链路。
已知限制与后续项
- 驱动器应答等待仍为固定 5ms 盲等,改进方向是信号量 + 超时;
- IWDG 未启用(喂狗点与启用步骤已就位,编译开关打开即用);
- 硬限位 EXTI 尚未接入软件保护链(常闭开关硬件与 EXTI 驱动已具备,接入方案:ISR 直接抱闸 + 通知任务走 SafeShutdown);
- USART6 波特率 9600,协议帧建议提升到 115200;
- printf 复用上位机串口,正式版应以编译开关关闭或改用独立调试口;
- 位置闭环当前为速度模式的接口预留,轨迹跟踪按上文 S 曲线方案扩展。
项目状态
下位机软件已完成「RS485 协议栈 + 统一运动接口 + 四大逻辑任务 + 多级异常保护」的完整架构实现;驱动层经整机联调实测,Modbus 通信、双轴运动控制、上位机链路稳定工作,支撑完成单段焊缝与截面焊缝检测流程。