RK3588交叉编译实战:PC编译ARM程序,为YOLOv5s部署铺路
1. 为什么在RK3588上跑程序偏偏要绕到PC上来编译先说一个我自己的经历。不少朋友第一次拿到香橙派5也就是搭载RK3588芯片这块板子之后第一反应都是先SSH连上去然后直接在板子上敲gcc编译代码觉得这样最直接、最省事。这个思路没有错RK3588性能并不弱4颗A76大核加上4颗A55小核日常编译个hello world级别的程序绰绰有余。但如果你真的打算在这个板子上跑YOLOv5s走的是模型部署这条路情况就完全不一样了。我之前在这套香橙派RK3588的系列教程里反复强调过一个观点部署不是写代码而是搬运和适配代码。YOLOv5s要落地到板子上涉及的东西远不止一个模型权重还有OpenCV图像处理、RKNN推理库、NPU驱动以及围绕模型转换生成的一套C/C推理代码。这些代码如果全部放到板子上本地编译OpenCV这种级别的库动辄编译一两个小时起步而且板子的散热、存储、内存都要跟着吃紧。更关键的是板子上自带的gcc版本、glibc版本和PC上开发环境往往不完全一致你在板子上本地编出来的东西最后链接到的库和你在PC上验证过的逻辑可能是两套体系。这时候交叉编译就派上用场了。所谓交叉编译简单理解就是在PC上用一套专门生成ARM架构程序的编译器把代码编译成可以在RK3588上直接运行的可执行文件然后拷贝到板子上执行。做这件事你的PC不会累板子也不用承担计算压力最后得到的产物和直接在板子上编出来的是同一套机器指令效果一样但效率高出一个数量级。我打过一个比方在板子上本地编译相当于把整个家具工厂搬到客户家里现场生产交叉编译则是你在自己的工厂里把家具做好再叫物流送过去。对于hello这种小项目搬工厂和做物流看着没差别可一旦换成YOLOv5s这种需要几十个依赖库联动的大项目没有物流体系你在现场能累到怀疑人生。这一篇教程就在整个系列的第6节文章标题非常直白——交叉编译hello。它的意义就在于用最小的成本把这条“PC编译、板子运行”的链路彻底打通。你能把hello跑起来就说明工具链、环境变量、编译参数、文件传输、执行权限这一整条管线全部正常。后面再换成YOLOv5s的推理代码只是在这条管线上增加内容而不是从零探索。这篇文章的操作目标或者说适合谁来学呢刚拿到香橙派5、RK3588开发板准备做视觉项目但还摸不清开发流程的新手。看过YOLOv5s部署教程却卡在“交叉编译”这个第一步连工具链都没装明白的玩家。想弄懂自己PC上编出来的程序为什么一到ARM板子上就报“Exec format error”的那些好奇者。整个操作不用超过二十分钟但效果立竿见影。你会在板子上看到一个自己亲手编译的hello输出而这个过程就是后续YOLOv5s部署的第一块地基。1.1 ARM板子明明能装gcc为什么还要多此一举很多人会问RK3588板子明明能装gcc我在板子上直接编不就行了吗多绕一道PC交叉编译难道不是脱裤子放屁先说结论对于hello这种玩具程序直接在板子上编译确实没问题对于YOLOv5s部署这类真实工程直接编译就是自找麻烦。第一个麻烦是编译时间。hello代码量小板子编译眨眼就完成。但一旦进入YOLOv5s的工作流你会碰到OpenCV这种几乎含着半个操作系统依赖的巨型库。在RK3588上从源码编译OpenCV即使全核火力全开也要大半个小时配置不当甚至更久。前面说了这个过程要在板子上反复调试每次微调一点代码都要等这么长时间开发节奏会被拖垮。第二个麻烦是环境一致性。你在板子本地编译时板子的系统自带了某个版本的gcc、某个版本的glibc。但在PC上开发推理代码时你习惯用的编译器版本、标准库版本未必和板子一致。交叉编译工具链则是专门为RK3588定制的它保证编译器生成的目标代码和板子上的运行环境严格匹配从源头消除“我这边编译好好的怎么到板子上就跑不起来”的隐性问题。第三个麻烦是产物管理和版本控制。正规的项目开发流程代码在PC上统一管理编译产物打包分发到目标设备。交叉编译天然符合这个模型PC是工厂板子是运行终端。如果每块板子都各自编译产物的来源、版本、依赖状态完全不可控出问题了排查起来非常痛苦。所以交叉编译不是说板子不能本地编译而是说在工程化、规模化地做嵌入式部署时交叉编译是唯一可持续的路线。这个道理等你真正跑起YOLOv5s推理代码的那一天会有更深的体会。1.2 交叉编译要解决的三个核心问题交叉编译看起来只是“换了个编译器”但背后其实是三个问题的打包解决。你在RK3588上跑程序绕不开这三件事。第一CPU架构的对齐。RK3588是ARM架构的64位处理器PC绝大多数是x86_64架构。两种架构的机器指令完全不同。你用PC自带的gcc编出来的程序本质上是x86指令集拿到ARM板子上根本没法治执行。交叉编译工具链生成的是aarch64指令集的可执行文件也就是RK3588能直接读懂的语言。第二ABI接口的匹配。架构对了只是第一步还要保证二进制接口一致。ABI决定了函数参数怎么传、系统调用怎么发、结构体内存怎么排。工具链里特别强调的glibc版本、动态链接器路径都属于这个范畴。RK3588板载系统的lib目录路径通常是/lib/aarch64-linux-gnu你的交叉编译工具链必须按照同样的路径规则来链接动态库程序拿到板子上才能找到自己的依赖。第三依赖关系的处理。一个工程往往不是单文件而是带一堆动态库、静态库、头文件。交叉编译时你得告诉编译器这些依赖放在哪里编译时链接进去运行时在板子上也要能找到对应的库文件。hello不需要额外依赖但YOLOv5s推理代码要链接RKNN Runtime库、OpenCV库这个依赖处理的复杂度会指数级上升。把这三件事想清楚你再看交叉编译命令那一长串参数就不会觉得头疼了。每个参数背后都是在和架构、ABI、依赖这三位打交道。下表是我整理的本地编译和交叉编译的核心区别初学阶段对照看就够了对比项板子本地编译PC交叉编译编译工具运行位置板子ARM环境PCx86环境生成代码架构aarch64aarch64由工具链决定依赖库获取板子系统自带需手动指定交叉编译库编译速度受板子性能限制受PC性能限制适用场景小项目快速验证大型工程、持续迭代工程可追溯性弱强2. 环境准备工具链的选择直接决定后续部署路线的顺畅程度工具链是交叉编译的核心选对了后续一路顺畅选错了光是版本冲突就够折腾几个晚上。我在这块踩过不少坑整理下来给后来者省点时间。先说结论针对香橙派5、RK3588、YOLOv5s这套组合推荐使用Rockchip官方SDK内置的交叉编译工具链也就是gcc-arm-10.3-x86_64-aarch64-none-linux-gnu。这套工具链我从试用开始就一直用到现在稳定性扛住了多次完整部署项目没出过工具本身的差错。打开香橙派或瑞芯微官方资料页找到Linux SDK下载包解压后路径通常在prebuilts/gcc/linux-x86/aarch64/下面。这个工具链是完整的一套包括gcc、g、ld、objcopy等一系列工具不用自己再去网上东拼西凑装依赖非常省心。2.1 RK3588对应的工具链版本怎么选很多初学者看到“gcc-arm-10.3”这种命名就懵不知道这串字符是什么含义。拆开看其实很直观gcc这是GNU编译器套件里的C语言编译器配套的g用来编CYOLOv5s推理代码大量用C所以你的工具链里一定要包含g别只装gcc。arm目标架构是ARM。10.3GCC主版本号。版本不是越新越好关键是要和板子系统的glibc版本兼容。10.3这个版本在RK3588相关的官方SDK里经过充分验证属于“官方选择的版本”直接拿来用最稳妥。x86_64指的是这套编译器运行在x86_64架构的PC上。aarch64-none-linux-gnu目标运行环境是64位ARM Linuxnone表示没有vendor也就是无特定厂商定制属于通用交叉编译工具链的标准命名。如果你不走SDK内置路线也可以直接用apt install gcc-aarch64-linux-gnu装Ubuntu发行版的ARM交叉编译工具。两条路都走得通但我不建议新手用apt这条路。原因有三版本太新和RK3588板载系统的glibc版本容易脱节。编译出来的程序拷到板子上报告GLIBC_2.34 not found这类错误时排查起来非常折磨人。apt工具链没有经过Rockchip硬件平台的适配测试涉及NPU、MIPI屏幕这类板载外设时头文件和链接脚本可能缺胳膊少腿。SDK内置工具链还带了配套的开源库包括YOLOv5s部署时必要的OpenCV等省心。风向上说你在野火、鲁班猫、香橙派这几个社区里看到的RK3588部署教程绝大多数也是先用官方SDK工具链把基础环境跑通。和社区生态保持一致后续碰到问题能搜到的案例也多得多。2.2 两条工具链路线以及为什么教程路线我选SDK工具链我把两个方案放一起对比你心里就有数了对比项官方SDK内置工具链apt的gcc-aarch64-linux-gnu版本GCC 10.3适配RK3588随Ubuntu发行版浮动glibc匹配度高官方调校需自行确认容易踩坑附带库含OpenCV等常用库无附带可搜索的教程案例多较少上手难度低中适合玩家新手/中级中级我个人推荐SDK内置工具链还有一个很实际的原因YOLOv5s部署到RK3588上要用的RKNN Toolkit2其官方Runtime库对应的交叉编译示例默认对接的正是这套工具链。你后续做模型推理代码时官方示例里的CMakeLists.txt会写死人但你不需要改直接用就行。如果你用apt工具链可能需要手动调整一堆链接参数不符合“手把手教程”省心的初衷。需要说明的是这套SDK工具链个头不小解压后几个GB算是常态。放到一个不带空格的路径下比如~/rk3588/toolchain/避免后期Makefile里路径解析出诡异问题。Windows用户如果是在WSL里操作也要注意别把工具链放在/mnt/c/这种跨文件系统路径下不然编译时库文件遍历会慢到让你怀疑人生。2.3 为Windows用户准备的几条硬性建议我知道相当比例的朋友是Windows主力机临时为了搞RK3588才开始碰Linux环境。这里分享几条硬性建议按这些做可以少走一大堆弯路优先用WSL2。Windows 10/11自带的功能一条wsl --install就能装好一个Ubuntu 22.04环境和物理机共享文件SSH、SCP命令都原生支持。比虚拟机启动快和VS Code的Remote插件配合也顺手。交叉编译的PC环境建议统一用Ubuntu 22.04 x86_64。这套组合和SDK工具链兼容性最好教程中所有的路径、命令都是在这个环境验证过的。不要把SDK工具链放在WSL的/mnt目录。WSL挂载Windows磁盘时走的是9P协议大文件访问极慢。工具链解压一次要半天编译的时候频繁读取头文件更是灾难。正确做法是把工具链放在WSL自己的文件系统里Windows侧的代码文件通过\\wsl$路径访问。Windows侧写代码时换行符务必设为LF。VS Code右下角可以看到当前文档换行符格式如果是CRLF传到Linux环境后会带来隐蔽的编译错误。这个坑我后面单独讲印象特别深。3. 手把手编译hello从源码到板卡运行环境准备好了随手写点代码把链路打通力求整个过程没有多余动作。这次写的hello不搞花活但我会故意加上一点小功能让程序跑起来后能直接打印出板子架构用输出给自己验证交叉编译成功。3.1 写一个能自证架构的hello.c打开编辑器新建hello.c#include stdio.h #include stdlib.h int main() { printf(Hello from Orange Pi RK3588!\n); printf(Current architecture: ); fflush(stdout); system(uname -m); return 0; }这段代码里system(uname -m)在程序运行时调用系统的uname命令输出当前内核架构。如果交叉编译成功程序在RK3588板子上打印出来的应该是aarch64。这个设计的目的就是让运行结果本身来证明代码确实是以ARM架构编译的。有人可能会问为什么不直接交叉编译一段只打印Hello World然后结束的简单代码因为那种程序跑起来后你无法从输出上确认它真的是ARM版本的二进制。加了uname -m这一句程序运行时主动报告当前平台验证过程就变得透明了。后面你换成YOLOv5s的检测程序也可以沿用这个思路加个平台信息打印作为启动自检。3.2 三连编译对比本机x86、板子本地、交叉编译这一节是整篇教程的核心操作。我分三条路分别编译同一个hello.c让你直观看到结果差异。第一步PC本机编译生成x86版本在PC终端执行gcc -o hello_x86 hello.c ./hello_x86输出Hello from Orange Pi RK3588! Current architecture: x86_64这个版本CPU架构是x86_64只能在PC上跑拷到板子上就是废纸一张。第二步板子本地编译生成板根上的没有意义SSH登录香橙派5将hello.c传过去在板子终端执行gcc -o hello_board hello.c ./hello_board也能正常运行输出架构是aarch64。但如果我一直用这条路后面到了YOLOv5s的OpenCV和RKNN库编译时间代价会让你后悔没有早点学会交叉编译。第三步交叉编译这个才是重点回到PC终端进入SDK工具链目录找到编译器可执行文件aarch64-none-linux-gnu-gcc执行$HOME/rk3588/toolchain/gcc-arm-10.3-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc -static -O2 -o hello_arm hello.c命令参数解析每一条都少不得-static静态链接把C库直接打包进可执行文件。这个参数在第一次交叉编译时务必带上后面解释原因。-O2二级优化编译出的代码在性能和体积之间平衡得不错。-o hello_arm指定输出文件名叫hello_arm方便区分。编译完成没有任何输出就代表成功了。你可以用file命令检查产物格式file hello_arm输出示例hello_arm: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, BuildID[sha1]..., for GNU/Linux 4.9.0, not stripped看到ARM aarch64这几个字说明产物就是给RK3588准备的这一步相当关键后面踩坑章节还会提到。3.3 file命令和scp部署以及跑起来的完整步骤编译产物有了接下来就是把它弄到板子上。最简单的方式是SCPscp hello_arm root你的板子IP:/root/把你的板子IP替换成香橙派5的实际IP比如192.168.1.88。首次连接会提示确认指纹回答yes再输密码传输就完成了。SSH进入板子给文件加执行权限然后运行chmod x hello_arm ./hello_arm看到下面的输出就说明整个链路大功告成Hello from Orange Pi RK3588! Current architecture: aarch64这里有一个细节不要忽略交叉编译默认产物是没有执行权限的所以chmod x这一步必须做。很多新手卡在“文件拷过去了却运行不了”往往就是少了这个动作。Windows下甚至可能出现文件拷过去权限全无的情况务必检查。如果不说你或许注意到一个反差同样是编译hello.cPC本机直接编的版本没法在板子上跑交叉编译的版本却能跑。这就是架构差异的体现也是整个交叉编译的价值所在。另外提醒一下如果你的板子终端报hello_arm: No such file or directory或者Exec format error不要慌把下面的排错章节看完绝大多数问题都能对号入座。4. 踩坑实录文件本体、工具链版本和开发调试的三处关键障碍交叉编译这个事说穿了就是五个步骤选工具链、敲命令、看文件格式、传文件、跑程序。但每个步骤都有它独特的坑。我把这段时间被问得最多的几个问题集中到这一章按照排查链路完整走一遍你遇到类似情况能按图索骥。4.1 “Exec format error”与跨架构产物的判别方法你可能遇到过这个经典报错把PC上gcc编译的程序拷到RK3588板子上运行时报出Exec format error。很多人第一反应是“板子系统坏了”或者“命令有问题”其实原因很简单CPU不认这个二进制文件的指令集。我把排查链路完整列给你以后但凡碰到“程序跑到板子上执行不了”按这个顺序查基本不会漏。先在PC上看产物格式file hello_x86输出里如果是x86-64别再拷到板子上了白费力气。x86-64格式的ELF文件ARM内核拿到手上根本没办法解析入口指令只能吐出一个Exec format error。如果文件格式是ARM aarch64却依然报错查文件权限ls -l hello_arm看第一列有没有x权限。没有就补上chmod x hello_arm如果权限没问题再查动态链接器路径。非静态编译的程序会依赖编译器工具链里的动态链接器路径通常写死在ELF头里。在板子上执行readelf -l hello_arm | grep interpreter如果interpreter路径指向PC上某个不存在的路径程序当然起不来。解决办法要么保证路径在板子上存在要么干脆用-static静态编译跳过这一环。最后才考虑是不是工具链版本过老或过新导致指令不兼容。这四条排查下来90%的“跑了没反应/报错”都能定位。核心思路是逐层剥离可疑因素从最明显、最容易查的开始。4.2 GLIBC版本不匹配与静态编译的取舍这个坑我建议你直接预习因为它几乎必然出现在你交叉编译第一个非hello级项目的时候。工具链里的glibc和板子系统里的glibc版本不是任何时候都一致。如果工具链的glibc版本高于板子运行时程序在板子上启动时就会报GLIBC_2.34 not found这类错。这不是你代码的问题是动态链接器在找符号时发现板子上缺少对应版本的库直接罢工。解决办法有三个层次最简单交叉编译时加-static。静态链接把C库打包进可执行文件运行时不依赖板子系统库版本几乎一劳永逸。代价是可执行文件体积变大hello这种程序一下从16K涨到700K左右。但换来的是“拷贝即运行”的确定性尤其在初期验证阶段这个取舍非常值得。稍微进阶用和板子系统glibc版本匹配的工具链。交叉编译不一定要追新官方SDK选择的10.3版本就是为了和板载Ubuntu/Fedora系统的库兼容。用SDK工具链这个问题在源头被规避掉。更彻底让工具链链接动态库时使用板子的sysroot。这个涉及到把板子系统的根文件系统拷贝到PC上作为编译时的虚拟根目录工程规模大了以后这是必经之路但新手阶段不用碰。我一直建议初学阶段无脑用-static。先把链路跑顺不要被动态库的隐性问题搅乱。等YOLOv5s推理程序也跑通了再研究怎么瘦身、怎么走动态链接。4.3 开发环境里最隐蔽的坑换行符、编码与权限交叉编译本身不复杂但开发环境的小问题能把人折磨到怀疑工具链有问题。这里说三个我亲测踩过的坑。坑一Windows记事本/VS Code默认CRLF换行符。Windows下按回车输入的是\r\nLinux只认\n。hello.c这种简单代码影响不大但如果换行符问题跑到Makefile、shell脚本里那报错可以说是五花八门make: *** missing separator. Stop、$\r: command not found一眼看去像天书。解决办法是在VS Code右下角把换行符设为LF或者编译前用dos2unix统一转换。养成习惯后面处理YOLOv5s项目的脚本时能少掉一大半莫名其妙的报错。坑二编译器路径里的空格。我见过有人在Windows下解压工具链到C:\Program Files\RK3588 SDK\toolchain进入Linux因为目录带空格Makefile里一大串路径变量全乱了。在WSL里路径也最好别带空格老老实实放到~/rk3588/toolchain这种干净路径下。坑三SCP传输时的权限丢失。scp传输文件后文件权限往往保持源端的模式交叉编译产物默认没有执行位所以拷过去运行时报Permission denied。这不是系统问题是权限位没给够。一句话每次传完新编译的二进制先chmod x再跑。5. 这一步和YOLOv5s到底有什么关系写到这里你可能会产生一个合理疑问我费这么大力气交叉编译一个hello和标题里说的YOLOv5s有什么关系一个hello程序和一个目标检测模型看起来八竿子打不着。但恰恰是这一步通了后面所有的部署工作才能顺利开展。5.1 hello world验证的是什么用一个最朴素的角度来回答交叉编译hello world真正验证的是整条工具链的“可用性”。你想想YOLOv5s部署要做什么事情要把PyTorch训练出的模型转换成RKNN格式要编写一份C/C推理代码加载RKNN模型、处理图像、执行推理、打印结果要链接RKNN Runtime库和OpenCV库编译生成可执行文件把可执行文件拷贝到板子上和NPU驱动、系统库协作跑通推理。这四步里面第2、3、4步绕不开交叉编译。如果hello这一步就卡住后面几万字的技术文档看了也是白看。相反hello跑通说明工具链已经能正确生成aarch64指令集的可执行文件SCP传输正常执行权限设置无误运行环境兼容——这些是任何后续可执行文件能够跑起来的前置条件。换句话说hello是交叉编译链路的“冒烟测试”。冒烟测试通过整个信号通路是通的后面直接换载荷就能推进。5.2 从交叉编译到YOLOv5s后续会用的技术栈全貌提前让你看一眼后面的路线你就知道今天这一步在整盘棋里的位置。RK3588上部署YOLOv5s完整的技术栈大致长这样交叉编译工具链今天装好的这套gcc-arm-10.3-x86_64-aarch64-none-linux-gnu。后续所有C/C推理代码都用它编。RKNN Toolkit2运行在PC上的模型转换工具把PyTorch导出的YOLOv5s权重转换成RK3588 NPU能执行的.rknn模型文件。RKNN Runtime库由瑞芯微提供运行在板子上的推理库你的C/C代码要链接它的librknnrt.so。OpenCV交叉编译版本图像预处理、画框、缩放都要用到。需要你基于交叉编译工具链把OpenCV编成aarch64版本工程量大但离不开。模型与资源文件YOLOv5s的.rknn模型、类别标签、输入输出配置。CMake构建脚本管理编译过程中的头文件、库文件、可执行文件路径。后面我们会基于它写一份专门适配RK3588的CMakeLists.txt。看到了吗后面每一步都用得上今天搭好的交叉编译环境。你现在练熟的static参数、file命令、chmod操作在OpenCV和RKNN库的编译调试中会频繁出现。5.3 给新手配一条循序渐进的练习路径如果完全没接触过嵌入式部署我的建议是按照这个路径走每一步都有清晰产出不要跳完成本教程的交叉编译hello产出在板上运行的aarch64可执行文件。改造hello程序让它读取一个图片文件、打印图片尺寸交叉编译并部署。这一步让你先碰碰文件IO为图像处理热身。用官方SDK跑通一个RKNN官方示例比如rknn_benchmark确认NPU驱动和Runtime库能正常工作。跑通RKNN Toolkit2自带的YOLOv5s demo确认模型转换链路没有断点。最后才是自己写代码把YOLOv5s的模型加载、预处理、推理、后处理串起来部署到RK3588板子上。这样一路走下来每个阶段出问题都能快速定位是模型问题、编译问题还是环境问题不会一锅粥地查。我在实际部署YOLOv5s时最深的体会是真正让人卡住的地方往往不是模型本身而是编译和运行环境。一个二进制能不能在板子上跑起来取决于架构、依赖、权限、路径这一串基础变量。今天花二十分钟把hello跑通就是在给这些变量做体检。工具链认不认RK3588、SCP通不通、权限对不对一口气全验证完。把这一步走稳了后面YOLOv5s的部署才能踏实往下推。最后再分享一个小技巧把这个交叉编译命令存成一个shell脚本以后每次编译新程序都复用省得每次敲一长串工具链路径。我就是这么干的写了个mkarm.sh放在工程根目录后续编OpenCV和RKNN推理代码的时候改改参数就能直接编效率翻倍。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →