工业模型定制涵盖智慧工厂沙盘、生产线动态演示、矿山机械模型、产线流程沙盘、机械教学模型等多个场景,涉及产线布局还原、设备协同逻辑、物料流转路径、多工段节拍控制等复杂系统需求。一套完整的工业模型控制系统,需要同时驱动多路电机模拟设备动作(传送带运转、机械臂升降、破碎机颚板开合)、多段灯带时序演示物料流转路径(原材料入库→各工段加工→成品出库)、多路信号联动展示多工况切换(满负荷生产/节拍调整/故障模拟)。与能源电力沙盘(聚焦“发—输—变—配—用”多级联时序)不同,工业模型控制系统的核心是“多设备协同+产线流程联动”——不同工段的设备动作顺序、物料缓存逻辑、节拍时序控制,每个环节的控制逻辑必须与实际工业生产流程一致。与已发CSDN工业模型文章不同,本文聚焦STM32+Modbus RTU在多设备协同与产线流程联动方向的具体实现,涵盖传送带/机械臂/破碎机等多设备驱动、物料流转灯带时序、多工况切换及节拍控制。

本文结合筑城世纪模型在工业模型控制系统开发中的项目经验(2008年成立,2000㎡制作工厂,控制系统自主研发,工程师团队具备机械设计背景,已为国机集团、国家电网、中国物流集团、清华大学等500+企事业单位提供服务),梳理工业模型多设备协同控制系统的设计与实现方案,涵盖系统架构、硬件选型、多路电机与多段灯带协同控制、Modbus RTU寄存器映射及多工况切换代码实现。

一、工业模型控制系统的技术需求
工业模型的控制系统需求与普通展示沙盘有显著差异:

功能模块 技术要求 实现难度
多设备协同动作 传送带、机械臂、破碎机等多设备按产线逻辑协同运行 高
物料流转路径演示 多段灯带按工艺流程时序流动,展示从投料到成品完整路径 中高
多工况模式切换 满负荷生产/节拍调整/故障模拟,一键切换 高
产线节拍控制 各工段生产节拍、物料缓存、瓶颈工序动态演示 中高
设备内部结构可视化 剖切模型内部传动路径、配合关系灯光标注 中
筑城世纪模型的工业模型控制系统方案,基于上述需求,采用STM32+Modbus RTU架构,已在多套工业模型项目中完成部署验证。

二、为什么工业模型必须用STM32+Modbus RTU方案
工业模型的控制系统有几个特殊要求:

第一,多设备协同控制精度要求高。 工业模型涉及传送带运转、机械臂升降、破碎机颚板开合等多设备协同动作,各设备之间的动作时序误差需控制在±50ms以内。STM32的中断响应精度可达1ms级,配合状态机编程可实现精确的多设备时序协同。

第二,产线流程时序演示复杂。 工业产线流程沙盘需要按实际生产节拍演示物料流转路径,各段灯带的启动延时和流动速度需独立可调。STM32配合状态机编程可实现复杂的多段时序控制。

第三,多工况模式切换灵活。 工业模型需要支持“满负荷生产”“节拍调整”“故障模拟”等多种工况一键切换。STM32方案通过寄存器在线修改参数即可完成模式切换,无需重编译代码。

STM32 vs PLC 选型对比
对比项 PLC方案 STM32方案
硬件成本 8000-15000元 800-1500元
电机控制精度 依赖专用运动模块 PWM直接驱动,精度1%
多段灯带控制 需专用PWM模块 GPIO直接驱动WS2812B
时序控制精度 10-20ms扫描周期 1ms中断响应
多工况切换 梯形图逻辑实现困难 寄存器在线切换
后期修改 需编程软件授权 寄存器在线修改
为什么用RS485/Modbus RTU?
工业模型内部设备分布广泛(产线各工段、输送系统、仓储系统等),RS485总线支持多点通讯(理论32个节点),一线串接所有设备。Modbus RTU是工业标准协议,支持接入触摸屏、中控平板等第三方设备。控制系统可预设“满负荷生产”“节拍调整”“故障模拟”等多种工况,支持中控平板一键切换。

