ARTICLE DETAIL

资讯详情

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

RK3588交叉编译hello:在香橙派5上为YOLOv5s部署铺路

RK3588交叉编译hello:在香橙派5上为YOLOv5s部署铺路 如果你手里有一块香橙派5这种基于RK3588的开发板又心心念念想在它上面跑YOLOv5s目标检测那大概率已经翻过不少教程了。翻来翻去你会发现大量资料一上来都在讲“先交叉编译一个hello程序”。看着这一行hello world很多人心里都会犯嘀咕我不是要跑AI模型么学这玩意儿干嘛这其实是整个嵌入式AI落地链条里最容易被低估的一环。RK3588是ARM64架构跟我们日常用的PC是两套完全不同的指令集你在电脑上编译出来的程序根本没法直接在板子上执行。想让YOLOv5s后续的C推理代码在香橙派上跑起来就必须先在PC上用交叉编译工具链生成ARM架构的可执行文件。这个“hello”练好了后面编译模型推理demo、跑RKNN接口全是同一套流程只是换了个工程而已。这篇文章就围绕这个第06期主题展开把交叉编译hello这件事从头到脚拆开讲为什么要这么做、工具链怎么装、常见错误怎么排查。我不玩虚的尽量把每一步的“为什么”也讲清楚你拿这篇文章当操作手册也好当避坑指南也罢都能少走不少弯路。1. 为什么交叉编译是RK3588部署YOLOv5s的第一课1.1 先梳理YOLOv5s部署全流程里交叉编译的位置很多人以为在RK3588上跑YOLOv5s就是把Python训练好的权重文件拷到板子上然后pip install一堆包就行了。真这样做过一次就会知道这条路在嵌入式设备上几乎走不通。RK3588虽然有6TOPS算力的NPU但要让模型真正跑到NPU上得先把PyTorch模型转换成RKNN格式再通过C/C的RKNN API写推理代码调用它。而这个推理程序本身是在PC上编译好再放到板子上跑的。这里就出现了一个关键矛盾PC是x86_64架构香橙派是aarch64架构两者不能直接通用。所以整个部署流程里凡是要生成可执行文件的操作几乎都依赖于交叉编译。rknn_model_zoo里的各种C/C例子、你自己写的yolov5s后处理代码全都要先过交叉编译这一关编出ARM版本才能在板端运行。这一期做hello本质上就是一次“工具链体检”。如果连一个hello程序都不能通过交叉编译跑到板上那后面编译带一堆依赖的工程代码时你会完全分不清问题是出在代码上、CMake配置上还是工具链上。先拿hello把编译、传输、运行这条链路打通后面所有工作都会顺很多。1.2 为什么不直接在板子上用gcc编译可能有人会说RK3588好歹是8核处理器直接ssh到板子上编译不就行了实测下来简单的程序确实能编但一碰上真实项目就会很难受。首先RK3588的性能跟一台普通PC还是有差距的尤其是I/O和散热。编译一个中等规模的C工程板子上可能要几分钟甚至十几分钟期间CPU一直满负荷功耗堆上来以后散热片都是烫手的。其次板子上的Linux系统很多精简过的gcc、make、各种依赖库的dev包未必齐全临时装一套构建环境既麻烦又占空间。更要命的是如果你后续要编译OpenCV、RKNN Toolkit依赖这类大体积库板载编译基本等于自虐。交叉编译的思路其实很好理解把“编译”这个又重又慢的活留在PC上干PC内存大、磁盘快、工具链想装什么装什么编出来的ARM可执行文件再传到板子上“跑”。打个比方你在一台工厂里排版印刷书籍书印刷出来后运到各个书店去卖而不是把整条印刷线搬到书店里。我们需要的只是印刷出来的“成品”也就是能直接被ARM平台执行的文件。2. 交叉编译原理与工具链选型2.1 交叉编译的三板斧到底指什么一套完整的交叉编译工具链其实包含三大部分编译器GCC、汇编器与链接器binutils、目标平台的C/C运行库典型的就是glibc。编译器负责把C代码翻译成汇编binutils里的as、ld负责把汇编变成机器指令并链接成可执行文件glibc则是程序运行时跟系统交互的基础库。一个新手最容易绕晕的点是为什么在x86_64电脑上装一个“aarch64-linux-gnu-gcc”编出来的程序就能发给ARM设备运行了这里的关键在于交叉编译指的是“生成的目标代码架构”和“运行编译工具的主机架构”不同。编译器程序本身运行在x86上但通过内置的target配置它最终生成的机器码是ARM64指令集。你不需要在家里摆一台ARM服务器来编译只要工具链选对了目标架构就对了。这也引出另一个重要问题目标板的运行库版本。交叉编译时链接用的是工具链自带的aarch64版glibc如果这个库的版本比板端系统里的glibc还要新程序跑到板子上就会报“GLIBC_xx not found”。所以挑工具链时不光看能不能编译还得留意版本匹配度。2.2 三条路线获取aarch64工具链在PC上拿一套能用的aarch64工具链主流有这么三条路路线典型来源优点缺点适合场景发行版软件源直接安装sudo apt install gcc-aarch64-linux-gnu安装简单、依赖自动处理版本较新可能比板端glibc高hello及中小型项目、学习入门瑞芯微SDK自带工具链SDK内部prebuilts/gcc目录下的aarch64-buildroot-linux-gnu版本与官方板端系统匹配度好需要先拉取完整SDK体积大正式项目、RKNN推理工程编译Buildroot自编译工具链自己用Buildroot配置生成版本完全可控可定制配置复杂、编译耗时长定制rootfs、量产固件场景新手阶段我建议直接用系统源里的gcc-aarch64-linux-gnu。它安装很省事装完就能用对hello这种小工程完全够。等真正开始编译RKNN相关的C项目了再切到官方SDK自带的工具链到时候你会发现很多官方demo的Makefile里都已经默认写好了SDK工具链的路径你只需要保证路径存在就行。2.3 动态编译与静态编译的选择差异工具链装好后编译时还有一个很重要的策略选择动态编译还是静态编译。默认情况下gcc是动态编译的生成的可执行文件很小但它依赖目标板系统里的glibc动态库。比如一个hello动态编译出来大概20KB左右放到板子上能不能跑取决于板端有没有对应版本的/lib/aarch64-linux-gnu/libc.so.6。大多数完整版Debian、Ubuntu系统都有所以一般也能跑。但如果你在PC上用了特别新的工具链板端系统又比较老一运行就报GLIBC版本不足。静态编译则是用-static参数把程序依赖的库函数全部打包进可执行文件里。这样编出来的hello体积会有700KB到1MB但好处是什么呢它基本不再依赖板端的任何库拷过去就能跑对系统版本几乎免疫。做hello阶段我特别建议用静态编译这样可以把变量因素降到最低只要工具链本身没问题这个文件在任何ARM64 Linux上都能运行。等后面编译调用RKNN API的程序时再切回动态编译因为RKNN的lib还需要链到板端的驱动库上。3. 环境准备与工具链安装实战3.1 编译主机的选择Win下用WSL还是搞虚拟机交叉编译第一步是要有一台能装Linux工具链的主机。如果你本身就是用Linux桌面那就省事了。如果主力机是Windows有两个常见选择WSL2和VMware虚拟机。我个人推荐WSL2理由很简单启动快、资源占用低、跟Windows文件系统互操作方便。安装WSL2后装个Ubuntu 22.04然后在里面装工具链流程跟在Linux实体机上完全一样。VMware也不是不行但每次启动要等完整开机流程共享文件得配VMware Tools交互起来没那么顺手。需要注意的一个小点WSL2里访问Windows盘文件走的是/mnt路径跨文件系统读写性能比较差。所以我建议整个编译工程都放在WSL的Linux文件系统里比如 ~/rk3588_work不要放到 /mnt/d 下面去编译否则大工程构建时会明显感觉到磁盘I/O拖慢。3.2 安装工具链并做首轮快速验证主机准备好后安装工具链其实就两条命令的事sudo apt update sudo apt install -y build-essential gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu如果你后续要编译C工程顺手把C交叉编译器和标准库也装上sudo apt install -y g-aarch64-linux-gnu装完先别急着写hello先确认编译器版本和默认目标架构aarch64-linux-gnu-gcc -v看到输出里有Target: aarch64-linux-gnu这一行说明装对了。接着写一个最简的验证程序并不需要真正“运行”只要确认它生成了ARM64的机器码echo int main(){return 0;} /tmp/test.c aarch64-linux-gnu-gcc /tmp/test.c -o /tmp/test file /tmp/testfile命令会告诉你这个可执行文件的真实架构。如果输出是ELF 64-bit LSB executable, ARM aarch64那说明工具链工作正常如果显示的是x86-64说明你大概率调用了本机gcc而不是交叉编译器。3.3 三个必会的ELF查看工具交叉编译后验证产物是否正常靠的就是这几个工具file最直观一条命令看架构、动态静态、调试信息等。aarch64-linux-gnu-readelf交叉工具链自带的readelf可查看ELF头、程序段、依赖的动态库。排查链接问题比file深入得多。aarch64-linux-gnu-objdump反汇编用的程序崩了或者想确认机器码指令时非常管用。强调一个小习惯平时在PC上看自己编的x86程序用readelf、objdump是用本机的现在编ARM程序最好养成用带aarch64-linux-gnu-前缀的习惯。如果你用本机的ldd去查看一个ARM可执行文件通常会报错或显示“not a dynamic executable”这不是程序有问题而是你用错了工具。想看ARM程序的动态库依赖要用aarch64-linux-gnu-readelf -d或者直接看板子上的ldd。4. 手把手把hello程序跑在香橙派上4.1 编写源码与Makefile先把源码建出来比如在~/rk3588_work/hello目录下新建hello.c#include stdio.h int main(void) { printf(Hello from RK3588!\n); return 0; }这段代码没有任何平台相关特性每条C编译器都能编。但也正因为它写起来毫无难度很多人会忽略Makefile的意义。在嵌入式里Makefile写得好不好直接影响后面移植工程的效率。我给出一个兼顾扩展性的MakefileCROSS_COMPILE ? aarch64-linux-gnu- CC : $(CROSS_COMPILE)gcc TARGET : hello SRCS : hello.c all: $(TARGET) $(TARGET): $(SRCS) $(CC) -o $ $^ -static clean: rm -f $(TARGET)这里的关键就在第一行CROSS_COMPILE ? aarch64-linux-gnu-。?的意思是如果用户在命令行里没指定这个变量就用后面的默认值。比如你后面切换到瑞芯微SDK自带的工具链时可以直接这样make CROSS_COMPILE../rk3588_sdk/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-不用改任何Makefile工具链就换了。这种设计在瑞芯微官方的很多demo工程里大量存在你现在把习惯养好后面会非常受用。4.2 编译、传输与板上运行在hello目录下执行make编译完成后先看一眼产物类型file hello应当输出类似hello: ELF 64-bit LSB executable, ARM aarch64, statically linked, not stripped的信息。看到statically linked就说明我们前面设置的-static参数生效了。接下来把文件传到香橙派上。如果你板子已经连着局域网并且开了SSH一条scp命令就行scp hello orangepi192.168.x.x:/home/orangepi/如果没有网络简单粗暴的办法是拷贝到U盘再插到板上。注意U盘挂载后通常默认不允许直接执行二进制文件需要拷贝到本地再运行比如sudo cp /media/usb0/hello /home/orangepi/ sudo chmod x /home/orangepi/hello cd /home/orangepi ./hello正常情况下你会看到终端打印出Hello from RK3588!。到这整条交叉编译链路就算正式跑通了。4.3 完整验证清单运行成功后我建议再花一分钟把整个链路自查一遍检查项命令预期结果编译器是否交叉编译file hello显示ARM aarch64架构是否静态链接file hello显示statically linked文件是否完整传输md5sum hello两边对比哈希值一致板端是否可执行直接./hello打印Hello信息板端架构确认板上执行uname -m输出aarch64对比PC端和板端的md5值可以排除传输损坏的坑。很多奇怪问题比如拷贝过程中文件被截断或者U盘文件系统有毛病都能用这招快速定位。5. 常见问题与排查技巧实录5.1 新手最容易踩的五个坑速查表交叉编译hello这个看似简单的流程实际操作中翻车的人真不少。我把这些年见过的典型报错整理成一个速查表报错信息原因解决方案cannot execute binary file: Exec format error编译工具用错生成了x86_64文件检查Makefile里用的是不是aarch64-linux-gnu-gcc并用file验证架构version GLIBC_2.34 not found工具链自带glibc版本比板端系统高改用静态编译或换版本匹配的SDK工具链/lib/ld-linux-aarch64.so.1: No such file or directory动态链接文件在精简系统上缺少动态加载器静态编译或者把板端glibc补齐Permission denied文件没有执行权限chmod x hello注意U盘挂载目录通常不允许直接执行串口连接时提示cannot receive hello packet串口参数、线序或USB转接板供电问题检查波特率、数据位/停止位配置换根线或换个USB口再试最后一项看起来跟hello程序本身没关系但实际调试设备时非常常见。很多人把程序跑在板子上想通过串口看输出结果上位机一直报收不到数据包就以为是程序挂了。排查顺序永远是先确认物理连接和串口参数再怀疑程序逻辑。5.2 从实战里总结的几个小技巧这个hello项目虽然简单但有几个习惯如果从现在就养成能帮你躲掉后面很多麻烦。第一个小技巧把静态编译的hello文件当成“工具链健康标尺”。我自己的习惯是每次新拿到一块RK3588/RK3568板子先交叉编译一个静态hello传到/root/下跑一遍。如果这个能跑说明工具链和传输链路都没问题再遇到别的程序跑不起来我就只需要往代码和库依赖方向排查省掉一大半无效调试时间。第二个小技巧Makefile里务必保留CROSS_COMPILE和CC这些变量分离的习惯。后面你用瑞芯微SDK里的rknn_model_zoo时会发现官方大量工程的编译方式就是“设置工具链路径-make”如果你自己写的工程早就习惯这种结构迁移会非常丝滑。第三个小技巧别小看aarch64-linux-gnu-objdump -D hello这条命令。如果你的程序编译出来在板子上段错误了先用PC上的反汇编看一眼指令很多时候问题一眼就能定位到是寄存器操作还是跳转地址出了问题比你在板子上疯狂加printf高效太多。我个人在实际操作中的体会是交叉编译hello这件事本质上是把整个嵌入式开发流程中最容易出错的三个环节——架构、链接、传输——提前用最简化的方式演练了一遍。这个流程通了后面跑YOLOv5s的C部署代码就有底气了。而且这套流程不只适用香橙派RK3588你手头换成其他ARM64板卡甚至Windows下做Linux开发思路都是一毛一样的。先把这个基本功练扎实下一节我们可以聊RKNN模型转换和NPU推理了。
返回列表