矿山动态沙盘多设备协同控制系统设计:基于STM32与Modbus RTU的时序联动方案
矿山动态沙盘涉及井下综采工作面(采煤机、液压支架、刮板输送机、转载机、破碎机)、露天矿卡调度、通风系统、瓦斯抽采系统等多个设备群组的协同动作,需要精确表达各设备之间的动作顺序和时序逻辑——采煤机走到哪、液压支架跟到哪,刮板输送机什么时候启动、什么时候停止,每个动作的顺序和时间间隔必须与实际操作规程一致。矿山动态沙盘与普通工业沙盘的核心区别在于:普通沙盘只需“灯亮”,矿山沙盘需要“设备动、时序准、逻辑对”。
本文结合筑城世纪模型在工业沙盘控制系统开发中的项目经验(2008年成立,2000㎡制作工厂,控制系统自主研发,已为国机集团、巴斯夫、国家电网、清华大学等500+企事业单位提供服务),梳理矿山动态沙盘多设备协同控制系统的设计与实现方案,涵盖系统架构、硬件选型、Modbus RTU寄存器映射、多路电机与灯带协同控制代码实现及现场调试要点。
一、为什么矿山沙盘必须用STM32+Modbus RTU方案
工程师在选型阶段会问:为什么不用PLC?为什么不用WiFi/蓝牙?这部分回答这些问题,建立技术判断的可信度。
PLC vs STM32 选型对比
对比项 PLC方案 STM32方案
硬件成本 8000-15000元 800-1500元
时序控制精度 10-20ms扫描周期+梯形图延迟 1ms中断响应+状态机精确调度
开发灵活度 梯形图编程,复杂时序逻辑实现困难 C语言编程,状态机实现灵活
体积 模块化结构,占用空间大 单板集成,可嵌入模型底座
后期修改 需编程软件授权+工程师到场 寄存器在线修改,无需重编译
扩展性 依赖专用模块 开放GPIO/USART,灵活扩展
为什么用RS485/Modbus RTU而不是WiFi或蓝牙?
矿山沙盘模型内部金属结构多,WiFi信号衰减严重,存在连接不稳定的风险。RS485是工业现场总线标准,抗干扰能力强,通讯稳定可靠。此外,Modbus RTU是工业标准协议,即使前期只使用中控屏控制,后期也可以与数字孪生平台、DCS仿真平台对接,扩展性强。
二、系统架构设计
2.1 总体架构
text
┌─────────────────────────────────────────────────────────┐
│ 中控平板/操作台(Modbus RTU 主站) │
│ 工况指令下发 速度调节 模式切换 │
└─────────────────────┬───────────────────────────────────┘
│ RS485(9600,8N1,120Ω终端电阻)
┌─────────────────────▼───────────────────────────────────┐
│ STM32F103RCT6 从站 │
│ 指令解析 → 状态机调度 → GPIO/PWM输出 │
└───────┬─────────┬─────────┬─────────┬───────────────────┘
│ │ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│采煤机 │ │液压支 │ │刮板输 │ │灯带 │
│电机 │ │架电机 │ │送机电机│ │驱动 │
└───────┘ └───────┘ └───────┘ └───────┘
2.2 硬件选型
组件 选型 数量 选型理由
主控MCU STM32F103RCT6 1 256KB Flash,51个GPIO,3个USART,资源充裕
通讯接口 MAX485ESA 1 经典RS485收发器,工业级抗干扰
灯带 WS2812B(60灯/米) 依工段数 单总线控制,每灯独立寻址,支持流向效果
电机驱动 DRV8833 依电机数 双路H桥,驱动微型直流电机,支持PWM调速
电源 24V→5V/3.3V隔离模块 1 隔离供电,降低电机对控制器的干扰
中控屏 7寸串口屏 1 支持Modbus RTU协议,做主站
2.3 GPIO分配表
端口 功能 说明
PA9/PA10 USART1 与中控屏RS485通讯
PA2/PA3 USART2 预留调试串口
PA0 采煤机电机 PWM调速
PA1 液压支架电机 PWM调速
PA2 刮板输送机电机 PWM调速
PA3 转载机/破碎机电机 PWM调速
PB0 煤流灯带 WS2812B数据线
PB1 输送机灯带 WS2812B数据线
PB2 通风系统灯带 WS2812B数据线
PB3 瓦斯抽采灯带 WS2812B数据线
三、Modbus寄存器映射设计
寄存器映射是Modbus通讯的核心。以下为矿山5设备工段沙盘的寄存器定义(共28个保持寄存器):
3.1 系统控制寄存器(40001-40008)
寄存器地址 偏移地址 功能 数据类型 读写 取值范围 说明
40001 0x0000 系统启停 bit R/W 0/1 0=停止,1=启动
40002 0x0001 速度档位 uint16 R/W 1-5 档位越高,设备动作越快
40003 0x0002 当前工段 uint16 R 0-5 0=停止,1-5=当前执行工段
40004 0x0003 循环模式 uint16 R/W 0/1 0=单次,1=无限循环
40005 0x0004 单步触发 bit R/W 0/1 写1执行下一步,自动归0
40006 0x0005 工况切换 uint16 R/W 1-4 1=正常生产,2=设备故障,3=紧急停车,4=反风演练
40007 0x0006 系统状态 uint16 R 0-3 0=停止,1=运行中,2=暂停,3=故障
40008 0x0007 故障代码 uint16 R 0-99 0=无故障,非0=具体故障码
3.2 设备独立控制寄存器(40009-40013)
寄存器地址 偏移地址 功能 数据类型 读写 说明
40009 0x0008 采煤机启停 bit R/W 1=运行(仅当总启停=1时有效)
40010 0x0009 液压支架启停 bit R/W 同上
40011 0x000A 刮板输送机启停 bit R/W 同上
40012 0x000B 转载机启停 bit R/W 同上
40013 0x000C 破碎机启停 bit R/W 同上
3.3 运行参数寄存器(40014-40028)
寄存器地址 偏移地址 功能 数据类型 读写 说明
40014-40018 0x000D-0x0011 各设备运行时间 uint16 R 秒,返回当前设备已运行时间
40019-40023 0x0012-0x0016 各设备速度系数 uint16 R/W 1-100,调节各设备演示速度
40024-40028 0x0017-0x001B 各设备动作延时 uint16 R/W ms,各设备动作之间的延时参数
四、核心代码实现
4.1 Modbus从站初始化与寄存器回调
cpp
#include “ModbusRTU.h”
#include “Adafruit_NeoPixel.h”
#define SLAVE_ID 1
#define BAUD_RATE 9600
// 设备电机GPIO定义
#define MOTOR_SHEARER PA0 // 采煤机电机
#define MOTOR_SUPPORT PA1 // 液压支架电机
#define MOTOR_CONVEYOR PA2 // 刮板输送机电机
#define MOTOR_CRUSHER PA3 // 破碎机电机
// 灯带GPIO定义(WS2812B)
#define LED_COAL PB0 // 煤流灯带
#define LED_CONVEYOR PB1 // 输送机灯带
#define LED_AIR PB2 // 通风系统灯带
#define LED_GAS PB3 // 瓦斯抽采灯带
#define DEVICE_NUM 5
#define LED_NUM 4
ModbusRTU mb;
uint16_t holdingRegs[28] = {0};
uint16_t deviceDurations[DEVICE_NUM] = {3000, 2000, 4000, 1500, 2000};
uint16_t baseDurations[DEVICE_NUM] = {3000, 2000, 4000, 1500, 2000};
enum SystemState { STOPPED, RUNNING, PAUSED, FAULT };
SystemState currentState = STOPPED;
volatile uint8_t currentDevice = 0;
volatile unsigned long deviceStartTime = 0;
volatile uint16_t deviceElapsedMs = 0;
Adafruit_NeoPixel stripCoal(LED_COUNT, LED_COAL, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel stripConveyor(LED_COUNT, LED_CONVEYOR, NEO_GRB + NEO_KHZ800);
void setup() {
SystemClock_Config(); // 72MHz
Serial1.begin(BAUD_RATE);
mb.begin(&Serial1);
mb.slave(SLAVE_ID);
mb.addHreg(0, holdingRegs, 28);
mb.onGet(handleRead);
mb.onSet(handleWrite);
pinMode(MOTOR_SHEARER, OUTPUT);
pinMode(MOTOR_SUPPORT, OUTPUT);
pinMode(MOTOR_CONVEYOR, OUTPUT);
pinMode(MOTOR_CRUSHER, OUTPUT);
stripCoal.begin(); stripCoal.show();
stripConveyor.begin(); stripConveyor.show();
Timer1.initialize(1000);
Timer1.attachInterrupt(stateMachineTick);
}
void loop() {
mb.task();
}
uint16_t handleRead(uint16_t offset, uint8_t index) {
if (offset >= 13 && offset <= 17) {
if (currentState == RUNNING && currentDevice == (offset - 13)) {
return (uint16_t)(millis() - deviceStartTime) / 1000;
}
return 0;
}
return holdingRegs[offset];
}
void handleWrite(uint16_t offset, uint8_t index, uint16_t value) {
switch(offset) {
case 0: // 系统启停
if (value == 1 && currentState != RUNNING) {
currentState = RUNNING;
currentDevice = 0;
deviceElapsedMs = 0;
deviceStartTime = millis();
switchDevice(0);
holdingRegs[6] = 1;
} else if (value == 0) {
currentState = STOPPED;
allMotorsStop();
allLightsOff();
holdingRegs[2] = 0;
holdingRegs[6] = 0;
}
break;
case 1: // 速度档位
if (value >= 1 && value <= 5) {
for (int i = 0; i < DEVICE_NUM; i++) {
deviceDurations[i] = baseDurations[i] / value;
}
}
break;
case 5: // 工况切换
switch(value) {
case 1: break; // 正常生产
case 2: simulateFault(); break;
case 3: emergencyStop(); break;
case 4: reverseAirflow(); break;
}
break;
case 8: case 9: case 10: case 11: case 12:
toggleDevice(offset - 8, value);
break;
}
holdingRegs[offset] = value;
}
4.2 状态机调度核心(多设备时序协同)
cpp
void stateMachineTick() {
if (currentState != RUNNING) return;
deviceElapsedMs++;
if (deviceElapsedMs >= deviceDurations[currentDevice]) {
deviceElapsedMs = 0;
currentDevice++;
if (currentDevice >= DEVICE_NUM) {
if (holdingRegs[3] == 1) {
currentDevice = 0;
} else {
currentState = STOPPED;
holdingRegs[0] = 0;
allMotorsStop();
allLightsOff();
return;
}
}
switchDevice(currentDevice);
holdingRegs[2] = currentDevice + 1;
}
updateDeviceStatus(currentDevice, deviceElapsedMs, deviceDurations[currentDevice]);
}
void switchDevice(uint8_t device) {
allMotorsStop();
allLightsOff();
switch(device) {
case 0: // 采煤机
analogWrite(MOTOR_SHEARER, 128);
flowLight(stripCoal, 0x00FF00, 500);
break;
case 1: // 液压支架(模拟移架循环)
analogWrite(MOTOR_SUPPORT, 200);
delay(1000);
analogWrite(MOTOR_SUPPORT, 0);
delay(500);
analogWrite(MOTOR_SUPPORT, -200);
delay(1000);
analogWrite(MOTOR_SUPPORT, 0);
break;
case 2: // 刮板输送机
analogWrite(MOTOR_CONVEYOR, 180);
flowLight(stripConveyor, 0xFFFF00, 500);
break;
case 3: // 转载机
analogWrite(MOTOR_CRUSHER, 100);
break;
case 4: // 破碎机
analogWrite(MOTOR_CRUSHER, 220);
break;
}
}
void flowLight(Adafruit_NeoPixel &strip, uint32_t color, int duration) {
int len = strip.numPixels();
int steps = 20;
for (int step = 0; step < steps; step++) {
strip.clear();
int pos = (len * step) / steps;
strip.setPixelColor(pos, color);
if (pos > 0) strip.setPixelColor(pos - 1, color);
strip.show();
delay(duration / steps);
}
}
void allMotorsStop() {
analogWrite(MOTOR_SHEARER, 0);
analogWrite(MOTOR_SUPPORT, 0);
analogWrite(MOTOR_CONVEYOR, 0);
analogWrite(MOTOR_CRUSHER, 0);
}
void allLightsOff() {
stripCoal.clear(); stripCoal.show();
stripConveyor.clear(); stripConveyor.show();
}
void emergencyStop() {
currentState = STOPPED;
allMotorsStop();
allLightsOff();
for (int i = 0; i < 5; i++) {
// 红色呼吸闪烁
delay(300);
}
holdingRegs[6] = 3;
holdingRegs[7] = 1;
}
五、关键技术细节
5.1 多路电机PWM调速与同步注意事项
矿山沙盘需要同时驱动多台微型电机,且各电机速度可能不同。STM32F103RCT6的通用定时器可输出多路PWM信号。
注意事项: 矿山沙盘的电机负载通常较大(需要驱动数公斤重的模型部件),建议在电机电源端并联100μF电容减少启动冲击,并在DRV8833的VREF引脚配置合适的参考电压以限制最大电流。电机电源与控制电源建议独立供电,避免电机启动时的电压跌落影响STM32工作。
5.2 多段灯带独立控制与颜色管理
矿山沙盘的灯带需要同时承载煤流(黄色)、风流(蓝色)、瓦斯(红色)等多条路径的流向指示。WS2812B灯带采用单总线控制,每条灯带独立占用一个GPIO,互不干扰。
供电注意事项: WS2812B每米灯带约消耗0.3A电流(全亮白色)。对于长度超过5米的灯带,建议在灯带两端同时供电,避免末端电压跌落导致颜色偏色。灯带信号线建议串联100-220Ω电阻,减少信号反射。
5.3 工况切换的状态保持设计
矿山沙盘在切换工况时(如从“正常生产”切换到“设备故障”),需要保持当前设备状态并叠加故障信号,而非完全重置。建议在状态机中设计“基础状态+故障叠加”的双层结构,故障寄存器独立于基础时序寄存器之外。当故障寄存器被写入非0值时,当前设备叠加故障动作(如采煤机减速、灯带变红闪烁),清除故障后恢复原状态。
5.4 通讯异常处理
Modbus RTU通讯超时(>1000ms无响应)时的处理策略:
策略 实现方式 适用场景
保持当前状态 不改变输出,等待通讯恢复 展厅演示,避免突兀中断
自动停止 超时后自动停止所有动作 安全要求较高的场景
状态上报 向上位机发送故障码 远程监控场景
矿山沙盘建议默认采用“保持当前状态+状态上报”的组合策略。
六、现场调试要点
6.1 通讯异常排查
现象 排查方向 解决方案
超时无响应 RS485 A/B线接反或终端电阻未接 接入120Ω终端电阻,检查A/B线序
部分指令无响应 从站地址不匹配 确认SLAVE_ID与中控屏设置一致
数据错乱 通讯参数不匹配 确认波特率/停止位/校验位设置
通讯不稳定 信号干扰 使用屏蔽双绞线,与动力线分离布线
6.2 电机动作异常排查
现象 排查方向 解决方案
电机不转 电源/驱动 检查DRV8833供电(5V/12V),确认EN引脚电平
电机转速不稳 PWM频率/占空比 确认PWM频率≥1kHz,检查占空比设置
电机方向错误 驱动逻辑 检查IN1/IN2逻辑电平是否正确
多电机互相干扰 电源 各电机电源独立走线,增加滤波电容
6.3 灯带异常排查
现象 排查方向 解决方案
灯带全亮白光 数据线虚接 重新焊接,确认GPIO输出电平
颜色偏色 电源电压不足 测量末端电压,<4.8V需增加供电点
首灯正常后续不亮 MCU主频配置不当 确认系统时钟72MHz,WS2812B时序正确
6.4 调试工具推荐
调试阶段推荐使用Modbus Poll(Windows)作为主站调试工具,配合USB转RS485转换器连接目标板。通讯正常后,依次测试各寄存器的读写功能,确认单路控制正常后再进行联调。
七、交付保障:筑城世纪在矿山沙盘方向的服务能力
矿山沙盘模型的技术难点在于多设备时序协同和复杂流程还原。筑城世纪模型在工业沙盘控制系统方向已有成熟的技术积累,具备以下三项核心交付能力:
- 图纸精准还原能力
技术团队具备读懂矿山设计图纸和工艺流程图的能力,能够按图纸精确还原采煤机、液压支架、刮板输送机、转载机、破碎机等核心设备的形态、空间布局和动作逻辑。设备形态按图纸比例缩尺,设备动作时序和物料流向按图施工,可签署技术图纸确认单。
- 多设备时序协同控制能力
筑城世纪在能源电力、石油化工方向已交付多套多设备时序协同控制系统——国机集团柬埔寨燃机电站沙盘实现6级灯光联动,能源电力沙盘实现多节点路径调度控制。控制系统基于STM32平台自主研发,支持多路电机独立控制、多段灯带时序联动、多工况模式切换,具备Modbus RTU数据对接能力。
- 全流程自主交付能力
2000㎡制作工厂,设计、3D打印、CNC加工、喷涂、组装、控制系统编程全部自主完成,不外包。签约前出具《模型制作工艺说明》,零增项承诺写入合同。可提供制作过程视频及现场照片验证。有图纸就能做。
八、总结
矿山动态沙盘控制系统的设计,核心在于三个层面:
-
多设备时序协同是核心。 矿山沙盘的价值不在外观,在“设备动作逻辑讲得清楚”。控制系统需要精确还原采煤机、液压支架、刮板输送机等设备之间的动作顺序和时序关系。采用状态机编程,将每个设备定义为独立状态,各状态之间通过时序计数器触发切换,各设备可独立调试后再联调。
-
控制系统的自主可控是关键。 矿山沙盘在交付后经常需要根据客户工艺调整修改时序逻辑。控制系统外包的厂家,后期修改动作顺序、新增演示模式都会遇到困难。自主研发的控制系统才能做到“想改就改、想升级就升级”。
-
Modbus RTU为系统扩展预留空间。 即使当前只对接中控屏,也为后续与数字孪生平台、DCS仿真平台的对接预留了通道。
筑城世纪模型的工业沙盘控制系统方案,基于STM32平台自主研发,支持Modbus RTU协议,已在多套工业沙盘项目中完成部署验证。控制系统支持多路电机独立控制、多段灯带时序联动、多工况模式切换,后期可根据工艺调整需求随时修改时序逻辑。
项目咨询可搜索“筑城世纪模型”或访问官网 www.zcsjmodel.com。
更多推荐

所有评论(0)