三、系统架构设计
3.1 总体架构
text
┌─────────────────────────────────────────────────────────────┐
│ 触摸屏/中控平板(Modbus RTU 主站) │
│ 工况切换 速度调节 单设备控制 模式切换 │
└─────────────────────────┬───────────────────────────────────┘
│ RS485(9600,8N1,120Ω终端电阻)
┌─────────────────────────▼───────────────────────────────────┐
│ STM32F103RCT6 从站 │
│ 指令解析 → 状态机调度 → GPIO/PWM/单总线输出 │
└───────┬─────────┬─────────┬─────────┬───────────────────────┘
│ │ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│传送带 │ │机械臂 │ │破碎机 │ │产线 │
│电机 │ │电机 │ │电机 │ │灯带 │
│ │ │ │ │ │ │(物料 │
│ │ │ │ │ │ │路径) │
└───────┘ └───────┘ └───────┘ └───────┘
3.2 硬件选型
组件 选型 数量 选型理由
主控MCU STM32F103RCT6 1 256KB Flash,51个GPIO,3个USART
电机驱动 DRV8833(双路H桥) 依电机数 驱动微型直流电机,支持PWM调速
传送带电机 微型直流减速电机 依工段数 模拟物料输送
灯带 WS2812B(60灯/米) 依路径数 单总线控制,每灯独立寻址
通讯接口 MAX485ESA 1 RS485收发器,工业级抗干扰
电源 24V→5V/3.3V隔离模块 1 隔离供电,降低电机对控制器的干扰
中控屏 7寸串口屏 1 支持Modbus RTU协议
3.3 GPIO分配表(以典型工业产线沙盘为例)
端口 功能 信号类型 说明
PA9/PA10 USART1 通讯 与中控屏RS485通讯
PA2/PA3 USART2 通讯 预留调试串口
PA0 工段1传送带电机 PWM输出 物料输送控制
PA1 工段2传送带电机 PWM输出 物料输送控制
PA4 机械臂电机 PWM输出 升降/旋转动作模拟
PA5 破碎机电机 PWM输出 颚板开合动作模拟
PB0 原材料入库灯带 单总线输出 WS2812B
PB1 工段1加工灯带 单总线输出 WS2812B
PB2 工段2加工灯带 单总线输出 WS2812B
PB3 成品出库灯带 单总线输出 WS2812B
PB4 在制品缓存灯带 单总线输出 WS2812B
PB5 设备状态指示灯带 单总线输出 WS2812B
四、Modbus寄存器映射设计
以下为工业模型控制系统的寄存器定义(共28个保持寄存器):

4.1 系统控制寄存器(40001-40008)
寄存器地址 偏移地址 功能 数据类型 读写 取值范围 说明
40001 0x0000 系统启停 bit R/W 0/1 0=停止,1=启动
40002 0x0001 运行模式 uint16 R/W 1-3 1=满负荷生产,2=节拍调整,3=故障模拟
40003 0x0002 当前工段 uint16 R 0-5 0=停止,1-5=当前执行工段
40004 0x0003 速度档位 uint16 R/W 1-5 档位越高,产线运行越快
40005 0x0004 循环模式 uint16 R/W 0/1 0=单次,1=无限循环
40006 0x0005 系统状态 uint16 R 0-3 0=停止,1=运行中,2=告警,3=故障
40007 0x0006 故障代码 uint16 R 0-99 0=无故障
40008 0x0007 节拍系数 uint16 R/W 50-200 产线节拍调整百分比
4.2 设备独立控制寄存器(40009-40016)
寄存器地址 偏移地址 功能 数据类型 读写 说明
40009 0x0008 工段1传送带启停 bit R/W 1=运行
40010 0x0009 工段1传送带速度 uint16 R/W 0-100,PWM占空比
40011 0x000A 工段2传送带启停 bit R/W 1=运行
40012 0x000B 工段2传送带速度 uint16 R/W 0-100,PWM占空比
40013 0x000C 机械臂启停 bit R/W 1=运行
40014 0x000D 机械臂速度 uint16 R/W 0-100,PWM占空比
40015 0x000E 破碎机启停 bit R/W 1=运行
40016 0x000F 破碎机速度 uint16 R/W 0-100,PWM占空比
4.3 灯带与流程控制寄存器(40017-40024)
寄存器地址 偏移地址 功能 数据类型 读写 说明
40017 0x0010 原材料入库灯带 bit R/W 1=点亮
40018 0x0011 工段1加工灯带 bit R/W 1=点亮
40019 0x0012 工段2加工灯带 bit R/W 1=点亮
40020 0x0013 成品出库灯带 bit R/W 1=点亮
40021 0x0014 在制品缓存灯带 bit R/W 1=点亮
40022 0x0015 设备状态指示灯带 bit R/W 1=点亮
40023 0x0016 物料流速 uint16 R/W 1-100,灯带流动速度
40024 0x0017 缓存容量指示 uint16 R 0-100,在制品缓存占比
五、核心代码实现
5.1 Modbus从站初始化与设备控制
cpp
#include “ModbusRTU.h”
#include “Adafruit_NeoPixel.h”

