边缘计算网关:本地 AI 推理,断网也能用
前几篇的 CV、环控、行为分析,都提到了「在边缘侧跑」。这篇专门讲边缘网关------为什么用 RK3588、怎么部署模型、断网时数据怎么缓存补传、以及边缘-云端如何协同。
为什么需要边缘网关?
三个字:快、稳、省。
| 对比维度 | 边缘推理(RK3588) | 云端推理(API) |
|---|---|---|
| 延迟 | 50-100ms | 300-800ms |
| 断网 | 正常工作 | 全瘫 |
| 带宽 | 只传结果,0.2KB/次 | 传原图,200KB/次 |
| 成本 | 一次性 1000 元 | 按量付费,1万次/月≈100元 |
农业场景断网是常态。农村 4G 基站覆盖不稳定,刮风下雨掉线是家常便饭。病虫害巡检的轨道车边走边拍------如果每张照片都要传云端等结果,100ms 延迟意味着小车要多走 10cm,错过的病叶就漏掉了。
RK3588 硬件选购与刷机
| 型号 | 内存 | 存储 | 价格 | 推荐 |
|---|---|---|---|---|
| Orange Pi 5 Plus | 8GB | 32GB eMMC | 800 元 | ⭐ 性价比之选 |
| Firefly ITX-3588J | 16GB | 64GB eMMC | 1500 元 | 工业级,-20℃~60℃ |
| Radxa Rock 5B | 8GB | 无 eMMC(插 TF 卡) | 700 元 | 最便宜 |
推荐 Orange Pi 5 Plus。 8GB 内存足够跑 YOLOv8n + 姿态估计 + 本地 SQLite + MQTT 客户端,余量还很多。
刷机(以 Orange Pi 5 为例):
bash
# 1. 下载官方 Ubuntu 22.04 镜像
wget https://github.com/Joshua-Riek/ubuntu-rockchip/releases/download/...
# 2. 用 Balena Etcher 或 rkdeveloptool 烧录到 TF 卡
sudo rkdeveloptool db rk3588_spl_loader_v1.08.111.bin
sudo rkdeveloptool wl 0 ubuntu-22.04-preinstalled.img
# 3. 插卡开机,第一次需要接显示器设置网络
# 4. 启用 NPU
sudo apt install rockchip-mpp rockchip-npu-driver
模型转换与部署:PyTorch → RKNN
RK3588 的 NPU(6 TOPS)必须用 RKNN 格式,不能直接跑 PyTorch。
bash
# 1. 安装 RKNN-Toolkit2(在 x86 开发机上)
pip install rknn-toolkit2
# 2. PyTorch → ONNX
python -c "
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
model.export(format='onnx', opset=12)
"
# 3. ONNX → RKNN(量化到 int8 提速)
python -c "
from rknn.api import RKNN
rknn = RKNN()
rknn.config(mean_values=[[0,0,0]], std_values=[[255,255,255]], target_platform='rk3588')
rknn.load_onnx('yolov8n.onnx')
rknn.build(do_quantization=True, dataset='calibration.txt') # 量化数据集
rknn.export_rknn('yolov8n.rknn')
"
# 4. 把 rknn 文件 scp 到 RK3588 上
scp yolov8n.rknn orangepi@192.168.1.100:~/models/
在 RK3588 上跑推理:
python
from rknnlite.api import RKNNLite
import cv2, numpy as np
rknn = RKNNLite()
rknn.load_rknn('models/yolov8n.rknn')
rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_AUTO)
def detect(frame):
img = cv2.resize(frame, (640, 640))
img = np.expand_dims(img, 0)
outputs = rknn.inference(inputs=[img])
# outputs 是 (1, 84, 8400) 的 numpy 数组
# 后处理:解码 bbox + NMS
boxes = decode_outputs(outputs, conf_thres=0.5, iou_thres=0.45)
return boxes
# 接 USB 摄像头实时检测
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
boxes = detect(frame)
draw_boxes(frame, boxes)
cv2.imshow('Edge Detection', frame)
k = cv2.waitKey(1)
if k == 27: break
YOLOv8n 在 RK3588 NPU 上跑 640×640 推理:约 18-22ms(~50 FPS)。够你在猪舍里实时数猪了。
断网数据缓存与补传------SQLite + 本地队列
网络断了,数据不能丢。方案:
传感器数据 → ESP32 → (WiFi) → RK3588 本地接收
├── MQTT 在线 → 直传 EMQX
└── MQTT 离线 → 存 SQLite → 网络恢复后补传
实现:
python
import sqlite3
import json
import paho.mqtt.client as mqtt
class EdgeDataBuffer:
def __init__(self, db_path='cache.db'):
self.conn = sqlite3.connect(db_path)
self.conn.execute('''
CREATE TABLE IF NOT EXISTS data_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT,
topic TEXT,
payload TEXT,
qos INTEGER,
ts INTEGER
)
''')
def push(self, topic, payload, qos=1):
"""存入本地缓存"""
self.conn.execute(
'INSERT INTO data_cache (topic, payload, qos, ts) VALUES (?, ?, ?, ?)',
(topic, json.dumps(payload), qos, int(time.time()))
)
self.conn.commit()
def flush(self, mqtt_client):
"""补传所有缓存数据,发一条删一条"""
rows = self.conn.execute(
'SELECT id, topic, payload, qos FROM data_cache ORDER BY id'
).fetchall()
for row in rows:
id_, topic, payload, qos = row
result = mqtt_client.publish(topic, payload, qos=qos)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
self.conn.execute('DELETE FROM data_cache WHERE id = ?', (id_,))
self.conn.commit()
remaining = self.conn.execute('SELECT COUNT(*) FROM data_cache').fetchone()[0]
return remaining
MQTT 的 on_connect 回调里自动触发补传:
python
def on_connect(client, userdata, flags, rc):
if rc == 0:
print('MQTT 已连接,开始补传缓存数据...')
remaining = buffer.flush(client)
print(f'补传完成,剩余 {remaining} 条')
边缘-云端协同策略
不是所有数据都值得发到云端。在边缘侧做一次过滤,只传有价值的:
python
def should_upload(data):
"""判断这条数据是否需要上云"""
# 1. 告警数据:无条件上传
if data.get('alarm'):
return True
# 2. 普通传感器数据:时间间隔采样
if time.time() - last_upload_time[data['dev']] < 60:
return False # 1 分钟内不重复传
# 3. 变化超过阈值的才传
last = last_known_value.get(data['dev'], {})
if abs(data.get('air_temp', 0) - last.get('air_temp', 0)) < 0.5:
return False # 温度没变化超过 0.5℃,免传
# 4. 图像数据:只传检测到异常(有病斑/有异常行为)的帧
if data['type'] == 'image' and not data.get('anomaly_detected'):
return False
return True
云边协同的数据流:
边缘层(RK3588) 云端(Spring Boot)
───────────────── ─────────────────
采集数据
↓
本地推理(CV/异常检测)
↓
should_upload?
├─ No → 本地 SQLite(7天过期)
└─ Yes → MQTT publish ────────→ EMQX → TDengine(永久存储)
↓
规则引擎 → 告警
↓
ECharts 大屏
运维要点
- 监控 NPU 温度:RK3588 满载时能到 70℃,盛夏季节需要主动散热(5V 小风扇即可)
- TF 卡寿命:频繁写 SQLite 会磨损 TF 卡。缓存数据存 eMMC,日志做 logrotate 7 天清理
- OTA 升级 :通过 MQTT 下发模型文件 URL → RK3588 下载新
.rknn文件 → reload → 无缝切换。不要用 SSH 一台台登。 - 看门狗:设一个守护进程,每 30 秒检测 NPU 推理进程是否存活,死了自动重启
下一篇:《智慧农业创业半年:我踩过的非技术坑》------怎么找客户、怎么报价、怎么落地,以及(适度打码的)真实营收数据。