ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

microduck 的 RK3566 NPU 移植实录:把鸭鸭检测器放进 0.8 TOPS 的 INT8 NPU

microduck 的 RK3566 NPU 移植实录:把鸭鸭检测器放进 0.8 TOPS 的 INT8 NPU 机器人嵌入式强化学习人工智能智能硬件计算机视觉音视频【免费下载链接】microduckA Tiny biped duck robot 项目地址https://gitcode.com/gh_mirrors/mi/microduck点击查看免费下载本文是 microduck一款 Tiny biped duck robot 在 RK3566 上启用 NPU 并运行鸭鸭检测器的完整技术记录模型从哪个 Hub 仓库来、如何随更新自动安装、setup-npu.sh如何打开被 Armbian 默认禁用的设备树节点、duck-bench如何在真机上回答能不能跑、还认不认鸭子、要花多少代价三个问题以及检测器距离成为机器人行为输入还差哪几步。读完你可以在一台 Radxa Zero 3 上从零复现整套 NPU 推理链路并理解mediad的帧捕获media.frame与快照接口的用法。背景RK3566 上的 0.8 TOPS 单核 INT8 NPURK3566 自带一个很小的 NPU——0.8 TOPS、单核、INT8。本文记录的就是把训练好的鸭鸭检测器放到这颗 NPU 上的过程跑什么、预期什么、在一个行为behaviour能真正用到它之前还缺什么。模型在duck_detector训练仓库中训练以量化后的.rknn格式进入本仓库。第一个参考模型是yolo11n320×320 输入、单类别来自三个 session 共 150 帧数据在留出的一个 session 上mAP50 达到 0.976INT8 量化后只有3.9 MB并且在桌面上对照 float 模型2/2 个检测在 95% 框重叠box overlap下全部保留。模型从哪里来和策略通道同一条 Hub 管线模型权重不放在本仓库里。duck_detector训练仓库把每次运行都发布到 Hugging Face 上的pollen-robotics/microduck-duck-detector模型仓库——NPU 用的duck_detect.rknn和 CPU 兜底用的duck_detect.onnx以固定文件名放在仓库根目录每次运行一个 tag。mediad从/opt/robot/detector/current读取检测器而填充这个目录的机制是工具作用scripts/seed-detector.sh由发布版本的 postinstall 钩子运行在什么都没有的板子上安装[workspace.metadata.detector]中固定的 pin且绝不触碰不是它安装的那套robotctl duck-detector check对比板上装了什么与仓库当前提供什么sudo robotctl duck-detector update [--version tag]安装某个版本并重启mediad使用它这套机制和seed-policies.shrobotctl policy check/update同构——只是根目录不同、文件列表固定背后同样是updaterd的detector.check/detector.install调用。其中的推理在 docs/design/policy-channel-design.md §9Leaving the artifact中有完整阐述可直接照搬pin 是下限floor而不是上限、任何半成品都不上线、一次重训就是一个 tag 而不是一次 daemon 发布。有两个值得知道的细节模型仓库与数据集仓库同名。机器人永远只寻址模型仓库…/resolve/rev/…、api/models/…而数据集位于datasets/前缀下所以机器人在线路上不可能意外抓到一帧数据集。update的最新由版本 tag 判定v2排在v1之上像experimental这种名字永远不算数。所以打算给机器人用的运行需要打vN这样的 tag——首个运行被打成duck-v1pin 就指名这个 tag按名字安装没问题但它不是一个check能排序的版本。对应地pin 记录在 Cargo.toml 的[workspace.metadata.detector]repo pollen-robotics/microduck-duck-detector、version duck-v1seed-detector.sh内部携带同样的字面量并由一个 xtask 测试断言两者一致——因为脚本从发布包内部运行读不到工作区清单。seed-detector.sh 的安装规则scripts/seed-detector.sh 是 scripts/seed-policies.sh 的固定文件列表变体关键行为两个文件总是都下载.onnx是给设备树里 NPU 被关掉的板子的 CPU 兜底这正是 Armbian 出厂的 Radxa Zero 3 的样子。先全部落到 staging 再原子切换releases/seed-version/就位后current以相对符号链接方式被换掉半截下载永远不会上线。已装即不动current指向任何非seed-*目录说明是别人放的检测器脚本直接退出——否则下一次无关的 daemon 更新会悄悄把别人选好的版本换回去。每个下载限时 20 秒--max-time 20且从不致命检测器默认关闭没模型的机器人只是在mediad的日志里说一声而不是让一次更新失败。每套文件旁写一个.source记录repo/version/fetched 时间供robotctl duck-detector check读取它该问哪个仓库。仓库里有什么组成部分内容duck-detectcrateletterbox、运行时绑定、decode——外加duck-bench基准工具scripts/setup-npu.sh启用 NPU 设备树节点、安装librknnrt.so并报告驱动状态读这两个之前需要知道两个贯穿全局的决策决策一dlopen而不是链接librknnrt.so是不在任何 Debian 套件里的厂商 blob。一个链接它的 crate 将无法在 CI 里交叉编译。robotd访问 ONNX Runtime 用的也是同一招。代价就是 duck-detect/src/rknn.rs 这份文件收益是cargo board --bins在一台笔记本上依然能工作。实现上duck-detect/src/rknn.rs 用libloading按顺序尝试四个候选路径CANDIDATES从librknnrt.so到/usr/local/lib/librknnrt.so见 #L28-L33再按名字查找七个 C 符号rknn_init、rknn_destroy、rknn_query、rknn_inputs_set、rknn_run、rknn_outputs_get、rknn_outputs_release。rknn_query返回的是布局即 ABI 的定长结构体任何字段摆错都是静默的胡言乱语而不是报错——所以 rknn.rs 的测试 在启动时断言每个结构体的尺寸和关键字段偏移如RknnTensorAttr总长 376 字节、dims偏移 8、scale偏移 356作为对 blob 边界的手写护栏。决策二反量化交给运行时量化模型的输出张量是 int8带一个 scale 和一个零点。rknn_outputs_get在请求时会转成 float而这里确实请求了want_float: 1——替代方案是把 scale 带进解码器然后错一次悄悄地错见 duck-detect/src/rknn.rs 中infer的实现。检测器本身从帧到框的三件事duck-detect/src/lib.rs 是相机帧 → 边界框之间的三件事letterboxletterbox_rgb#L65-L97按比例缩放进 320×320 方形114 灰填充最近邻采样——因为它每帧都跑在 50 Hz 控制循环旁边而双线性要多花三倍代价去把框移动一个像素。Letterbox记录 scale 和 paddecode 时再映射回原帧坐标。运行时绑定rknn::ModelNPU与onnx::ModelCPU 兜底duck-detect/src/onnx.rs。decode#L105-L145YOLO 单张量头输出 2100 个候选框头部不做任何抑制——一只鸭子会回来二十个重叠框。decode按阈值过滤、按 IoU 做 NMS再解除 letterbox 映射回原始帧。注意头部张量是[1, 5, N]planar 布局所有 cx、然后所有 cy……按交错读会得到几乎合理的框——这是最糟的错法测试 the_head_is_planar_not_interleaved 专门钉死这一点。此外Detection::bearing()#L54-L57把框中心归一成 -1最左到 1最右转向它只需要这一个数——这正是行为侧真正想要的。打开 NPUsetup-npu.sh 实战驱动是闸门它是厂商内核的一部分主线内核没有用户空间没有任何东西能绕过它的缺失。一次普通的robotctl update就干了这件事hooks/preinstall.in里的install_npu会运行发布包自带的 scripts/setup-npu.sh 副本与setup-gstreamer.sh、setup-rkaiq.sh并列见 hooks/preinstall.in 中 #L194-L209从不致命报告写进更新日志。所以一台在 NPU 出现之前就已交付的板子会被一次普通更新修好而不是等某个人记得敲命令。手动运行只是重试sudo sh /opt/robot/daemon/current/scripts/setup-npu.sh注意第一次携带它的更新会要求重启。Armbian 在每一台 Radxa Zero 3 上把npufde40000配成status disabled——所以一台出厂板子有硬件、有内核、有驱动却仍然没有 NPU。脚本会写入修复它的 overlay 并明确说出来节点在下一次启动时才绑定。--no-enable-node只装运行时之后用dmesg | grep rknpu确认。运行发布副本而不是/usr/local/sbin/robot-setup-npuoverlay 的 .dts 源码就在脚本旁边而留在/usr/local/sbin的副本在第一次运行时旁边什么都没有。脚本做了什么启用节点默认开启ENABLE_NODE1setup-npu.sh #L47把 deploy/overlays/rk3568-npu-enable.dts 用dtc编译成.dtbo装进/boot/dtb/rockchip/overlay/并把npu-enable追加到/boot/armbianEnv.txt的overlays列表前一份文件保留为.before-npu。这个 overlay 只改一个属性——status okay节点上 clocks/resets/power-domains/IOMMU 全都已经在基座里了出错模式是驱动不 probe而那正是未动过的板子原本的状态见 rk3568-npu-enable.dts 头部注释。撤销只需从overlays里删掉这个词。安装运行时从 Rockchiprknn-toolkit2仓库的固定 tag默认v2.3.2与 Cargo.toml 的[workspace.metadata.rknpu]一致脚本带字面量、测试断言两者一致直接下载librknnrt.so到/usr/lib/写版本戳/usr/lib/librknnrt.version跑ldconfig。下载物会先做ELF magic 检查7f454c46和大小检查1 MB——截断的下载会在第一次推理时 segfault而不是报错。幂等只在缺失或版本不同时下载。报告驱动从/sys/kernel/debug/rknpu/version、/proc/rknpu/version或 dmesg 读取驱动版本。参数一览参数作用--runtime TAG从哪个 rknn-toolkit2 tag 取librknnrt.so。必须至少和转换模型的 toolkit 一样新比模型老的运行时会在rknn_init时用一个数字失败没有任何解释--no-enable-node不动设备树。默认是启用——因为默认值装在 Armbian 出厂的每一台 Zero 3 上都能用。下次启动生效脚本从不自己重启--enable-node仍被接受现在只是把默认行为说出口--help用法失败时错误信息会点名设备树duck-detect/src/rknn.rs 的why_init_failed#L161-L178按真实发生顺序排查rknn_init失败先读/proc/device-tree/npufde40000/status若是disabled就告诉你去跑setup-npu.sh若节点根本不存在说明内核不是带 rknpu 驱动的 Armbian 厂商内核只有节点已启用时才轮到怀疑模型平台或驱动太老。运行时的原始日志failed to open rknpu module, need to insmod rknpu dirver!会把排查引向一个其实已经编进内核的模块。跑基准duck-bench从你机器上的克隆仓库开始cargo board --bins -p duck-detect scp target/aarch64-unknown-linux-gnu/release/duck-bench microduckrobot:/var/tmp/ scp the.rknn microduckrobot:/var/tmp/duck.rknn scp -r datasets/raw/a-session microduckrobot:/var/tmp/framesduck-bench不在发布包里它是测量工具打包它等于为两个人的便利把它装到每一台机器人上。它靠scp过去直到出现真正需要检测器的行为——届时随发布上线的是mediad里的检测器而不是这个。/var/tmp/duck-bench --model /var/tmp/duck.rknn --frames /var/tmp/frames它按重要程度依次回答三个问题见 duck-detect/src/bin/duck-bench.rs 头部能跑吗加载不了的运行时、为别的平台转换的模型、比运行时老的驱动都会在这里失败而不是在 daemon 内部失败。还认鸭子吗它报告每帧的检测数——因为能跑但什么都检测不到的模型和正常工作的模型看起来一模一样。量化正是检测器最容易在这里悄悄死掉的地方。代价是多少延迟分位数以及本进程烧掉的 CPU——用 NPU 的理由是别去动robotd的 50 Hz 循环而这个说法值得测量。参数一览见 duck-bench.rs #L29-L65参数默认说明--model—量化后的.rknn--frames—JPEG 目录一次采集 session 即可它读 JPEG 而不是开相机因为相机在mediad手里--warmup5计时前跳过这么多次让首调代价不进答案--passes3整套帧跑几遍小 session 上拿到稳定的分位数--threshold0.35检测阈值见下文--hz2.0每秒推理次数。默认就限速这不是客气全速跑会把 Radxa Zero 3 推到 95 °C、CPU 被节流到 408 MHz——全速跑出来的数字是已经太热、给不出数字的板子的数字。--hz 0去掉限速用于在有风扇/散热片的板子上找天花板--verbose—每帧一行找行为异常的帧另外两个测量细节CPU 时间从/proc/self/stat的 utimestime 算属于本进程而非板子上别的东西SoC 温度从thermal_zone里找soc-thermal读。--threshold是最先该动的手。量化模型的分数在它们自己的尺度上——float 模型的 0.5 不是这个模型的 0.5。一个什么都检测不到的运行更可能是阈值问题而不是转换坏了。先试0.2再信最坏的情况。若仍然全空duck-bench 会直接打印NOTHING DETECTED并提醒你阈值而非模型。数字来自一台 Radxa Zero 3duck-bench以限速 2 Hz 跑 30 帧 × 3 遍项实测备注驱动 / 运行时0.9.8 / 2.3.2setup-npu.sh会打印两者延迟 p50 / p9525.7 ms / 58.4 ms推理 decode不含 JPEG 解码每帧 CPU20.7 ms见下文——这不全是推理检测数对照的是人已经标好的帧SoC 温度63 °C限速一轮跑完时CPU 这个数字不是 NPU 的代价而它的呈现方式很容易让人读成代价。延迟列计时的是inferdecodeCPU 列则是整个循环的进程 CPU 除以帧数所以它还背着letterbox_rgb——一次 1280×720 → 320×320 的 CPU 重采样根本不在延迟里。剩下的差额是否意味着rknn_run在忙等把 NPU 等待记到 CPU 头上目前未知。无论哪种2 Hz 下它都是单核的 4%在任何人把这个数引用为感知的代价之前这两件事应该被分开测量。还缺什么从帧到状态mediad有一条原始 tee 分支它的存在就是为了这件事——见 docs/design/architecture.md §5.3。两条前进的路且不互斥media.frame—— 已完成。一个答一帧的调用走mediad自己的 unix socket/run/mediad/media.sock和其他观测 socket 一样组可读。它的用处远不止感知控制台里的一张快照、bug 报告里的一张静帧也意味着采集数据集不再需要停掉mediad才能拿相机。协议是先是一个 JSON-RPC 头、指明字节数然后就是那些字节——一帧原始帧约 1.8 MiB这不是能 base64 塞进一条 control 回复的东西。它向 tee 要下一帧而不是取缓存帧所以读者不会被塞给停了的相机停下的那一帧。mediad里的检测器订阅原始分支以几 Hz 跑模型把检测结果发布到状态流。这就是归宿——感知挨着传感器派生特征而不是搬运像素——也是行为会消费的东西。一旦检测成为状态docs/ideas/autonomous_behavior.md 里那些目前以蓝牙为键的行为一只鸭子在附近就可以改为以视觉为键一只鸭子在那里靠近、跟随、面向以及鸭子们唱歌时互相看着的合唱。检测器在 mediad 里的实现形态mediad/src/detect.rs 已经把第二条路铺好了大半一个线程而不是一个 task推理是每帧 60 ms 的阻塞工作而 tokio 运行时在服务 WebRTC 信令——让一个检测器每 0.1 秒占用一个 workersession 建立会无缘无故地卡顿。限速 2 Hz 是一个热力学数字全速在 Radxa Zero 3 上到 95 °C、CPU 节流到 408 MHz——看得清但走得烂的机器人。那边有没有一只鸭子2 Hz 绰绰有余代价约十分之一核。后端由模型扩展名决定.rknn→ NPU.onnx→ CPU。spawn_first#L118-L148按列表逐个试失败者warn后尝试下一个全失败才拒绝——因为回落到 CPU是值得在日志里看到的决定那是 60 ms 和别人家的 60 ms的区别。每次观测以Sighting { width, height, found, took_ms }通过容量 8 的 broadcast 发布控制台侧收到media.detections通知框在帧自己的像素里尺寸随帧一起给页面缩放不需要知道相机任何事video_notification用media.video把rotate告诉 WebRTC 对端。每 20 次观测2 Hz 下是 10 秒在info级输出一次心跳looks/seen/starved/took_ms——检测器还活着吗不该需要浏览器回答。starved专门区分线程死了 / tee 安静了 / 房间里没有鸭子三种从外部无法区分的情况。开关与参数都在 deploy/robotd.toml 的[duck_detector]段#L350-L380——这个文件是robotctl configure编辑的文件机器人只有一处放设置的地方[duck_detector] # 默认关闭没人让机器人看鸭子时不该每半秒付 ~60 ms 的代价 # enabled true # 用哪个模型也就决定了用哪颗处理器.rknn 跑 NPU.onnx 跑 CPU。 # 不设则用已装的那套——/opt/robot/detector/current/duck_detect.rknn 优先 # 设备树里 NPU 被关掉的板子Armbian 出厂的 Radxa Zero 3回落 duck_detect.onnx。 # model /home/microduck/my_detector.rknn # 每秒看几次。2 是热力学极限而不是偏好全速在 Radxa Zero 3 上到 95 °C、 # CPU 节流到 408 MHz # hz 2.0 # 置信阈值在这个模型自己的尺度上。量化后分数不是概率 # 真检测读约 1.3其余什么都不读——输出张量和框坐标共享同一个量化 scale。 # 所以这是个有/无阈值而不是一个旋钮 # threshold 0.35拍一张快照机器人上robotctl frame在机器人上执行robotctl frame --output frame.uyvy保存一帧新鲜的打包 UYVY 帧并把 JSON 元数据打印到 stderrwidth、height、bytes、采集时间戳、rotate——rotate是相机相对直立安装位置顺时针旋转的度数和media.video告诉 WebRTC 对端的那个数一致。转换时用这些尺寸并施加这个旋转例如一台 90° 安装的相机拍的 1280×720 帧ffmpeg -f rawvideo -pixel_format uyvy422 -video_size 1280x720 -i frame.uyvy \ -frames:v 1 -vf transpose1 frame.png改了相机模式或安装后两个数都别猜。文件只在完整响应到达后写入。像素就是传感器交付的像素rotate是报告出来而不是施加的——pipeline 不再旋转帧因为videoflip让编码器丢了零拷贝路径、板子掉了 22 fps详见 docs/project/media-bringup.md 的 The rotation that cost 22 fps 一节所以每个消费者自己转。上面省略-vf图片就是横的而且没有任何东西说明为什么——这正是rotate存在要防的事。当--flip-in-pipeline已经在 pipeline 里转过时它是0。浏览器上HTTP 路由在机器人的 LAN 里浏览器打开http://robot:8080/frame或保存curl --fail http://robot:8080/frame -o frame.png返回正立的 PNG带Cache-Control: no-store。这条路由是上段规则的例外——PNG 没有地方携带角度四分之一圈的安装会让宽高和采集几何对调代价是每请求转一次而不是每帧转一次。相机停止或不可用时返回HTTP 503绝不会给最后一张好照片。PNG 保留了 RGB 转换而没有 JPEG 压缩它不是原始 UYVY 数据的逐字节替代。这条路由和现有控制台、相机流有相同的 LAN 访问边界没有额外认证。协议与限制本地端点支持先hello然后在同一连接上media.frame。socket 可用mediad --frame-socket path配置对应robotctl --media-socket path frame。抢占 socket 失败会中止启动已有的文件或活着的监听者会被原样留下。限制一览限制值本地连接数最多 16 个每个限 5 秒HTTP 快照任务最多 4 个并发HTTP 捕获截止3 秒响应元数据上限 4 KiB载荷 / 几何超过 16 MiB 或非法几何两个客户端都拒绝media.frame刻意没有Call/service-lane 路由它的 JSON 头后面跟着二进制尾巴。WebRTC 控制 datachannel 和duckctl当前的 BLE 传输都不携带快照请用本地 socket 或控制台的 HTTP 路由远程视频传输是另一回事。未来一个共享的 socket-group 辅助函数可以替换现有的重复属主代码而不必把这个功能耦合进一次多 daemon 重构。适用前提与边界以上数字与行为全部以当前仓库为据并注意两点前提基准数字来自Radxa Zero 3 驱动 0.9.8 / 运行时 2.3.2的组合换板、换内核或换运行时后应重测NPU 节点默认被 Armbian 出厂设备树禁用所有 NPU 推理都要先过setup-npu.sh加一次重启这道闸。检测器进mediad的最终形态在状态流上发布检测在仓库中已具备全部零件——原始 tee 分支、Sightingbroadcast、media.detections通知——只差把订阅端的行为消费逻辑接起来。赞分享机器人嵌入式强化学习人工智能智能硬件计算机视觉音视频【免费下载链接】microduckA Tiny biped duck robot 项目地址https://gitcode.com/gh_mirrors/mi/microduck点击查看免费下载相关推荐Microduck视觉感知详解在0.8 TOPS的RK3566 NPU上运行INT8量化鸭子检测模型Microduck视觉感知详解在0.8 TOPS的RK3566 NPU上运行INT8量化鸭子检测模型 Microduck 的视觉感知系统让这只迷你双足鸭形机器机器人嵌入式强化学习人工智能智能硬件计算机视觉音视频microduck 模拟器实战用 MuJoCo 里的虚拟鸭驱动真实守护进程与策略microduck 模拟器实战用 MuJoCo 里的虚拟鸭驱动真实守护进程与策略 本文是一份面向开发者的实操指南在 microduck 开源仓库中如何通过机器人嵌入式强化学习人工智能智能硬件计算机视觉音视频vLLM-Omni 昇腾 NPU 移植指南GPU 到 NPU 代码翻译模式全解析vLLM Omni 昇腾 NPU 移植指南GPU 到 NPU 代码翻译模式全解析 本文以 vLLM Omni 的 NPU昇腾 Ascend移植工作为背景人工智能大模型模型推理服务多模态语音音频媒体生成本地部署上一篇快速检查quickcheck Testable trait原理剖析属性测试的基石下一篇终极指南1500 AI代理技能库如何彻底改变你的开发工作流创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表