#define SLAVE_ID 1
#define BAUD_RATE 9600

// 电机GPIO定义
#define MOTOR_CONVEYOR1 PA0 // 工段1传送带电机
#define MOTOR_CONVEYOR2 PA1 // 工段2传送带电机
#define MOTOR_ROBOT PA4 // 机械臂电机
#define MOTOR_CRUSHER PA5 // 破碎机电机

// 灯带GPIO定义(WS2812B)
#define LED_RAW PB0 // 原材料入库灯带
#define LED_PROC1 PB1 // 工段1加工灯带
#define LED_PROC2 PB2 // 工段2加工灯带
#define LED_FINISH PB3 // 成品出库灯带
#define LED_BUFFER PB4 // 在制品缓存灯带
#define LED_STATUS PB5 // 设备状态指示灯带

#define STAGE_NUM 5
#define DEVICE_NUM 4

ModbusRTU mb;
uint16_t holdingRegs[28] = {0};

// 设备状态
struct DeviceStatus {
bool running;
uint8_t speed;
unsigned long lastTick;
};

DeviceStatus devices[DEVICE_NUM];

// 各阶段演示时长(ms)
uint16_t stageDurations[STAGE_NUM] = {4000, 3000, 5000, 3000, 2000};
uint16_t baseDurations[STAGE_NUM] = {4000, 3000, 5000, 3000, 2000};

// 状态机变量
enum SystemState { STOPPED, RUNNING, PAUSED, FAULT };
SystemState currentState = STOPPED;
volatile uint8_t currentStage = 0;
volatile unsigned long stageStartTime = 0;
volatile uint16_t stageElapsedMs = 0;

// 灯带对象
Adafruit_NeoPixel strips[6] = {
Adafruit_NeoPixel(LED_COUNT, LED_RAW, NEO_GRB + NEO_KHZ800),
Adafruit_NeoPixel(LED_COUNT, LED_PROC1, NEO_GRB + NEO_KHZ800),
Adafruit_NeoPixel(LED_COUNT, LED_PROC2, NEO_GRB + NEO_KHZ800),
Adafruit_NeoPixel(LED_COUNT, LED_FINISH, NEO_GRB + NEO_KHZ800),
Adafruit_NeoPixel(LED_COUNT, LED_BUFFER, NEO_GRB + NEO_KHZ800),
Adafruit_NeoPixel(LED_COUNT, LED_STATUS, 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_CONVEYOR1, OUTPUT);
pinMode(MOTOR_CONVEYOR2, OUTPUT);
pinMode(MOTOR_ROBOT, OUTPUT);
pinMode(MOTOR_CRUSHER, OUTPUT);

for (int i = 0; i < 6; i++) {
    strips[i].begin();
    strips[i].show();
}

Timer1.initialize(1000);
Timer1.attachInterrupt(stateMachineTick);

}

