第二讲:实时监控系统架构

大家好,我是老张。今天咱们聊聊实时监控系统的骨架——架构设计。

做过多轴运动控制的人都知道,监控系统要是没搭好,后面调试起来简直想砸键盘。我刚开始带项目那会儿,就吃过架构混乱的亏。后来慢慢摸索出一套比较稳的套路,今天分享给你。

一、上位机与下位机架构

先说说最基础的东西。实时监控系统,说白了就是「上位机 + 下位机」的组合。

上位机,一般是PC、工控机或者平板。它负责显示数据、记录日志、下发指令。说白了就是「大脑」。

下位机,通常是MCU、DSP或者FPGA。它直接跟电机驱动器、编码器打交道。负责采集数据、执行控制算法。它是「手脚」。

我习惯把这种架构叫做「主从式」架构。上位机是主,下位机是从。但注意,不是所有场景都这么分。有些场合下位机自己就能独立跑,上位机只是做监控。

关键点:上位机和下位机之间,通信延迟必须可控。我见过有人用USB转串口,结果波特率设太低,数据一多就丢包。嗯,这里要注意,通信协议最好选实时性强的,比如EtherCAT、CANopen。

举个例子。我之前做六轴机器人监控,上位机用C#写的WPF界面,下位机用STM32H7。通信走的是CAN FD。上位机每1ms发一次查询指令,下位机收到后立即返回当前各轴的位置、速度、电流。整个过程延迟控制在200μs以内。

为什么会这么严格?因为一旦延迟超过1ms,你看到的「实时数据」其实已经是历史数据了。对于高速运动控制来说,这很危险。

二、实时操作系统(RTOS)简介

下位机跑什么系统?很多人第一反应是「裸机跑循环」。但说实话,对于多轴控制,裸机真的不够用。

我建议用RTOS。比如FreeRTOS、RT-Thread、uC/OS-III。为什么?因为RTOS能帮你做三件事:

  • 任务调度:多个控制任务可以按优先级跑,不会互相卡死
  • 时间确定性:每个任务的响应时间是可预测的
  • 资源管理:信号量、消息队列、互斥锁,这些工具能帮你避免资源冲突

我记得有一次,一个同事用裸机做四轴运动控制。结果在高速运行时,一个轴的位置更新被另一个轴的中断打断了,导致轨迹出现抖动。后来换成FreeRTOS,把位置更新任务设为最高优先级,问题就解决了。

小技巧:RTOS的任务优先级不要设太多。我一般只设3-4级:紧急控制任务、数据采集任务、通信任务、后台任务。优先级太多反而容易出问题。

下面是一个简单的FreeRTOS任务创建示例:

// 创建控制任务
xTaskCreate(
    vControlTask,       // 任务函数
    "Control Task",     // 任务名称
    512,                // 栈大小
    NULL,               // 参数
    4,                  // 优先级(最高)
    NULL                // 任务句柄
);

// 创建采集任务
xTaskCreate(
    vAcquisitionTask,
    "Acquisition Task",
    256,
    NULL,
    3,
    NULL
);

// 创建通信任务
xTaskCreate(
    vCommTask,
    "Communication Task",
    256,
    NULL,
    2,
    NULL
);

你看,这样一划分,每个任务各司其职。控制任务每100μs执行一次,采集任务每500μs执行一次,通信任务每1ms执行一次。互不干扰。

三、监控系统的分层设计

这部分是我最想跟你聊的。一个好的监控系统,一定要分层。我把它分成三层:采集层、处理层、显示层

为什么要分层?你想想看,如果所有代码都揉在一起,改一个显示逻辑,结果把采集逻辑搞崩了,那多冤啊。

下面这张图是我自己画的,能帮你快速理解这三层的关系:

实时监控系统分层架构 显示层 UI界面 · 实时曲线 · 报警显示 · 数据记录 处理层 数据解析 · 滤波算法 · 异常检测 · 协议转换 采集层 传感器驱动 · 编码器读取 · ADC采样 · 通信接口

下面我分别说说每一层。

1. 采集层

采集层在最底下,直接跟硬件打交道。它的任务就是「拿到原始数据」。比如编码器的脉冲数、电流传感器的AD值、温度传感器的电压值。

我曾经犯过一个错:在采集层里做了太多滤波处理。结果导致数据延迟变大,控制效果变差。后来我明白了,采集层只负责「拿数据」,不负责「处理数据」。处理的事交给上一层。

避坑指南:采集层一定要用DMA或者中断方式,不要用轮询。轮询会占用CPU,影响控制任务的实时性。我曾经用轮询读编码器,结果控制周期从100μs抖到了300μs,电机直接啸叫了。

2. 处理层

处理层是承上启下的关键。它从采集层拿到原始数据,然后做三件事:

  • 数据解析:把原始值转换成物理量。比如把编码器脉冲数转成角度,把AD值转成电流值
  • 滤波去噪:用滑动平均、卡尔曼滤波等方法去掉噪声
  • 异常检测:判断数据是否超出正常范围,比如电流过流、位置超限

我习惯在处理层里放一个「数据池」。所有处理完的数据先放到池子里,显示层需要的时候再来取。这样显示层和处理层就解耦了。

// 处理层数据池示例
typedef struct {
    float position[6];    // 六轴位置
    float velocity[6];    // 六轴速度
    float current[6];     // 六轴电流
    uint32_t timestamp;   // 时间戳
    uint8_t status;       // 状态标志
} DataPool_t;

DataPool_t g_dataPool;  // 全局数据池

3. 显示层

显示层是用户直接看到的。它负责把处理层的数据用图形、数字、曲线等形式展示出来。

我个人比较喜欢用曲线图来展示实时数据。因为曲线能直观地看出趋势。比如电机速度的波动、位置跟随误差的变化,一眼就能看出来。

显示层要注意一个问题:刷新频率。不是刷新越快越好。人眼能感知的刷新率也就60Hz左右。你刷到1000Hz,用户也看不出来,反而浪费CPU。

我的习惯:显示层刷新频率设在20-30Hz就够了。但数据记录(比如存到CSV文件)可以设到100Hz以上,方便事后分析。

四、总结一下

今天聊了三个核心点:

  • 上位机与下位机架构:主从式,通信要实时
  • RTOS:任务调度、时间确定性、资源管理
  • 三层设计:采集层拿数据、处理层算数据、显示层看数据

这套架构我用了好几年,从单轴到六轴,从低速到高速,基本都能覆盖。你刚开始做的时候,可以先从简单的两层(采集+显示)开始,慢慢再加处理层。别一口吃成胖子。

好了,今天就到这儿。下次咱们聊聊具体的通信协议怎么选。


专注资料整理