Microduck-HD1910嵌入式AI开发实战:硬件调试、模型部署与软件实现

发布时间:2026/9/26 1:52:01
Microduck-HD1910嵌入式AI开发实战:硬件调试、模型部署与软件实现 Microduck-HD1910这块开发板我拿到手整整折腾了两个月中间推翻了三版方案最后总算是把模型部署、硬件调试、软件开发三个环节完整跑通。今天把这套手把手流程原样整理出来希望能让后面入坑这块板子的人少走点弯路。内容不挑基础哪怕你是第一次碰嵌入式AI只要跟着顺序来也能把这套链路自己搭出来。1. 项目整体拆解第一步不是写代码而是确认板子定位1.1 Microduck-HD1910是什么适合做什么Microduck-HD1910本质上是一块面向边缘计算场景的开发板板载一颗四核Cortex-A55级别的应用处理器主频在1.8GHz左右同时集成了算力约2TOPS的NPU。内存版本一般有2GB和4GB LPDDR4X两种存储用eMMC加TF卡双通道。板上的接口很齐全双路MIPI-CSI摄像头接口、千兆网口、USB 3.0、HDMI输出、40pin GPIO排针调试串口也是标配。单板功耗满载大概能压在8W以内所以很多做智能IPC、工业质检、农业巡检、无人零售的项目都拿它做原型验证。这块板子最核心的价值其实不是CPU多强而是那个2TOPS的NPU。说得直白一点CPU部分相当于中端手机芯片的定位但NPU可以在8W功耗下跑一些轻量级目标检测模型比如YOLOv8n、YOLOv5s、MobileNet系列帧率能做到30帧以上。我做的是一个小型视觉检测终端需要在本地完成人形识别、轮毂瑕疵检测和结果上报正好是它的典型场景。如果你也是要做类似的边缘视觉应用这个开发路线就非常对味。1.2 开发路线为什么是“硬件调试→模型部署→软件开发”一开始我按照做传统嵌入式项目的习惯先把应用层代码写了个大概然后才去部署模型结果踩了一路坑。后来重新梳理流程我建议所有新手都按这个顺序来先把硬件环境完全跑通再部署模型最后做应用软件开发。原因其实很朴素。模型部署依赖系统正常运行比如摄像头驱动、NPU驱动、内存带宽、电源稳定性。如果硬件上有问题比如供电纹波大导致NPU推理时反复重启你大概率会误判成“模型转换出错”或者“代码有bug”排查起来非常痛苦。反过来先把串口、GPIO、I2C、摄像头、NPU驱动都验证一遍再跑模型出问题时定位范围会小很多。软件开发放最后也不是为了拖节奏而是因为应用层要和推理结果做联动如果模型没跑通画框、上报、存储这些功能都只是空中楼阁。还有一个现实的好处硬件调试阶段会把很多环境依赖一次性装好比如交叉编译工具链、Python环境、系统服务配置这些后续都会被软件开发复用。顺序对了整个项目推进效率至少提升一倍。2. 硬件调试把板子当成一个裸金属系统去对待2.1 上电前的三查电源、串口线、Boot拨码很多人拿到开发板第一件事就是插电开机然后发现起不来或者串口乱码才开始回头找原因。我现在的习惯是上电前检查三样东西。第一是电源。Microduck-HD1910的电源接口一般支持DC 12V输入建议至少配12V/2A的适配器。千万别用树莓派的5V/3A电源去顶板上虽然有板载电源管理但输入电压范围通常不允许偏差太大强行插可能直接烧掉DC-DC模块。如果你手头有可调电源或电流表上电时串进去看一眼电流正常系统起来后电流会稳定在400到800mA之间如果一上电就超过1A并且持续拉高赶紧断电查短路。第二是串口线。板上调试串口一般是3.3V TTL电平用USB转TTL模块连接时模块的TX接板子的RX模块的RX接板子的TXGND接GND。我见过太多人把TX和RX接反然后抱怨“为什么串口没输出”。另外板上那个VCC引脚通常不用接因为板子自己供电你再去接反而可能造成电平打架。波特率方面默认固件有的是115200有的是1500000可以在启动日志或SDK文档里确认不确定就先试115200。第三是Boot拨码或启动按键。HD1910一般支持eMMC启动、TF卡启动、MaskRom烧录模式三种拨码组合在说明书里都有表格。第一次使用我建议先确认拨码在eMMC启动位置因为出厂系统就烧在eMMC里避免你明明插了TF卡却从eMMC启动导致系统行为异常。做系统开发后这块拨码要经常切换建议写张便签贴在板子上。上电后通过串口登录用户名和密码在SDK的README里有一般默认是root / microduck。登录成功后先看内核日志uname -a cat /proc/cpuinfo free -h dmesg | grep -i microduck\|npu\|sensor如果dmesg里能看到npu初始化和摄像头sensor的probe信息说明硬件底层基本没问题。2.2 跑通系统并验证基础外设系统能登录只是开始我会花半小时把所有外设轮询一遍相当于给板子做一次“体检”。GPIO验证最简单。找一个LED灯阳极接限流电阻后再接某个GPIO引脚阴极接地然后在开发板上用libgpiod操作gpiodetect gpioset -m keep 0 121如果灯亮说明GPIO输出没问题。输入的话可以接一个按键用gpioget读电平。然后是I2CHD1910上通常会引出两路I2C接一个常用的传感器模块比如温湿度传感器或OLED屏扫描总线地址i2cdetect -l i2cdetect -y 0能看到设备地址就是通的。如果一直在扫描超时检查上拉电阻和接线而不是先怀疑系统。摄像头验证是整个硬件调试验收的重点。安装摄像头模组时要先把板子断电然后接FPC排线锁紧卡扣。登录后查看设备节点v4l2-ctl --list-devices通常会出现/dev/video0和/dev/video1之类分别对应主摄像头和ISP处理通道。再用v4l2-ctl抓一张图到本地v4l2-ctl --device/dev/video0 --set-fmt-videowidth1920,height1080,pixelformatNV12 --stream-mmap --stream-toframe.raw --stream-count1然后用ffmpeg或python把raw转成jpg看看有没有画面。这一步能同时验证MIPI传输和ISP通路如果画面花屏、偏色或者全黑多半不是应用层问题而是sensor配置或摄像头模组没接好。2.3 硬件调试踩坑记录这一节我想把最值得记住的几个坑单独列出来。第一电源纹波问题。最开始我用一个杂牌12V适配器供电单独跑CPU任务完全正常但只要一跑NPU推理板子过几分钟就自动重启。后来用示波器测电源相位发现满载时纹波超过了800mVNPU和DDR在这种波动下产生了错误复位。解决办法是换了一个质量好的开关电源再在DC输入正负极并联一个220uF/16V电解电容问题解决。如果你没有示波器至少拿万用表在推理时测一下12V有没有明显跌落。第二串口乱码不一定是波特率问题。有次我设置了正确波特率但输出依然是乱码。后来发现是电脑USB转TTL模块供电不足导致TTL电平没达到3.3V阈值。换一个带独立供电的模块就好了。另外串口线别太长超过20cm就容易引入噪声。第三MIPI-CSI摄像头识别不到。排查顺序是先看dmesg里有没有sensor probe信息再看设备树里MIPI lanes和时钟频率是否和sensor匹配最后检查FPC排线方向。我那次就是排线插反了结果设备树和驱动都正常就是没图。把排线重新插拔一次铁片朝上问题秒消。第四温度降频问题。微集板没有配散热风扇时NPU满载跑3分钟核心温度会冲到85度以上然后NPU频率自动降档推理耗时从30毫秒涨到60毫秒。建议常备一个5V PWM风扇直接接在板上的风扇接口或者至少贴一块散热片配合导热垫。热设计这块在样机阶段最容易被忽略量产时更要注意。3. 模型部署把训练好的权重变成板子上能跑的推理程序3.1 模型选型与部署路径对于HD1910这类2TOPS算力的平台模型选型必须克制。我推荐从YOLOv8n或YOLOv5s入手这两个都是轻量级检测模型参数量在2到7兆之间INT8量化后延迟可以控制在30到60毫秒。如果你的任务不是目标检测而是分类那MobileNetV3-Small或EfficientNet-Lite0也很合适。模型部署路径我建议这样走PyTorch训练 - 导出ONNX - 板端推理引擎。很多人喜欢直接把PyTorch模型搬到板上然后装个PyTorch库去跑这种做法在嵌入式平台非常不明智PyTorch依赖太大、启动慢、推理效率低而且NPU基本无法直接调度。ONNX作为一个中间格式几乎所有推理引擎都支持后续想切换CPU、NPU或GPU只需要转换一次目标格式。如果你决定用CPU推理板上装onnxruntime就够了pip install onnxruntime但CPU跑YOLOv8n在四核A55上大概只能到10帧体验并不好。更好的选择是用板子的NPU转换出来的模型格式常见有.rknn、.nb、.kmodel等不同芯片后缀不同。HD1910的SDK里一般会带一个AI工具链可能是命令行也可能是图形界面流程都是“加载ONNX - 设置量化方式 - 用校准集做INT8量化 - 生成板端模型”。3.2 一步一步完成模型转换和板端推理先看PC端导出ONNX。yolov8n.pt可以在官方权重基础上直接转也可以用你微调过的权重。关键参数是固定输入尺寸和batchyolo export modelyolov8n.pt formatonnx imgsz640 batch1 opset12这里固定batch为1是因为很多NPU工具链不支持动态batch导出成动态shape到转换阶段会报错。opset选择12是为了兼容更多嵌入式桌面环境太高版本的opset在旧版工具链里可能无法解析。然后进入NPU转换阶段。以HD1910 SDK里的工具链为例命令行大概会长这样md-toolkit convert \ --input model/yolov8n.onnx \ --output model/yolov8n.mdnn \ --target hd1910 \ --quantize int8 \ --calib-dir ./calib_images如果你手头的SDK命令名不一样没关系关键参数永远是四个输入模型、输出模型、目标平台、量化校准目录。calib-dir里面放300张左右和你业务场景接近的图片不用带标签工具会统计每层激活值的分布范围从而计算出INT8的量化系数。这一步非常影响精度我用COCO图像做校准集在工业瑕疵检测上精度掉了8个点换成现场采集的瑕疵样本后掉点缩到2个点以内。转换完成后把生成的模型文件复制到板子推荐放在/opt/models/目录。接着在板上写推理脚本。CPU版本的完整流程可以这么写import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(/opt/models/yolov8n.onnx, providers[CPUExecutionProvider]) def preprocess(img): img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) img img.astype(np.float32) / 255.0 img img.transpose(2, 0, 1) return np.expand_dims(img, axis0) img cv2.imread(test.jpg) input_tensor preprocess(img) outputs sess.run(None, {images: input_tensor})[0] # outputs shape: (1, 84, 8400) 表示4个坐标 80个类别共8400个anchor注意这里{images: input_tensor}的输入名需要看导出ONNX时的输入节点名可以用onnxruntime的get_inputs()打印验证。后处理包括置信度过滤和NMS工程上可以直接复用ultralytics库里的后处理逻辑或者自己实现一个简单的非极大值抑制。如果你用的是NPU模型SDK通常会提供Python推理封装比如加载模型后调用run_inference接口。接口内部已经处理好模型加载和输入输出内存管理你只需要把预处理后的数据传进去拿出来的就是类似原始ONNX的输出tensor后续后处理完全一致。3.3 板端性能调优不仅看帧率模型能跑通只是及格性能优化才是真正拉开差距的地方。我习惯先量化三个指标单次推理延迟、CPU占用率、NPU利用率。在板上写个小工具跑100次推理求平均import time import numpy as np tensor np.random.rand(1, 3, 640, 640).astype(np.float32) times [] for _ in range(100): t0 time.perf_counter() sess.run(None, {images: tensor}) times.append((time.perf_counter() - t0) * 1000) print(favg: {np.mean(times):.1f} ms)然后用top或htop观察CPU占用如果NPU模型推理时CPU占用依然偏高多半是预处理和后处理在CPU上耗时太多。这时候优先优化图像缩放和NMS。图像缩放用OpenCV的INTER_LINEAR足够但要注意把BGR转RGB和归一化合并成一次遍历避免反复读写内存。NMS尽量用numpy向量化实现不要写三层for循环。另一个关键优化点是避免图像数据在内存里反复拷贝。如果你用NPU的Python接口它可能要求输入数据是连续内存但你在前面做letterbox时已经生成了一个numpy数组再强行np.ascontiguousarray又复制一遍白白浪费几毫秒。最稳的做法是在预处理前就直接分配好一个固定缓冲区所有帧都写进同一块内存。再提一个容易被忽略的东西CPU调频策略。有些板子默认的governor是ondemand推理的时候CPU频率可能没有立刻拉满导致预处理变慢。可以用cpupower frequency-set -g performance把策略改成performance整体时延能低不少。功耗如果实在敏感也可以把四个核中两个绑到采集线程另外两个给推理线程用taskset指定CPU亲和性减少上下文切换。4. 软件开发把推理能力封装成稳定的边缘服务4.1 软件架构不要让主线程做推理模型调通之后下一步就是写真正的应用。这里最大的建议是不要让主线程同步去做推理。如果检测流程阻塞了摄像头读取画面会一卡一卡结果上报也会延误。我用的架构非常简单但很稳——《相机采集线程》把帧放入一个有界队列《推理线程》从队列取帧执行模型推理《结果处理线程》负责画框、上传统一管理。三个线程之间用Python的queue.Queue(maxsize5)来解耦队列满时直接丢弃最新帧保证整个系统永远都在处理最新画面而不是处理老帧。这样做有一个副作用系统整体帧率可能略有下降但实时性大幅提升。比如在串口日志里打印结果时你看到检测到目标的时间戳和画面实际出现时间相差不超过100ms这在安防和巡检场景非常关键。为了应对掉电、软件crash等问题我还会把应用封装成systemd服务。项目目录/opt/md_app/下放主程序和依赖写一个unit文件[Unit] DescriptionMicroduck-HD1910 Detection Service Afternetwork.target [Service] Typesimple WorkingDirectory/opt/md_app ExecStart/usr/bin/python3 /opt/md_app/main.py Restartalways RestartSec3 EnvironmentPYTHONUNBUFFERED1 [Install] WantedBymulti-user.target这样只要板子一开机服务就会自动拉起来进程意外退出三秒后自动重启。开发过程中我无数次体验到systemd的救命作用比自己在代码里写循环重试省心太多。4.2 摄像头采集和预处理要点摄像头读取我建议直接用OpenCV的cv2.VideoCapture做原型但正式项目最好用V4L2的mmap模式直接操作帧缓冲区避免OpenCV内部又做一次拷贝。HD1910的摄像头驱动会暴露/dev/video0采集NV12格式的数据然后转成BGR。如果是NPU推理很多SDK直接支持NV12输入反而不用转BGR还能省下不少CPU周期这一步在文档里通常有示例。预处理方面最关键的坑是letterbox与坐标映射。很多人直接把图片cv2.resize到640x640模型倒是能跑但检测框的坐标是相对于缩放后的图像的如果原图不是正方形框会偏。正确做法是先按长边缩放到640再用灰色填充短边然后记录缩放系数和填充偏移。推理完成后把输出坐标反向映射回原图scale min(640 / img_w, 640 / img_h) pad_x (640 - img_w * scale) / 2 pad_y (640 - img_h * scale) / 2 # 对每个检测框做还原 x1 (x1 * scale pad_x) # 注意x1、y1等是模型输出坐标 y1 (y1 * scale pad_y) x2 (x2 * scale pad_x) y2 (y2 * scale pad_y)不要在这些小细节上图省事不然你会看到检测框始终和物体对不上排查起来极其浪费时间。另外颜色通道顺序也要统一。模型是用RGB图像训练的但OpenCV默认读进来是BGR。如果你直接扔给模型识别准确率会明显下降尤其是带红绿颜色特征的场景。统一在预处理里做一次cv2.COLOR_BGR2RGB或者直接用SDK中支持NV12输入的接口用硬件转换来规避这个问题。4.3 业务联动本地显示、告警推送与数据上报模型输出的是原始检测框真正到业务层还需要做筛选和联动。我一般把置信度阈值设为0.45类别按业务需求过滤比如“人形”和“手机”是重点关注对象其他类别忽略。检测到目标后我会把目标框标注在视频帧上并通过RTSP推流出去方便后面接Web端或者手机端查看。数据上报我用MQTT因为它轻量、断线重连方便。发布检出事件的代码很简单import paho.mqtt.publish as publish payload { device: hd1910-001, timestamp: time.time(), class: detected_cls_name, bbox: [x1, y1, x2, y2], confidence: conf } publish.single( device/hd1910/events, json.dumps(payload), hostname192.168.1.50, port1883, qos1 )MQTT broker断了不会导致应用崩溃publish失败后面重试即可。如果是结构化数据量大一点的场景也可以换成HTTP REST POST但心跳保活、token刷新这些都要自己处理比MQTT重一些。除了上报本地还需要一个可观测的日志系统。我的做法是每次检测都写一行JSON日志到/var/log/md_app/detect.log再用logrotate做轮转防止日志撑满eMMC。调试阶段还可以通过串口打印关键指标但在正式运行时建议把打印频率降到1Hz以下否则日志本身会成为性能瓶颈。5. 常见问题与排查技巧实录5.1 模型转换失败和推理精度不对模型转换最典型的报错是“Unsupported operator”或“Shape mismatch”。前者说明ONNX里的某个算子板端工具链不支持解决思路是简化模型结构比如把Resize模式从bilinear改成nearest或者更新到最新SDK工具链后者一般源于输入shape不固定重新导出ONNX时固定batch1和固定尺寸就能解决。精度掉点的问题我的排查顺序是先检查预处理是否和训练时一致然后检查量化校准集是否和业务场景匹配最后再怀疑模型结构。曾经有一次我用PIL读图RGB顺序是好的但忘了归一化到[0,1]结果检测率掉了九成。这种问题用Python脚本快速对比PC和板端的输出分布一分钟就能定位。5.2 板子崩溃、死机、温度过高如果在推理过程中板子反复死机优先怀疑供电和散热。用cat /sys/class/thermal/thermal_zone0/temp看温度持续超过85度就需要加强物理散热。其次检查/var/log/syslog里有没有watchdog报错有些板载硬件看门狗会在系统卡死时自动复位但如果你把看门狗服务关了系统可能就挂住不动了。我后期都会开启systemd自带的看门狗配合硬件既保证异常重启又不至于误杀正常进程。还有一种比较隐蔽的死机原因内存不足。2GB内存在YOLOv8n Python OpenCV MQTT的叠加下内存占用很容易跑到1.8GB再贪心开多几个线程就可能触发OOM。解决方法是关掉不用的图形桌面服务改用轻量窗口库必要时把推理线程的后处理释放中间变量避免历史帧数组在内存里堆积。5.3 开发效率提升技巧最后分享几个实战中让我省下大量时间的小技巧。第一别每天抱着开发板跑强烈建议配置一套PC端交叉编译环境代码在PC上写好、交叉编译出ARM版本然后通过scp分发到板上执行。Python项目虽然不用编译但每次改完代码推到板上也可以用一条rsync命令完成包括模型文件、配置文件、依赖库全自动同步。第二SD卡系统不如eMMC稳。如果只是开发测试我用TF卡启动方便备份和尝鲜一旦准备长时间跑业务就把系统固化到eMMC减少接触不良和数据损坏的风险。每次刷写前先备份eMMC里的出厂镜像一出问题就能用dd恢复。第三在板上调试时多利用tmux。串口会话断开、网络超时tmux的分屏和会话保持功能能让你并行开着日志、监控、代码编辑器大幅减少重新登录、重新启动服务的重复劳动。我甚至会在系统启动脚本里自动拉起来一个tmux session开机就能看到全部应用日志排查问题快好几倍。这套Microduck-HD1910的开发流程我在另外两个项目里又原样复用了一遍从拿到板子到稳定跑出检测结果最快一次只花了一个星期。我个人的体会是硬件调试阶段的细致程度决定了后面所有环节是否顺利模型部署阶段一定要锚定“先CPU后NPU、先浮点后量化”的渐进策略软件开发阶段则要把稳定性和可观测性放在第一位。如果你也在用这块板子做类似的边缘AI产品照着这个顺序来剩下的就交给时间和耐心。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询