1 简介
本文基于 Milk-V DuoS 开发板 的监控系统的详细方案与实现流程,结合硬件选型、软件部署、算法优化和系统集成。系统架构如下所示。

2 系统功能
1、识别能力
• 识别速度:< 300 ms(含活体)
• 准确率:≥ 99.5 %
2、监控逻辑
• 硬件 NPU 人形/宠物/车辆检测,误报低
• 事件触发 → 本地 JPG 截图 + MQTT 告警 → 微信群通知
• MQTT 上报记录(JSON:ID、时间、抓拍图 base64)
3、管理后台
• Web 端(Python+ Django)
• 与安防系统联动:安防信号输入→报警
• 支持 HTTPS Web 端、微信小程序、Home-Assistant 插件
• HTTP RESTful API(远程监控、参数配置)
3 硬件架构
1、计算平台
• Milk-V DuoS 开发板(SG2000,512 MB RAM,0.5 TOPS INT8 TPU,双 MIPI-CSI)
• 供电:5 V/2 A Type-C;底板可扩展 12 V 门锁电源
2、视觉采集
• MIPI-CSI 摄像头
3、网络与交互
• 100 M 以太网
• 0.96″ OLED(状态提示) + 蜂鸣器 + 红绿指示灯
4 软件与算法栈
4.1 系统镜像
• 基于 Buildroot 官方 SDK V2,支持 ARM64 与 RISC-V 双启动,SD 卡或 eMMC 启动
4.2 Paho-MQTT客户端移植
4.2.1 获取 MQTTClient 源码
可以从 GitHub 克隆 Paho MQTT C 客户端库,它包含 MQTTClient 实现:
git clone https://github.com/eclipse/paho.mqtt.c.git
cd paho.mqtt.c
git checkout v1.3.8 # 使用稳定版本
4.2.2 编译安装
根据 Milkv-Duo S 的架构(RISC-V),设置交叉编译工具链:
export CC=/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc
export CXX=/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-g++
export AR=/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-ar
export STRIP=/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-strip
配置和编译。
mkdir build
cd build
cmake -DPAHO_WITH_SSL=OFF -DPAHO_BUILD_STATIC=ON \
-DPAHO_BUILD_SHARED=OFF -DPAHO_BUILD_SAMPLES=ON \
-DCMAKE_INSTALL_PREFIX=./install ..
make
make install
移植到 Milkv-Duo S,将编译生成的库文件和示例程序复制到 Milkv-Duo S:
$ scp -r install/ root@192.168.101.142:/usr/local/
4.2.3 简单使用示例
发布者示例 (publisher.c)
include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#define ADDRESS "tcp://192.168.101.100:1883"
#define CLIENTID "MilkvDuoPublisher"
#define TOPIC "test"
#define QOS 1
#define TIMEOUT 10000L
#define USERNAME "admin"
#define PASSWORD "password"
int main(int argc, char* argv[])
{
MQTTClient client;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.username = USERNAME;
conn_opts.password = PASSWORD;
int rc;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
printf("Failed to connect, return code %d\n", rc);
printf("Error: %s\n", MQTTClient_strerror(rc));
return -1;
}
const char* message = "Secure message from Milkv-Duo S";
MQTTClient_deliveryToken token;
rc = MQTTClient_publish(client, TOPIC, strlen(message),
message, QOS, 0, &token);
if (rc != MQTTCLIENT_SUCCESS)
{
printf("Failed to publish message, return code %d\n", rc);
}
else
{
printf("Message published successfully, delivery token %d\n", token);
MQTTClient_waitForCompletion(client, token, TIMEOUT);
}
MQTTClient_disconnect(client, TIMEOUT);
MQTTClient_destroy(&client);
return rc;
}
订阅者示例 (subscriber.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"
#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883"
#define CLIENTID "MilkvDuoSsubscriber "
#define TOPIC "test"
#define QOS 1
#define TIMEOUT 10000L
#define USERNAME "admin"
#define PASSWORD "password"
volatile MQTTClient_deliveryToken deliveredtoken;
void delivered(void *context, MQTTClient_deliveryToken dt) {
printf("Message with token value %d delivery confirmed\n", dt);
deliveredtoken = dt;
}
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
printf("Message arrived\n");
printf(" topic: %s\n", topicName);
printf(" message: %.*s\n", message->payloadlen, (char*)message->payload);
MQTTClient_free(topicName);
MQTTClient_freeMessage(&message);
return 1;
}
void connlost(void *context, char *cause) {
printf("\nConnection lost\n");
printf(" cause: %s\n", cause);
}
int main(int argc, char* argv[]) {
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
int rc;
int ch;
if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS) {
printf("Failed to create client, return code %d\n", rc);
exit(EXIT_FAILURE);
}
MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.username = USERNAME;
conn_opts.password = PASSWORD;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
printf("Failed to connect, return code %d\n", rc);
MQTTClient_destroy(&client);
exit(EXIT_FAILURE);
}
printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n", TOPIC, CLIENTID, QOS);
if ((rc = MQTTClient_subscribe(client, TOPIC, QOS)) != MQTTCLIENT_SUCCESS) {
printf("Failed to subscribe, return code %d\n", rc);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
exit(EXIT_FAILURE);
}
printf("Press Q<Enter> to quit\n\n");
do {
ch = getchar();
} while(ch!='Q' && ch != 'q');
MQTTClient_unsubscribe(client, TOPIC);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return EXIT_SUCCESS;
}
4.3 移动物体探测
• 多线程流水线:Camera → 移动侦测 → 报警逻辑
4.3.1 移动物体探测程序编译
进入到移动物体探测源码目录,使用 make 命令编译:
$ make

4.3.2 移动物体探测部署
1、将TDL SDK生成的移动物体探测程序上传到DuoS中
通过网络直接将主机编译的程序下载到 Duo S开发板中。
# tftp -g -l monitor -r monitor 192.168.101.14
当然也可通过scp在主机端将文件发送到开发板。
$ scp monitor root@192.168.101.14:/root/
2、在DuoS终端运行移动物体探测程序:
可能没有执行权限,需要先在开发板的系统里通过 chmod 命令添加可执行权限:
# chmod +x monitor
./monitor 30 10240
需要传入两个参数:
- 参数一:运动检测阈值,范围为[0-255]
- 参数二:最小检测区域(像素数)
Duo S的终端会显示如下信息。

3、演示实例
在连接DuoS的PC端,打开VLC播放器,菜单“媒体”中选择“打开网络串流”,选择“网络”标签,在“请输入网络URL”中输入。
rtsp://192.168.101.6/h264
当画面变化,则会框选出变化的画面。

5 性能与可靠性
• 工作温度:-10 ℃ ~ 55 ℃,无风扇散热片即可
• 掉电保护:超级电容保证 50 ms 数据 flush,避免库损坏
• 日志:/var/log/access.log 按日滚动,支持远程 syslog
6 系统演示
打开监控设备。


Web查看监控视频。



可截图操作。

当画面变化,会推送到企业微信群。