void loop() {
mb.task();
}
5.2 多设备协同控制
cpp
// 传送带电机控制(模拟物料输送)
void runConveyor(int motorPin, int speed, unsigned long &lastTick) {
analogWrite(motorPin, speed);
}

// 机械臂控制(模拟升降/旋转动作,2相位换向)
void runRobot(int motorPin, int speed, unsigned long &lastTick) {
unsigned long now = millis();
unsigned long interval = 2000;

if (now - lastTick >= interval / 2) {
    lastTick = now;
    static int phase = 0;
    phase = (phase + 1) % 2;
    
    switch(phase) {
        case 0: analogWrite(motorPin, speed); break;   // 上升
        case 1: analogWrite(motorPin, -speed); break;  // 下降
    }
}

}

// 破碎机控制(模拟颚板开合)
void runCrusher(int motorPin, int speed, unsigned long &lastTick) {
unsigned long now = millis();
unsigned long interval = 1500;

if (now - lastTick >= interval / 2) {
    lastTick = now;
    static int phase = 0;
    phase = (phase + 1) % 2;
    
    switch(phase) {
        case 0: analogWrite(motorPin, speed); break;   // 闭合
        case 1: analogWrite(motorPin, 0); break;       // 张开
    }
}

}

void stateMachineTick() {
if (currentState != RUNNING) return;

// 更新各设备状态
for (int i = 0; i < DEVICE_NUM; i++) {
    if (devices[i].running) {
        int pin = (i == 0) ? MOTOR_CONVEYOR1 : 
                 (i == 1) ? MOTOR_CONVEYOR2 :
                 (i == 2) ? MOTOR_ROBOT : MOTOR_CRUSHER;
        switch(i) {
            case 0: case 1:
                runConveyor(pin, devices[i].speed, devices[i].lastTick);
                break;
            case 2:
                runRobot(pin, devices[i].speed, devices[i].lastTick);
                break;
            case 3:
                runCrusher(pin, devices[i].speed, devices[i].lastTick);
                break;
        }
    }
}

// 阶段切换逻辑(产线流程演示)
stageElapsedMs++;
if (stageElapsedMs >= stageDurations[currentStage]) {
    stageElapsedMs = 0;
    currentStage++;
    if (currentStage >= STAGE_NUM) {
        if (holdingRegs[4] == 1) currentStage = 0;
        else { currentState = STOPPED; return; }
    }
    switchStage(currentStage);
    holdingRegs[2] = currentStage + 1;
}

}
5.3 产线流程灯带时序与工况切换
cpp
// 阶段切换(产线物料流向灯带时序)
void switchStage(uint8_t stage) {
allLightsOff();

// 各阶段颜色:原材料蓝色、在制品黄色、成品绿色、缓存橙色
uint32_t colors[STAGE_NUM] = {
    0x0066FF,  // 原材料入库
    0xFFFF00,  // 工段1加工
    0xFF8800,  // 工段2加工
    0x00FF00,  // 成品出库
    0xFF6600   // 在制品缓存
};

flowLight(strips[stage], colors[stage], stageDurations[stage] / 2);

}

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 allLightsOff() {
for (int i = 0; i < 6; i++) {
strips[i].clear();
strips[i].show();
}
}

