1. 为什么非得交叉编译直接在香橙派上写代码不行吗先抛出这个疑问是因为我刚接触RK3588的时候也天真地想过香橙派5都跑着完整的Ubuntu 20.04系统了CPU也是四核A76加四核A55性能并不弱我干嘛还要在电脑上费劲搞交叉编译把编译好的程序再传过去呢直接在板子上装个gcc写代码、编译、运行一气呵成它不香吗这个想法在当时看确实合理尤其RK3588这种旗舰级芯片性能比树莓派4B强出一大截甚至比很多老旧x86电脑还快。但随着我真正开始在板子上部署yolov5s模型、处理视频流、调优NPU推理我很快发现直接在板子上编译只是能跑但绝不是最优解。先说最直观的痛点时间。香橙派5的A76核心确实不弱但跟现代桌面级的x86处理器相比单核性能仍有差距而且嵌入式平台的散热和功耗摆在那里长时间高负载编译会让板子烫得吓人。我实测过一个中等体量的C项目在香橙派5上直接编译需要二十分钟的话在电脑的交叉编译环境里可能只需要三四分钟。当项目进入改一行代码就要重新验证的迭代阶段这十几分钟的差距会被无限放大累积成巨大的时间成本。再谈依赖管理。yolov5s部署到RK3588上涉及OpenCV、自定义算子、模型后处理等一系列组件这些依赖编译起来一个比一个麻烦。如果都放到板子上编译不仅时间翻倍而且一旦系统环境被搞乱重装依赖的成本极高甚至可能需要重新烧写系统。交叉编译把编译环境和运行环境彻底分开编译这活儿在电脑上干得又快又清爽板子上只负责运行出了问题也好排查。最后也是这一系列教程最核心的原因后续我们要做的yolov5s RKNN模型转换、NPU推理程序很多工具链天生就是为x86宿主机构建的。你要在板子上完成整套工作流路径会异常崎岖。与其等到那个阶段再手忙脚乱切环境不如现在就从最基础的hello程序开始把交叉编译这套流程彻底跑通。这就是我把交叉编译hello放在整个系列第6篇的原因——它是后续所有部署工作的地基这块地基不夯实后面盖什么楼都摇摇晃晃。2. 交叉编译到底做了什么先弄懂宿主和目标的关系在我们动手敲命令之前我强烈建议多花两分钟理解一下交叉编译的本质。这不是枯燥的理论而是能帮你少踩无数坑的核心概念。编译器做的事情是把人类可读的源代码翻译成机器能执行的机器码。而机器这个词决定了编译这个动作的目标。通常我们所说的本机编译也叫native编译是指编译生成的程序运行在和编译器相同的CPU架构和操作系统上。比如你在自己的x86_64电脑上编译一个Linux程序然后在这个电脑上运行这就是native编译。交叉编译的本质是在A平台上编译出只能在B平台上运行的程序。这里只能二字是关键——你电脑上是x86_64的CPU香橙派5上是ARM架构的aarch64 CPURK3588使用的ARMv8.2-A 64位指令集这两种CPU的机器码完全不同互相不认识。你在电脑上编译出的普通二进制程序放到香橙派上会直接报cannot execute binary file: Exec format error反过来也是一样。打个比方可能更好理解。你写了一份中文说明书源代码现在要发给一个只看得懂英文的读者RK3588。交叉编译就是你拿着这份中文说明书在办公室里用一本英汉字典把它翻译成英文版再把英文版寄出去。你在办公室x86电脑里做的事和读者在远方ARM板子读到的文字是两套完全不同的编码系统但内容是一致的。这引出了两个关键要素第一系统架构。这是最根本的差异。x86_64和aarch64是两种不同的CPU指令集机器码的编码规则、寄存器组织、函数调用约定都完全不同。编译工具链必须根据目标CPU架构生成相应的机器码。第二操作系统和C标准库。香橙派5跑的是Ubuntu 20.04这是一个基于Linux内核的系统它使用glibc作为C标准库。如果你的交叉编译工具链用的是musl libc另一种C标准库或者目标系统是一个使用不同libc版本的发行版就会出现动态链接库不匹配的问题——最常见的现象是程序拷贝到板子上以后运行时报error while loading shared libraries。对于RK3588这种运行Ubuntu系统的平台我们最省心的选择是使用aarch64-linux-gnu工具链。这里的aarch64是目标的机器架构linux是目标操作系统gnu表示使用的C标准库是glibc工具集。这里有一个非常容易混淆的概念你电脑上装的Ubuntu系统和板子上的Ubuntu 20.04虽然名字都叫Ubuntu但它们不是同一个世界。x86_64的Ubuntu软件仓库里存放的是x86_64架构的二进制包aarch64的Ubuntu软件仓库里存放的是aarch64架构的二进制包。交叉编译工具链本身就是让你在x86_64的世界里能够制造出属于aarch64世界的东西。3. 工具链的选型与安装这一步决定你后面顺不顺理解了宿主和目标的概念之后我们就可以开始准备工具链了。工具链这个词听起来很高端其实核心就是三样东西编译器把C代码变成机器码、汇编器把汇编代码变成机器码、链接器把多个编译产物合并成最终的可执行文件。在Linux环境下这套组合的常见形态就是gcc加上binutils我们统称为交叉编译工具链。3.1 两条路发行版自带工具链 vs 厂商SDK工具链对于RK3588平台你面前有两条路我建议先把两条路都搞清楚再根据自己情况选。第一条路直接用Ubuntu软件仓库里现成的工具链。这是最省事的方案sudo apt update sudo apt install gcc-aarch64-linux-gnu安装完成后系统中就会出现aarch64-linux-gnu-gcc、aarch64-linux-gnu-ld、aarch64-linux-gnu-objdump等一系列命令。这是Linaro公司维护的、Ubuntu官方收录的ARM交叉编译工具链成熟稳定对于编译hello、编译OpenCV、编译yolov5s后处理这种通用代码完全够用。第二条路下载Rockchip官方SDK里带有的工具链。瑞芯微在发布SDK的时候会在sdk/tools/linux/toolchain/目录下附带一套预编译好的交叉编译工具链通常是gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu或者类似命名的压缩包。解压后配置PATH环境变量即可使用。这两条路怎么选我给一个实际的经验判断如果你只是在这块板子上做常规的Linux应用开发、跑跑yolov5s推理用apt安装的工具链就够了。打个比方这两种工具链的差别相当于一个是专业运动跑车一个是普通家用轿车你要体验的是GT跑车才能带来的快感比如调用处理器厂商特有的编译优化指令才需要考虑厂商SDK工具链如果你的目标是写hello、写应用程序、跑模型推理家用轿车完全能满足需求而且鲁棒性反而更好——因为它有完整的Ubuntu环境支持和依赖管理。但如果你是冲着调试底层、裁剪系统、开发内核模块去的那就必须切换到SDK工具链或者自己构建工具链了。因为内核和驱动对编译器版本比较敏感发行版工具链的版本可能跟瑞芯微发布时的验证版本不一致容易碰见莫名其妙的编译错误。这是我们做上层应用开发暂时不需要操心的。我个人的选择是这一系列教程后续涉及的所有编译操作都统一用apt工具链。为什么因为博客要服务绝大多数读者你们中很多人可能是第一次接触交叉编译使用官方仓库工具链能保证同样的命令、同样的环境配置最后能得到同样的结果最大程度排除干扰因素。我先用最简单的方案把这个hello跑通这里面涉及的知识点、流程和坑跟用厂商SDK工具链是完全一致的。3.2 验证工具链安装是否成功工具链装好之后先别急着写代码做一次简单的验证aarch64-linux-gnu-gcc --version正常会输出类似这样的信息aarch64-linux-gnu-gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.看到版本信息说明编译器本体没问题。但你最好再验证一下它能不能正确地认出目标架构echo int main() { return 0; } | aarch64-linux-gnu-gcc -xc - -o /tmp/test_arch file /tmp/test_archfile命令会告诉你这个文件的真实类型正常输出应该是/tmp/test_arch: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, ...看到ARM aarch64这一行就说明你的工具链已经正确地把代码编译成了ARM 64位架构的机器码。这一步非常重要因为很多人工具链装了半天结果编译出来还是x86_64架构的程序拷到板子上当然是跑不起来的。提示如果file命令输出的不是ARM aarch64先检查你是不是误用了x86_64的gcc。这种情况新手经常发生——在PATH环境变量的优先级里系统自带的gcc排在交叉工具链前面导致你敲了gcc而不是aarch64-linux-gnu-gcc编译出来自然还是本机架构。我建议在实际项目中用哪个工具链就全程明确写全命令不要依赖系统自动选择。3.3 关于工具链版本的潜在问题用apt安装的工具链版本跟你电脑上Ubuntu系统的版本有关。比如Ubuntu 22.04装的是gcc 11.xUbuntu 20.04装的是gcc 9.x。这里有个重要的点编译出的程序对运行环境的glibc版本有一个最低要求。你在电脑上用的gcc 11默认链接的是电脑上的glibc 2.35如果你用的是Ubuntu 22.04而香橙派5上烧写的Ubuntu 20.04的glibc版本是2.31。如果你编译时用了比较新的glibc特性拷贝到板子上可能报GLIBC_2.34 not found之类的错误。这个问题的本质是程序的动态链接器在加载时会检查每个动态库依赖的符号版本板子上的glibc版本低于程序需要的最低版本就会拒绝加载。解决方法有三类编译时加-static参数采用静态链接让程序不依赖板子上的任何动态库。这是最暴力的方法但程序体积会变大很多。编译时加-Wl,--hash-stylesysv等参数这些是老技巧了帮助有限。最稳妥安装一个与目标板子系统glibc版本匹配的交叉工具链比如在Ubuntu 22.04上专门用gcc-aarch64-linux-gnu的gcc 9版本通过update-alternatives切换或者干脆用厂商SDK的工具链。对于hello程序我们不存在这种问题因为hello本身用到的glibc函数都是最基础的任何glibc版本都有。但这个陷阱在后续编译OpenCV等大项目时会真实遇见先在这里打个预防针。4. 第一个交叉编译实战从hello.c到香橙派运行现在工具链已经就绪我们正式进入实操环节。这篇文章的标题是交叉编译hello但hello只是载体真正要掌握的是从在电脑上写代码到在板子上运行这整条流水线。我会用非常啰嗦的方式一步一步走完整个流程。4.1 编写hello.c首先在你的电脑上建一个工作目录我习惯用~/rk3588_workspacemkdir -p ~/rk3588_workspace/hello cd ~/rk3588_workspace/hello用你顺手的编辑器创建hello.c#include stdio.h int main(void) { printf(Hello from Orange Pi 5 (RK3588)!\n); printf(Cross-compilation works!\n); return 0; }这段代码非常简单但它具备了验证交叉编译环境所需要的全部要素stdio.h头文件的查找、printf函数的链接调用、可执行文件的生成。如果这套流程能跑通后面编译任何C/C程序都只是增加代码复杂度的问题而不再是流程问题。4.2 编译并分析产物用交叉编译器编译这个文件aarch64-linux-gnu-gcc hello.c -o hello_arm编译完成后用file命令检查file hello_arm输出hello_arm: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]f2c1d9..., for GNU/Linux 3.7.0, not stripped看到关键信息了吗ELF 64-bit LSB pie executable, ARM aarch64——这是ARM 64位架构的可执行文件这正是我们要的。接下来看一个容易忽略的细节dynamically linked, interpreter /lib/ld-linux-aarch64.so.1。这说明你编译出来的是一个动态链接的可执行文件它运行的时候需要一个叫ld-linux-aarch64.so.1的动态链接器来加载它还需要链接到目标系统上的glibc动态库。这是什么意思呢这个hello_arm不能独立运行它依赖香橙派板子系统上的glibc库文件。好在香橙派5跑的是完整的Ubuntu 20.04系统glibc是系统标配所以正常情况下你的程序拷过去就能运行。但正常情况下这四个字很有分量。如果你把板子换成一个精简的嵌入式Linux系统比如Buildroot或者Yocto裁剪出来的rootfs这些系统可能把glibc的dev包裁剪掉了或者用了不同版本的库那么你的动态链接程序就可能因为找不到.so文件或找不到某个符号而无法启动。从这个角度说动态链接是双刃剑它让程序体积小巧但增加了对运行环境库版本的依赖。与之对应的我们再试一次静态编译aarch64-linux-gnu-gcc -static hello.c -o hello_staticfile查看hello_static: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, for GNU/Linux 3.7.0, not stripped注意看输出从dynamically linked变成了statically linked而且没有interpreter字段了。这个程序的体积也会大很多ls -lh hello_arm hello_static你自己试试大概率hello_static的体积是hello_arm的几十倍。这是因为静态链接把所有用到的C库函数都打包进了你自己的可执行文件里不需要依赖板子上任何库就能独立运行。4.3 动态与静态怎么选这个选择在交叉编译里是最经典的问题也是第一次接触的读者最容易纠结的地方。我给你一个可以直接抄作业的判断逻辑如果是给自己调试用的小工具、测试程序用动态链接就够了体积小编译快拷到板子上直接跑。板子上是完整体Ubuntu系统动态库齐全绝大多数情况下无障碍。如果是要移植到裁剪过的嵌入式系统果断用静态链接。因为你不知道目标系统里到底有哪些库、版本是否兼容静态链接一了百了能跑就是能跑不能跑就是工具链本身的问题排查范围大大缩小。如果是大型项目、涉及OpenCV等重量级依赖不要用静态链接。你以为静态链接就高枕无忧它会把所有依赖都塞进可执行文件体积可能膨胀到几百MB而且如果目标板上还要加载你编译的动态库插件的化两者映射地址可能冲突。大型项目推荐使用动态链接但要把目标板上的依赖库版本和编译机的依赖版本对上。对于本系列后续的yolov5s部署工程我明确告诉你动态链接是主流做法。因为RKNN的NPU推理库、OpenCV库都会是以动态库的形式放在板子上我们编译的目标程序只要依赖这些库即可没必要也没办法把它们全静态进一个文件里。但那是后话现在只要理解了两种方式的区别到时候自然知道怎么选。4.4 把程序传到香橙派5上编译产物放在电脑上它还是一堆没有生命力的文件。你要把它弄到香橙派5上去运行这一步很多人也会卡一下。我介绍三种常用的方法你自己挑顺手的。方法一scp命令TCP网络传输最常用前提是香橙派5和你在同一个局域网内并且你已经知道板子的IP地址。假设板子的IP是192.168.1.100用户名是orangepi在电脑上执行scp hello_arm orangepi192.168.1.100:/home/orangepi/系统会提示你输入密码输入板子上对应用户的密码即可。文件传输完成后在板子上就能看到这个文件。我强烈推荐这个方式因为它一次传输多个文件也很方便而且在后续开发中你要频繁地把编译好的程序或者模型文件拷到板子上scp是你需要熟练掌握的第一板斧。方法二U盘拷贝如果你手边没有网络环境或者板子还没联网U盘是最直接的手段。把U盘格式化成FAT32Windows和Linux都能识别把hello_arm文件拷到U盘里插到香橙派5的USB口上然后挂载sudo mount /dev/sda1 /mnt cp /mnt/hello_arm /home/orangepi/ sudo umount /mnt这里要注意U盘的设备节点不一定是/dev/sda1你可以在插入U盘后执行lsblk查看新增的设备节点。而且拔出U盘前务必先umount否则有损坏文件系统的风险。方法三NFS网络挂载适合频繁调试的场景如果你会进入编译-拷贝-运行-改代码的高速循环每次都用scp会觉得烦那NFSNetwork File System网络文件系统是更好的选择。它的原理是在电脑上共享一个目录板子上把这个目录挂载到本地路径然后两边看到的是同一份文件不需要拷贝。这样你每次编译完板子上看到的直接就是新文件。配置NFS稍微有点门槛需要先在电脑上安装NFS服务端sudo apt install nfs-kernel-server编辑/etc/exports加入共享目录配置/home/用户名/rk3588_workspace *(rw,sync,no_subtree_check,no_root_squash)重启NFS服务然后在板子上执行sudo apt install nfs-common sudo mount -t nfs 192.168.1.100:/home/用户名/rk3588_workspace /mnt这个以后用熟了会非常高效。如果你预计自己要在RK3588上折腾很久我建议尽早把NFS配置好省下来的时间相当可观。4.5 在香橙派上运行hello程序文件到了板子上之后先别急着运行。按照我习惯的流程先检查权限chmod x hello_arm然后直接运行./hello_arm看到输出Hello from Orange Pi 5 (RK3588)! Cross-compilation works!到这里你的交叉编译hello教程就算是100%跑通了。但你敲完这两行输出哇成功了之后应该再往前走一步验证一下这个程序确实是运行在RK3588的ARM架构上而不是板子偷偷用某种模拟机制跑了个x86程序虽然这不太可能。可以用下面这两条命令打配合uname -m输出应该是aarch64这表示你的系统内核运行在ARM 64位架构上。readelf -h hello_arm | grep Machine输出应该是Machine: AArch64这两者一致就说明你的交叉编译产物和你的运行平台是同一架构整个世界是自洽的。5. 实操中最常见的坑依赖问题、架构混用和权限迷局如果你前面的流程一次通过恭喜你运气很好。但以我带新人的经验90%的人第一次走这个过程会在几个相似的坑里摔跤。我把最高频的几个问题集中在这里方便你踩坑的时候快速索引。5.1 动态链接程序的file和报错陷阱你花了五分钟编译出一个hello_arm高高兴兴拷到板子上运行却得到-bash: ./hello_arm: cannot execute binary file: Exec format error看到这个报错第一反应应该是用file命令检查这个文件。大概率你会看到输出里写的是ELF 64-bit LSB pie executable, x86-64而不是ARM aarch64。这意味着你在编译的时候实际上用的是电脑本机的gcc而不是交叉编译工具链。为什么会出现这个问题最常见的原因有两个第一命令敲错了。你以为自己用的是aarch64-linux-gnu-gcc但系统里其实没装这个工具链于是bash用gcc顶上执行了编译。可以用which aarch64-linux-gnu-gcc检查工具链的路径。第二Makefile的锅。如果你用Makefile管理编译流程Makefile里写的是CC gcc那你当然是在用本机编译器。交叉编译的正确姿势是CC aarch64-linux-gnu-gcc或者在make命令行里覆盖make CCaarch64-linux-gnu-gcc这个坑在我编译OpenCV的时候也非常普遍——OpenCV的CMake配置需要显式指定CMAKE_C_COMPILER和CMAKE_CXX_COMPILER不指定或者指定错就会出现在x86环境下编译出x86库的尴尬局面。5.2 动态依赖缺失的问题程序能传过去、能赋予执行权限但运行时报./hello_arm: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory这种情形多数出现在板子系统是精简版或者非Ubuntu官方镜像的时候。如果你的板子刷的是完整版Ubuntu 20.04遇到这个问题的概率极低但万一遇到了可以用ldd命令查看程序依赖了哪些共享库ldd hello_armldd会列出所有依赖的共享库并标注哪些能找到、哪些找不到。如果是.so版本的冲突比如编译时链接的是libc.so.6但板子上只有libc.so.6的某个子版本没有符号兼容性那可能就要考虑静态编译绕过去或者换一个和目标系统glibc版本更加匹配的工具链。这里有一个可能被忽略的细节aarch64-linux-gnu-gcc的默认搜索路径是编译器安装目录下的aarch64-linux-gnu目录它会优先找到该目录下的头文件和库而不是你系统里的x86_64文件。如果你在编译时手动指定了-I或-L指向某些x86_64的头文件或库编译出来的东西当然不能运行在ARM架构上。这种问题我在交叉编译SQLite时真实踩过——电脑上同时装了x86版的SQLite开发包CMake自动找到了它结果编译产物在板子上怎么都跑不起来。5.3 权限问题把程序传输到板子上后可能遇到Permission denied的报错。这时候先检查执行权限ls -l hello_arm如果输出里没有x权限执行chmod x hello_arm为什么scp传过去会丢执行权限这与scp的实现有关。scp在传输时默认会保留原文件的权限位但如果你在电脑上编译时文件本身就没有执行权限某些编译器配置下产出的可执行文件默认不带x权限那传到板子上自然也没有。养成传输后立即chmod x的习惯是好的。另外如果你把程序放在/home之外的系统目录下还得检查你对这个目录是否有写和执行权限。我建议统一把程序放在home目录或者/opt下面权限管理最简单。5.4 架构判断错误armv7与aarch64的混淆这个坑是给那些手上有不止一块ARM板子的读者的。不同ARM板子之间架构差异很大。树莓派4B是Cortex-A72是aarch64香橙派Zero 2是Allwinner H616也是aarch64但香橙派Zero第一代用的是Allwinner H2那是armv7架构32位。如果你针对armv7编译了一个程序拿到aarch64的板子上运行同样会报Exec format error。怎么避免编译之前先想清楚目标平台的架构用uname -m确认。RK3588是明确的aarch64不存在混用问题但当你在网上查找资料、复制别人的编译命令时要特别留意对方的目标平台是不是和你一致。网上一搜交叉编译满屏的树莓派教程很多是针对armv7或者armv6的你照着抄就完蛋了。遇到这种教程先看对方的-march参数再看内核版本最后才看编译命令本身。6. Makefile的交叉编译配置与自动化hello.c这种单文件项目直接用命令行编译当然可以但当你开始做yolov5s部署这种多文件项目时让编译器在命令行里一个个列出源文件是很痛苦的事。从第一个项目开始养成用Makefile的习惯后面会省非常多力气。6.1 一个适合交叉编译的hello Makefile在hello目录下创建一个MakefileCROSS_COMPILE aarch64-linux-gnu- CC $(CROSS_COMPILE)gcc CFLAGS -Wall -O2 TARGET hello_arm OBJS hello.o all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) hello.o: hello.c $(CC) $(CFLAGS) -c hello.c clean: rm -f $(TARGET) $(OBJS) .PHONY: all clean这里的关键在于CROSS_COMPILE和CC的写法。使用CROSS_COMPILE aarch64-linux-gnu-作为前缀后面你换工具链的时候只需要改这一行。比如你想用厂商SDK工具链可能只需要改成/path/to/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-其余代码一行都不用动。这是Linux内核和U-Boot的Makefile常用的风格学它不会错。编译和清理make clean makemake执行完毕后在目录下应该能看到hello_arm。这里的-Wall会显示所有警告对新手很友好-O2是二级优化对于hello这种小程序无所谓但对后续的算法代码很重要因为优化级别直接决定了程序的运行速度和体积。6.2 把编译器前缀抽离成环境变量如果你有多个项目都要交叉编译每个Makefile都写死aarch64-linux-gnu-会有点烦。更灵活的做法是把编译器前缀通过环境变量传入CC $(CROSS_COMPILE)gcc然后在命令行执行make CROSS_COMPILEaarch64-linux-gnu-这样你只要在这一条命令里定义一次前缀Makefile内部会自动拼出完整的编译器名。这种方式在实际项目中用得非常多尤其是要同时维护多个平台香橙派5、树莓派5等的构建脚本时你只需要写一套Makefile然后为每个平台准备一个构建脚本脚本里定义各自的CROSS_COMPILE就行。6.3 自动判断架构的编译技巧有时候你会想写一个Makefile让它在x86环境上编译出本机程序在交叉编译环境上编译出ARM程序。利用uname -m可以做到ARCH : $(shell uname -m) ifeq ($(ARCH), aarch64) CC gcc else CC aarch64-linux-gnu-gcc endif这段逻辑的意思是如果当前机器本身就是aarch64架构也就是你直接在香橙派上编译就用本机gcc如果是在x86电脑上就用交叉编译器。这个技巧在你偶尔会在板子上直接编译小工具调试时很好用但不建议大肆使用因为它会让构建流程变得不那么透明——你很难一眼看清这次构建到底用的是哪个编译器。7. 交叉编译与后续yolov5s部署的衔接思路这一篇的hello程序跑通之后有人可能会问折腾了半天的交叉编译和标题里的yolov5s到底有什么关系其实关系比你想的紧密得多。我说说我的规划也算给这个系列做个承上启下。我们在香橙派5上部署yolov5s整体路径是加载RKNN模型并进行推理。模型本身不是C代码它是通过瑞芯微的RKNN-Toolkit工具在x86电脑上将PyTorch的yolov5s模型转换而来的rknn格式文件。这一部分在电脑上完成不涉及交叉编译——更像是模型文件的格式转换和量化压缩。但真的要让它跑起来你需要一个C/C程序来调用RKNN Runtime API加载这个.rknn模型文件对输入图像做预处理缩放、归一化交给NPU做推理再从输出张量里解析出检测框坐标、置信度和类别。这个程序就是要在电脑上交叉编译然后放到板子上运行。它与我们刚才编译hello_arm的流程一模一样只不过源文件从一段printf代码变成了一整套目标检测程序链接的库从glibc变成了librknnmrt.so和OpenCV。编译yolov5s推理程序的步骤也会类似在电脑上安装交叉编译版的OpenCV或者把头文件和库文件准备好。用aarch64-linux-gnu-gcc编译你的推理源码。把编译好的程序和.rknn模型文件一起传到板子上。在板子上运行时确保librknnmrt.so在板子的库搜索路径里。所以说现在这个hello程序并非孤立的玩具它是你构建整条部署链路的第一个里程碑。你现在掌握的流程包括工具链选择、动态库依赖分析、文件传输、Makefile配置在后边的所有步骤里都是每天都在用的基本功。尤其是依赖处理可以现在就拿ldd多多练手。你可以交叉编译一个OpenCV测试程序用ldd看看它依赖了哪些库然后把这些库一并传到板子上。这个过程走通之后你再编译yolov5s推理程序的时候心里会非常有底——你知道每一个.so文件是从哪来的知道板子上缺什么知道怎么补这才是真正的能跑起来和会跑起来的区别。我个人在实际操作中的体会是交叉编译最容易出问题的环节从来不是编译本身——编译失败编译器会明确告诉你哪里错了改一行代码就行。真正折磨人的是运行时问题程序编译成功了传过去了但板子上一跑就崩或者根本起不来而且错误信息含糊。这时候你之前对工具链选型、动态库依赖、架构一致性这些基础点的理解深度就直接决定了你能多快把问题定位出来。所以哪怕你现在只是跑通了一个hello也值得抽出时间理解背后这套机制后面遇到真正的部署项目时你会发现这笔时间花得太值了。最后再分享一个实用习惯建立一个专门的Makefile模板文件和构建脚本把工具链、编译参数、输出目录都固化下来。别小看这件事在这系列教程进行到后面项目文件越来越多交叉编译和板端调试反复切换一套趁手的构建体系能帮你节省大量时间。我也会在后续的文章里把yolov5s推理程序的完整Makefile分享出来但那是基于你已经掌握本篇文章内容的前提。所以把这个hello跑通并理解透彻是我们一起去往RK3588上跑起yolov5s这条路上的第一步走稳它后续就都是水到渠成的事了。