ARTICLE DETAIL

资讯详情

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

RK3588交叉编译实战:从hello world到板端运行全流程

RK3588交叉编译实战:从hello world到板端运行全流程 在折腾RK3588的路上交叉编译是绕不开的一道坎。前几篇咱们把香橙派5刷好系统、连上网络、跑通了基础环境也聊了 yolov5s 在板端推理的大致图景。现在到了衔接 PC 端和板子端的关键一步交叉编译。很多人一听这个词就头疼其实它没那么玄乎——就是在一台机器上编译出另一台机器能运行的程序。这篇我就拿最经典的 hello world 当试验品把交叉编译的完整链路彻底走一遍工具链装法、编译参数、传板方式、运行验证、常见报错一次讲明白。这篇之后你再去看 opencv、rknn 相关库的交叉编译思路完全一样等于提前把最难的水下冰山摸清楚了。1. 交叉编译到底是什么为什么绕不开1.1 一块板子两种方言先捋一个基本概念你的日常电脑只要不是苹果 M 系列大概率是 x86_64 架构香橙派5上的 RK3588 芯片则是 ARM 架构准确叫 AArch64。这两种芯片的机器指令互不通用就像普通话和粤语你说得再流利对方听不懂也是白搭。交叉编译的意思就是我在 x86 电脑上用一套会讲 ARM 方言的编译器把源代码编译成 RK3588 能直接运行的可执行文件。这套编译器跟普通编译器最大的区别在于它生成的机器码目标不是当前这台电脑而是另一台设备。所以叫交叉cross compile。这里有个容易混淆的点SDK、交叉编译器和目标平台之间不是固定搭配。理论上任何平台都能交叉编译出任何其他平台的程序只是工具链的获取难度和维护成本不同。对香橙派5来说最常规的方案就是 x86_64 Linux 主机上装 aarch64-linux-gnu 工具链输出 ARM 64 位的 ELF 文件。我们后面所有实操都基于这个组合。1.2 为什么不在RK3588上直接编译有人会问既然板子能开机、能装系统直接在板子上 gcc 编译不行吗行但你会很难受。第一个问题是资源。RK3588 性能确实不错8核A76A55跑推理还行但编译大型依赖库时CPU 长时间满载散热跟不上的话板子温度直接冲着 85 度以上去。内存如果只有 4GB 或 8GB编译 opencv、boost 这类重库时 swap 疯狂读写 TF 卡整个系统卡成幻灯片编译一个多小时然后中途报错这种体验我经历过一次就不想再来第二次。第二个问题是环境。在板子上编译你得先把开发包装在板端系统里apt 装一堆 -dev 库把板子原本干净的系统搞得乱七八糟。板端系统是拿来跑最终程序的不是拿来当开发机的。开发环境留在 PC运行环境留在板子这是嵌入式开发的黄金分工。第三个问题才是关键最终部署 yolov5s 相关 C 推理程序时很多依赖库你根本没法在板子上现场编译就算能也没有那个必要。交叉编译才是正路。1.3 交叉编译的边界编译链 vs 运行时交叉编译只解决代码变可执行文件这一步它不负责程序运行时的所有事情。编译时你选择了动态链接程序运行时就要求板子上有对应的动态库编译时用了静态链接那运行依赖就全都打包进文件里。这个区别后面 hello 实验里会实际看到提前有个概念编译是翻译运行是登台翻译稿再好舞台上缺道具照样演不了。明白这一点你就能理解为什么交叉编译出的程序经常会出现放到板子上报找不到动态库的奇怪现象。不是程序写错了也不是架构不对而是翻译稿里引用了板子上不存在的库。后面排查章节会专门细说。2. 环境准备先把工具链装明白2.1 一台能上网的x86 Linux电脑交叉编译的第一步是准备一台 Linux 宿主机。Ubuntu 20.04 或 22.04 都可以虚拟机也行但注意虚拟机里要给足够的磁盘和内存编译时别抠抠搜搜。Windows 上也可以用 WSL不过网络、USB 透传这些偶尔有小毛病如果目标是把 RK3588 部署玩明白我还是建议装个原生 Ubuntu一劳永逸。确认一下主机架构在终端跑uname -m输出 x86_64就说明主机是 64 位 x86 架构符合我们的前提。这步看着多余实际排查问题的时候很有用一多半架构错乱都是因为没确认这个。2.2 安装aarch64交叉编译器Ubuntu 的软件源里直接有交叉编译工具链不需要去第三方网站下载这点比很多人想象得省事。打开终端执行sudo apt update sudo apt install -y gcc-aarch64-linux-gnu g-aarch64-linux-gnu装完就有了两个关键命令aarch64-linux-gnu-gcc和aarch64-linux-gnu-g。前者编译 C 代码后者编译 C 代码。做 yolov5s 部署时C 程序用得到 g别只装 gcc。这里多说几句网上很多教程会引导你去下载 Linaro 或者 ARM 官网的工具链压缩包还要自己配环境变量折腾半天。其实 Ubuntu 源里的工具链版本虽然未必是最新但足够稳定支持 glibc 动态链接日常交叉编译完全够用。新手没必要一上来就挑战手工部署工具链先把流程跑通再说。2.3 验证工具链是否可用装完不要急着写代码先验证。执行aarch64-linux-gnu-gcc -v终端应该打印出包含 Target: aarch64-linux-gnu 的信息以及 gcc 版本号。看到这两行说明工具链生效了。如果提示 command not found大概率是工具链没装上或者当前 shell 没重新加载 PATH。可以试试重启一下终端或者直接重新执行 apt install。多数情况是前者。这里还有一个常见的低级错误有人会把arm-linux-gnueabihf-gcc当成 RK3588 的工具链。这个工具链是给 ARM 32 位 CPU 用的香橙派5是 64 位用了它编译出的程序在板子上会报 Exec format error。道理很简单但坑很真实我在很多交流群里见过新手卡在这。3. 第一个hello从源码到ARM可执行文件3.1 写一段最朴实的C代码现在动真格的。在宿主机上建个目录我习惯叫~/rk3588-cross/里面放一个文件名字就叫 hello.c#include stdio.h int main(void) { printf(Hello, RK3588!\n); return 0; }这段代码没有任何特殊之处就是最基础的 C 程序。但请相信我越基础的代码越适合做工具链验证。先把最简程序跑通再去玩复杂的这是嵌入式开发的铁律。代码里的输出我特意写成 Hello, RK3588! 而不是传统的 Hello World这样等会儿在板子上看到输出时能直观确认跑的就是这个交叉编译的产物。3.2 用交叉编译器生成ARM程序在终端进入目录执行aarch64-linux-gnu-gcc hello.c -o hello命令成功执行没有任何输出目录里多了一个名为 hello 的文件。注意编译过程没有任何提示就是最好的提示。如果报错多半是代码本身问题比如标点符号用了中文输入法或者缺少头文件。接下来最关键的一步验证用 file 命令看看这个文件到底是什么file hello正常输出hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]xxx, not stripped盯着 ELF 64-bit LSB executable 和 ARM aarch64 这两段。它明确告诉你这是一个 ARM 64 位的可执行文件不是 x86 的。交叉编译的第一步到这里就成功了。3.3 动态与静态两种链接方式怎么选file 输出里还有一行值得注意dynamically linked, interpreter /lib/ld-linux-aarch64.so.1。这句话的意思是这个程序依赖系统的动态加载器运行时需要板子上的 glibc 动态库。大部分情况下没问题因为香橙派5官方的 Ubuntu/Debian 系统里这些库都齐全。但如果你想更保险可以用静态编译aarch64-linux-gnu-gcc -static hello.c -o hello_static静态编译会把 C 库直接塞进可执行文件里编出来的文件体积大很多但完全不依赖板端库扔到任何 AArch64 Linux 系统上都能跑。我对比一下两个文件大小ls -lh hello hello_static动态版可能只有十几 KB静态版动辄几百 KB 甚至上 MB。这就是带不带干粮的区别动态版是到了再找饭馆静态版是把干粮全背身上。静态编译也有缺点编译慢、文件大、而且用到的库如果涉及 license打包分发会有合规问题。对 hello 实验来说建议两个都编译出来都传上板子跑一跑体会一下区别。4. 把程序送上香橙派跑起来4.1 三个派送可执行文件的方式程序编译好了怎么弄到板子上三个常用办法按推荐程度排序。第一SCP 网络传送。前提是板子和电脑在同一局域网下板子能上网你知道它的 IP 地址。我习惯先在电脑上确认板子 IP然后执行scp hello hello_static orangepi192.168.1.100:/home/orangepi/把 192.168.1.100 换成你板子的实际 IP。它会提示输入密码输入后文件就传过去了。这个方式最方便后面传模型文件、传库文件都用它。第二U 盘拷贝。把 hello 文件放进 U 盘插到板子的 USB 口上在板子上用lsblk看一下挂载路径一般是 /media/orangepi/xxx然后 cp 出来。这种方式不依赖网络适合电脑和板子不在同一网段时应急。第三ADB 推送。如果板子系统里开了 adb server刚好还被电脑识别了可以用adb push hello /home/orangepi/这个方法在调试阶段很有用特别是板子屏幕上操作不方便的时候。不过香橙派5官方系统默认不一定开 adb需要自己装或启用对新手来说门槛略高我更推荐前两种。4.2 给文件执行权限并运行文件传到板子上之后切记先做一件事给可执行权限。虽然编译器默认生成的文件通常带执行权限但经过 U 盘或某些传输方式之后可能会丢。在板子的终端里运行chmod x hello hello_static然后执行./hello正常情况下会输出Hello, RK3588!看到这行字恭喜整个交叉编译链路已经彻底闭环了x86 代码仓库、ARM 编译器、ARM 可执行文件、板端运行成功。再试试静态版./hello_static同样输出但步骤完全不一样你体会一下两者在板端运行时的差异。如果你够细心还可以对比下ls -lh看两个文件大小直观感受静态库的体积税。4.3 交叉编译思维的确立很多人以为交叉编译学会命令就完事了其实最重要的是一种思维转换以后你每次在电脑上写完代码第一反应不应该是本地跑一下试试而是这个是要给哪个平台用的。如果是板子上的程序就直接调 aarch64 工具链别再用 gcc 编译完了发现架构不对回头重新编。这个思维一旦建立后面看 opencv 交叉编译、rknn 相关库的 CMake 配置你会觉得特别亲。因为无论多复杂的库底层就是在做同一件事——告诉构建系统我的目标平台是 aarch64我的工具链在哪我的 sysroot 在哪。hello world 是这套思维的最小模型。5. 常见问题与排查技巧实录5.1 高频报错速查表这一节是大量实操积累出来的干货我按报错现象整理成速查表方便你直接对号入座。报错现象原因分析解决办法aarch64-linux-gnu-gcc: command not found工具链没安装或 PATH 未配置重新执行 apt install检查是否在正确的用户环境运行报 No such file or directory文件是动态链接板子上缺少依赖库改用-static静态编译或板端补装库用 ldd 查看缺失项运行报 Exec format error文件架构不对比如编了个 x86 或 ARM 32 位file 看架构确认用 aarch64-linux-gnu-gccPermission denied文件没有执行权限chmod x hello编译远超预期卡死或 OOM宿主机资源不足或 swap 不够换更高配置电脑用静态编译时更吃内存源码在板子上中文字符乱码hello.c 含中文注释或输出编码不一致保持源码用 ASCII输出英文文件统一 UTF-8表格之外补充一个很容易撞上去的坑代码文件是在 Windows 上编辑的保存成了 CRLF 行尾拿到 Linux 交叉编译时偶尔会报 stray 字符错误。解决办法是运行 dos2unix或者干脆在 Ubuntu 上写代码。5.2 排查三板斧file、readelf、ldd遇到问题别慌记住这三个命令。第一斧file 看架构。任何可执行文件到了手里先 file 一下确认 ELF 头和架构匹配。这能过滤掉八成问题。第二斧readelf 深入检查。比如readelf -h hello可以看到 Class 是 ELF64、Machine 是 AArch64还能看到程序入口地址。当你有多个工具链、多个版本的时候这招能精确判断文件到底是谁编出来的。第三斧ldd 查依赖。在板子上对动态链接的可执行文件执行ldd hello它会列出程序运行需要的所有动态库以及是否已经找到。如果某一行显示 not found那就是缺库直接把问题定位到运行时缺少哪个库这一层。注意 ldd 是在板子上用的不是在宿主机上因为宿主机和板子的库环境完全不同。这三板斧组合起来能从架构、入口、依赖三个维度把一个可执行文件看透。我在实际部署中只要遇到运行问题基本就靠这三招定位效率极高。6. 这跟yolov5s到底什么关系6.1 yolov5s在RK3588上的典型部署链路聊完 hello world很多人会问我学的是 yolov5s跟交叉编译有什么关系绝大多数情况下yolov5s 的部署不是直接把 PyTorch 模型扔到板子上跑那样速度太慢、资源也浪费。RK3588 的看家本领是内置的 NPU6 TOPs 算力模型要先经过转换变成 NPU 能认的 RKNN 格式然后才能在板端高效推理。这条链路一般是训练好的 yolov5s.pt 先转成 ONNX再用 RKNN-Toolkit2 在 PC 端转成 .rknn 文件最后把 .rknn 文件和推理脚本一起部署到板子上。如果你用的是 Python API那确实不用交叉编译但只要你想上 C API想提高实时性、想脱离 Python 环境就得交叉编译 RKNN runtime 和 opencv 这些依赖库。这些库体积大、源码复杂、交叉编译配置项多比 hello world 难不止一个量级。现在 YOLO 系列迭代很快yolov8、yolo26 各种新模型不断出来模型轻量化、NPU 适配是热点但落到 RK3588 上跑通交叉编译这条链路永远是共同的基础。模型换了一代又一代工具链的使用逻辑没变过。6.2 交叉编译的下一步学习路线如果你把 hello world 完整跑通了接下来的学习顺序我建议是这样先学会交叉编译一个带第三方依赖的简单 C 程序比如链接一个 sqlite 库再尝试交叉编译 opencv 的一个精简模块然后再碰 RKNN 的 C API。每一步都是在加深同一个技能告诉构建系统目标平台是 aarch64、工具链在哪、依赖库在哪。这个过程会有很多坑比如 CMake 找不到交叉编译器、库的 sysroot 不匹配、链接时缺符号等等。但我可以负责任地告诉你只要 hello world 这条链路通了后面那些坑大概率只是时间问题不会是方向问题。真正劝退人的永远是第一步没迈过去就放弃。我在实际项目中还发现一个规律很多同学卡住不是卡在编译本身而是卡在不知道自己的程序运行到哪一步了。建议你在板子上学会用dmesg看内核日志、用top看进程资源占用这些调试手段配合交叉编译能解决一大半疑难杂症。说白了交叉编译只是链条的一端另一端是目标机上的运行时观察能力两手都要硬。最后分享一点个人体会我刚开始折腾时也试过直接在板子上 make -j4看着温度飙到 80 多度、风扇狂转最后编译失败整个人差点崩掉。后来老老实实从 hello world 开始跑交叉编译才发现原来这条路通畅得多也轻松得多。hello world 虽小但它像是 PC 和板子之间的一次握手握成功了后面 opencv、rknn 这些大块头你才有信心继续往下碰。建议你现在就把 hello 和 hello_static 都跑一遍感受一下动态和静态的差异再顺手把 file、readelf、ldd 这三个命令在板子上都过一遍。这些基本功远比背几个编译参数值钱得多。
返回列表