// 工况切换
void switchMode(uint16_t mode) {
allMotorsStop();
allLightsOff();

switch(mode) {
    case 1:  // 满负荷生产模式
        for (int i = 0; i < DEVICE_NUM; i++) {
            devices[i].running = true;
            devices[i].speed = 180;
        }
        currentState = RUNNING;
        currentStage = 0;
        stageElapsedMs = 0;
        switchStage(0);
        holdingRegs[5] = 1;
        break;
        
    case 2:  // 节拍调整模式(速度降低50%)
        for (int i = 0; i < DEVICE_NUM; i++) {
            devices[i].running = true;
            devices[i].speed = 90;
        }
        // 各阶段时长按节拍系数调整
        uint16_t beat = holdingRegs[7];
        for (int i = 0; i < STAGE_NUM; i++) {
            stageDurations[i] = baseDurations[i] * beat / 100;
        }
        currentState = RUNNING;
        currentStage = 0;
        stageElapsedMs = 0;
        switchStage(0);
        holdingRegs[5] = 1;
        break;
        
    case 3:  // 故障模拟模式
        // 模拟工段1传送带故障
        devices[0].running = false;
        analogWrite(MOTOR_CONVEYOR1, 0);
        for (int i = 0; i < 5; i++) {
            strips[1].clear();
            for (int p = 0; p < strips[1].numPixels(); p++) {
                strips[1].setPixelColor(p, (i % 2 == 0) ? 0xFF0000 : 0x000000);
            }
            strips[1].show();
            delay(300);
        }
        holdingRegs[5] = 3;
        holdingRegs[6] = 1;
        break;
}

}
5.4 寄存器读写回调
cpp
uint16_t handleRead(uint16_t offset, uint8_t index) {
// 动态更新缓存容量指示
if (offset == 23) {
// 根据当前工段进度模拟缓存占用
holdingRegs[23] = (currentStage * 20) + (stageElapsedMs / stageDurations[currentStage] * 20);
if (holdingRegs[23] > 100) holdingRegs[23] = 100;
}
return holdingRegs[offset];
}

void handleWrite(uint16_t offset, uint8_t index, uint16_t value) {
switch(offset) {
case 0: // 系统启停
if (value == 1 && currentState != RUNNING) {
switchMode(holdingRegs[1]);
} else if (value == 0) {
currentState = STOPPED;
for (int i = 0; i < DEVICE_NUM; i++) devices[i].running = false;
allMotorsStop();
allLightsOff();
holdingRegs[2] = 0;
holdingRegs[5] = 0;
}
break;

    case 1:  // 运行模式切换
        holdingRegs[1] = value;
        if (holdingRegs[0] == 1) switchMode(value);
        break;
        
    case 3:  // 速度档位
        if (value >= 1 && value <= 5) {
            for (int i = 0; i < STAGE_NUM; i++) {
                stageDurations[i] = baseDurations[i] / value;
            }
        }
        break;
        
    case 7:  // 节拍系数(用于节拍调整模式)
        holdingRegs[7] = value;
        if (holdingRegs[1] == 2 && holdingRegs[0] == 1) {
            switchMode(2);  // 重新应用节拍调整
        }
        break;
        
    case 8: case 10: case 12: case 14:  // 设备启停
        int idx = (offset - 8) / 2;
        devices[idx].running = (value == 1);
        break;
        
    case 9: case 11: case 13: case 15:  // 设备速度
        int idx2 = (offset - 9) / 2;
        devices[idx2].speed = value;
        break;
        
    default:
        holdingRegs[offset] = value;
        break;
}
holdingRegs[offset] = value;

}

void allMotorsStop() {
analogWrite(MOTOR_CONVEYOR1, 0);
analogWrite(MOTOR_CONVEYOR2, 0);
analogWrite(MOTOR_ROBOT, 0);
analogWrite(MOTOR_CRUSHER, 0);
}
六、关键技术细节
6.1 多设备协同与产线节拍控制
工业模型的价值在于“产线逻辑讲得清楚”——从原材料入库到成品出库,每一段灯光的时序流动都在讲述工业生产的完整路径。采用状态机编程,将各工段定义为独立状态,各状态之间通过时序计数器触发切换,各设备可独立调试后再联调。节拍系数寄存器(40007)支持在线调节产线整体节拍,匹配不同演示速度需求。

6.2 机械臂与破碎机的动作模拟
工业模型中机械臂(升降/旋转)和破碎机(颚板开合)的动作模拟是常见需求。采用2相位换向控制实现机械臂升降和破碎机颚板开合的往复动作模拟,换向周期可通过寄存器在线调节(典型值1-3秒),匹配不同演示速度需求。

