工业边缘计算节点编程:在本地完成生产数据统计,无需上传云端
工业产线最怕两件事:网络一断数据空白、云往返延迟让告警慢半拍。边缘计算节点不是“把MCU数据转发到服务器”,而是把计数、良率、节拍、班产这些统计在网关/工控机本地算完,云只拿汇总结果。 架构本质是三层解耦:采集线程只往本地缓冲写,统计任务周期聚合,上传任务可断可续——不上云也能跑完整班。
一、边缘节点职责边界
现场数据源是PLC、Modbus从站、扫码枪、光电计数、OPC UA;边缘节点干四件事:
协议归一:Modbus/TCP、RTU、CANopen统一成内部事件
本地过滤:重复脉冲、抖动误触发先消掉
实时统计:产量/良品/不良/运行时间/节拍
存储缓存:断网落本地,恢复后续传
规则很简单:原始高频数据不留云,只留聚合结果。1秒级明细存本地几小时,分钟级汇总才往上送。
二、统计结构体:以工位为单位
别用全局变量散着加,按station建表:
typedef struct {
uint8_t station;
uint32_t total; // 总产出
uint32_t good; // 良品
uint32_t bad; // 不良
uint32_t run_ms; // 本班运行时间
uint32_t stop_ms; // 停机时间
uint32_t cyc_last_us; // 上次产品通过时间戳
float cyc_avg_us; // 平均节拍
uint32_t last_seq; // 断点续传序号
} stat_t;
stat_t st[16];
良率、性能稼动率都是派生值,现场不算也行,HMI本地读结构体直接显示。
三、采集回调:只计数不联网
光电/编码器/PLC完成信号统一进事件函数,去抖后更新计数:
void on_product_pulse(uint8_t ch, uint8_t is_good){
stat_t*s = &st[ch];
uint32_t now = tick_us();
if(s->cyc_last_us){
uint32_t dt = now - s->cyc_last_us;
/* 滑动平均,滤掉偶发粘连 */
s->cyc_avg_us = s->cyc_avg_us*0.8f + dt*0.2f;
}
s->cyc_last_us = now;
s->total++;
if(is_good) s->good++;
else s->bad++;
stat_mark_dirty(ch); // 标记待落盘,不在这里写Flash
}
void on_run_state(uint8_t ch, uint8_t running){
stat_t*s = &st[ch];
if(running) s->run_ms += tick_ms()-s->last_t;
else s->stop_ms+= tick_ms()-s->last_t;
s->last_t = tick_ms();
}
采集路径零分配、零文件系统调用,绝不在中断里碰SD卡。
四、本地聚合任务:1秒/1分钟滚动
单独任务跑聚合,算班产、良率、节拍:
void stat_aggregate_1s(void){
for(int i=0;i<16;i++){
stat_t*s=&st[i];
float yld = s->total? (float)s->good/s->total*100.0f : 0;
local_publish(s->station, s->total, yld,
s->cyc_avg_us/1000.0f);
}
}
需要OEE再补:可用率=run/(run+stop),性能=实际节拍/理论节拍。 但边缘节点默认只算到“班产+良率+节拍”就够产线看板用了。
五、本地存储:RAM环形+非易失落盘
实时计数在RAM,掉电会丢;关键的是“已汇总结果”和“待补传记录”要落盘。
小数据:FRAM/EEPROM存班统计快照
大数据:LittleFS/SPIFFS存JSON汇总
断网缓存:SQLite或自定义环形扇区
typedef struct {
uint32_t seq;
uint32_t ts;
uint8_t station;
uint16_t total;
uint16_t good;
uint8_t sync; // 0待传 1已传
} rec_t;
rec_t cache[256];
int c_w, c_r;
void stat_flush_to_disk(void){
rec_t*r=&cache[c_w];
r->seq = ++st[0].last_seq;
r->ts = rtc_ms();
r->total = st[0].total; r->good=st[0].good;
r->sync=0;
cache_write_sector(r);
c_w=(c_w+1)&255;
}
写盘频率别太高,1秒~1分钟一条汇总,Flash寿命和带宽都够。
六、断点续传:按seq补,不重不漏
上传线程和采集线程解耦,网络恢复后从最老未确认记录开始发:
void upload_task(void){
if(!net_ok()) return;
while(c_r != c_w){
rec_t*r=&cache[c_r];
if(r->sync) { c_r=(c_r+1)&255; continue; }
if(mes_post(r)==0){
r->sync=1;
cache_mark_sync(r->seq);
c_r=(c_r+1)&255;
}else break; // 网络又断,留原地
}
}
要点:
每条带seq+时间戳,云端按seq去重
只有收到ACK才标sync,否则重发
缓冲满按工位优先级淘汰老数据
实时告警(停机/不良突增)可越过缓存直发
七、落坑清单
每脉冲写一次Flash→擦写寿命耗尽
采集ISR里HTTP上传→阻塞丢脉冲
断网不落盘→恢复后班产空白
续传从头发→云端重复计数
不带seq→网络抖动重复插入
原始数据全传云→带宽被脉冲流打满
良率用全厂平均→工序问题被掩盖
节拍用理论值→性能损失算不出来
HMI直接读全局变量→多任务竞态
上电不清last_t→运行时间跳变
八、落地顺序
定义station维度统计结构体,采集只改RAM
脉冲/完成信号去抖后计数,绝不联网
1s任务算班产、良率、平均节拍
汇总记录写LittleFS/SQLite,带seq+ts
网络线程独立,断网沉淀本地
恢复后按seq顺序补传,ACK才清sync
HMI读本地统计,不依赖云端在线
关键告警可越级直发,普通数据批量补
边缘节点做生产统计的核心不是“会算数”,是采集和上传解耦:高频事件在RAM里聚合,汇总结果才落盘,网络只是可选通道。 断不断网都不影响现场计数和看板;真要回传MES,也是带seq的分钟级汇总,不是原始脉冲流。这样工业现场哪怕4G断几小时,恢复后数据按序补齐,班产、良率、节拍都不会漂——云端在这里只是备份,不是主计算。