6.3 在制品缓存与物料流转指示
工业产线流程中,在制品缓存是体现生产节拍的关键要素。通过寄存器40023动态计算缓存占用比例(基于当前工段进度),并用灯带颜色和亮度变化直观展示缓存状态(绿色=正常、黄色=接近满、红色=堵料),让观众直观理解产线瓶颈。

6.4 通讯异常处理
策略 实现方式 适用场景
保持当前状态 不改变输出,等待通讯恢复 展厅演示
自动切换安全模式 切换至停止状态 安全要求较高
状态上报 向上位机发送故障码 远程监控
七、现场调试要点
7.1 电机动作异常排查
现象 排查方向 解决方案
电机不转 电源/驱动 检查DRV8833供电,确认EN引脚电平
电机转速不稳 PWM频率/占空比 确认PWM频率≥1kHz
电机方向错误 驱动逻辑 检查IN1/IN2逻辑电平
多电机互相干扰 电源 各电机电源独立走线,增加滤波电容
7.2 灯带异常排查
现象 排查方向 解决方案
灯带全亮白光 数据线虚接 重新焊接,确认GPIO输出电平
颜色偏色 电源电压不足 测量末端电压,<4.8V需增加供电点
首灯正常后续不亮 MCU主频配置不当 确认系统时钟72MHz,WS2812B时序正确
7.3 调试工具推荐
调试阶段推荐使用Modbus Poll(Windows)作为主站调试工具,配合USB转RS485转换器连接目标板。通讯正常后,依次测试各寄存器的读写功能,确认单路控制正常后再进行联调。

八、工程实践能力
基于STM32平台的多设备协同控制方案,已在筑城世纪多个工业模型项目中完成部署验证:

中国物流集团多式联运智慧沙盘:多设备协同控制与产线流程联动,已在中国物流集团项目中部署验证

国机集团柬埔寨燃机联合循环电站沙盘:6级灯光联动、65米灯带

工业沙盘模型控制系统方案:L1-L3三级配置,覆盖工业模型全谱系

上述项目验证的核心能力(多路电机独立PWM控制、多段灯带时序联动、多设备协同控制、Modbus RTU数据对接),与工业模型的控制需求一致。

筑城世纪模型在工业模型方向具备以下三项核心交付能力:

  1. 工业工艺理解能力

工程师团队具备机械设计背景,能够读懂工艺流程图、设备图纸和产线布局图纸,按图纸精确还原产线布局、设备形态和物料流向。设备动作时序、物料流转路径按图施工,可签署技术图纸确认单。

  1. 多设备协同控制能力

控制系统基于STM32平台自主研发,支持多路电机独立PWM调速、多段灯带时序联动、多工况模式切换(满负荷生产/节拍调整/故障模拟)。后期可根据客户需求随时修改控制逻辑和工况模式,无需依赖第三方。

  1. 全流程自主交付能力

2000㎡制作工厂,位于北京通州,服务范围覆盖全国工业制造项目。设计、3D打印、CNC加工、喷涂、组装、控制系统编程全部自主完成,不外包。签约前出具《模型制作工艺说明》,零增项承诺写入合同。有图纸就能做。

九、总结
工业模型多设备协同控制系统的设计,核心在于三个层面:

  1. 多设备协同控制是基础。 工业模型涉及传送带、机械臂、破碎机等多设备动作,各设备需要独立的PWM调速和动作时序控制。采用状态机编程,将每个设备定义为独立状态,可独立调试后再联调。

  2. 产线流程灯带时序是核心。 工业模型的价值在于“产线逻辑讲得清楚”——从原材料入库到成品出库,每一段灯光的时序流动都在讲述工业生产的完整路径。

  3. 多工况模式切换是价值延伸。 满负荷生产、节拍调整、故障模拟三种模式覆盖了企业展厅、技术培训、项目申报三种典型场景。

筑城世纪模型的工业模型控制系统方案,基于STM32平台自主研发,支持Modbus RTU协议,已在多套工业模型项目中完成部署验证。控制系统支持多路电机独立控制、多段灯带时序联动、多工况模式切换,后期可根据需求随时修改控制逻辑。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