☰
RK3588交叉编译实战:从hello world到YOLOv5s部署
2026/10/5 7:07:13 网站建设 项目流程

1. 为什么交叉编译是RK3588开发绕不开的第一道坎

搞香橙派5的兄弟大概率都有过这样的经历:板子上跑着Ubuntu或者Debian,想编译一个最简单的C程序测试环境,结果发现板子自带的gcc编译一个hello world都要等好几秒,稍微大一点的工程直接卡到怀疑人生。这不是板子性能不行,RK3588这颗芯片本身是8核A76+A55的架构,性能放在ARM开发板里算是第一梯队了,问题出在存储和散热上——你插的那张TF卡或者eMMC的随机读写速度,跟PC上的NVMe固态完全不是一个量级,编译过程中大量的小文件读写会把IO打满,CPU反而在等IO。

交叉编译就是解决这个问题的核心手段。简单说,就是在你的x86_64电脑上,用一套专门为ARM架构准备的编译器,把代码编译成能在RK3588上运行的二进制文件,然后通过scp或者U盘拷到板子上执行。这样编译速度取决于你PC的性能,拷贝一个几KB的hello可执行文件也就是一瞬间的事。

这一节是整套教程的第六篇,前面几篇应该已经带着大家把香橙派5的系统烧录、串口登录、网络配置这些基础工作做完了。现在到了真正写代码的环节,我选择从hello world开始,不是因为它简单,而是因为它能把交叉编译的完整链路跑通——工具链安装、环境变量配置、编译参数指定、文件传输、板端执行,每一步都走一遍,后面编译YOLOv5s的推理程序时就不会手忙脚乱。

提示:交叉编译工具链的选择非常关键,选错了后面全是坑。香橙派官方和Rockchip原厂都提供了工具链,但版本和配置方式有差异,下面会详细对比。

适合阅读这篇内容的人:刚拿到香橙派5、已经能正常登录系统、准备开始写C/C++程序或者部署AI模型的开发者。如果你连板子还没点亮,建议先回去看前面的系统烧录教程。

2. 交叉编译工具链的选型与安装

2.1 三种主流工具链的对比

给RK3588做交叉编译,市面上能用的工具链主要有三类,我分别用过,踩过的坑不太一样。

第一类是Rockchip原厂SDK里自带的工具链,通常在SDK的prebuilts/gcc/linux-x86/aarch64/目录下,名字类似gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu。这套工具链跟RK3588的BSP是配套的,glibc版本、内核头文件都是对齐的,理论上兼容性最好。但问题是它藏在SDK里,如果你没下载完整的SDK,单独去搞这套工具链比较麻烦。

第二类是Linaro或者ARM官方发布的GNU工具链,比如gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu,这个跟Rockchip用的其实是同一个版本。ARM官方每季度会更新,下载方便,文档齐全。我目前用的就是这个,稳定性和兼容性都经过验证。

第三类是香橙派官方Wiki里推荐的工具链,有时候会指向一个特定的下载链接。香橙派官方文档更新频率一般,链接失效是常有的事,而且不同批次的板子可能预装不同的系统版本,工具链的glibc版本要对上。

工具链来源优点缺点推荐场景
Rockchip SDK自带与BSP完全配套需要下载完整SDK,体积大深度定制系统、编译内核驱动
ARM官方GNU工具链下载方便、版本清晰、文档全需要自己确认glibc兼容性应用层开发、AI模型部署
香橙派官方推荐官方验证过链接易失效、更新慢新手按官方教程走

我的建议是:如果你只是做应用层开发,比如编译YOLOv5s的推理程序、写一些测试工具,直接用ARM官方的GNU工具链就行。如果你要编译内核模块或者修改系统底层,那必须用Rockchip SDK自带的,否则内核版本对不上会出各种诡异问题。

2.2 下载与解压的实操细节

我以ARM官方10.3版本为例,这个版本对应的是glibc 2.33,香橙派5默认的Ubuntu 22.04系统glibc版本是2.35,向下兼容没问题。如果你用的是Debian 11,glibc是2.31,那就要注意了,用10.3编译出来的程序在Debian 11上可能跑不起来,因为依赖的glibc版本比系统自带的还高。

下载地址去ARM开发者官网找,搜索“GNU Toolchain for AArch64”,找到gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz这个文件。文件大概100多MB,下载完先校验一下MD5,避免下载过程中损坏。

# 下载完成后校验 md5sum gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz # 对比官网提供的MD5值

解压到一个你习惯放工具的目录,我一般放在/opt/toolchain/下面:

sudo mkdir -p /opt/toolchain sudo tar -xvf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz -C /opt/toolchain/

解压完检查一下目录结构,确认bin目录下有aarch64-none-linux-gnu-gcc这个可执行文件:

ls /opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/

你应该能看到aarch64-none-linux-gnu-gcc、aarch64-none-linux-gnu-g++、aarch64-none-linux-gnu-ld这些文件。如果没有,说明解压不完整,重新下载。

2.3 环境变量配置的两种方式

配置环境变量有两种方式,临时生效和永久生效。临时生效就是直接在终端里export,关掉终端就没了,适合测试:

export PATH=/opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH

永久生效要写进~/.bashrc或者/etc/profile。我推荐写进~/.bashrc,只对当前用户生效,不会影响系统里其他用户。在文件末尾追加:

echo 'export PATH=/opt/toolchain/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH' >> ~/.bashrc source ~/.bashrc

验证是否配置成功:

aarch64-none-linux-gnu-gcc -v

如果输出了gcc的版本信息,说明配置成功。如果提示command not found,检查路径是不是写错了,或者用绝对路径试试。

注意:有些教程会让你把工具链的bin目录直接加到PATH最前面,这没问题,但如果你系统里已经装了其他交叉编译工具链,比如arm-linux-gnueabihf,那PATH的顺序就很重要了。建议用which aarch64-none-linux-gnu-gcc确认一下实际调用的是哪个。

3. 从hello.c到板端运行的完整实操

3.1 编写一个“不那么简单”的hello程序

很多人觉得hello world太简单,随便写两行就行。但我要用这个程序验证的东西不少:编译器能不能正常工作、标准库链接有没有问题、生成的可执行文件架构对不对、板子上能不能跑起来。所以我写的hello.c会稍微加一点东西:

#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { printf("Hello, Orange Pi 5!\n"); printf("This binary is compiled for AArch64.\n"); printf("argc = %d\n", argc); for (int i = 0; i < argc; i++) { printf("argv[%d] = %s\n", i, argv[i]); } return 0; }

加打印参数个数和参数内容,是为了验证程序在板子上运行时能正常接收命令行参数,后面调试YOLOv5s的时候经常需要传模型路径、图片路径这些参数,提前验证一下没坏处。

3.2 编译命令的每个参数都要说清楚

编译命令看起来简单,但每个参数都有讲究:

aarch64-none-linux-gnu-gcc hello.c -o hello -static

-o hello指定输出文件名,这个不用多说。关键是-static这个参数,我强烈建议加上。动态链接的话,板子上必须有对应的动态库,而且版本要匹配。你编译时用的工具链glibc是2.33,板子上是2.35,动态链接可能没问题,但如果是反过来,板子上是2.31,那就会报GLIBC_2.33 not found的错误。静态链接把所有依赖都打包进可执行文件,体积会大一些,但省去了库版本匹配的麻烦。

编译完检查一下生成的文件:

file hello

输出应该是类似这样的:

hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, for GNU/Linux 3.7.0, not stripped

重点看ARM aarch64和statically linked这两个信息。如果是x86-64,说明你调用的还是系统自带的gcc,不是交叉编译器,检查PATH。如果是dynamically linked,说明-static没生效,检查参数拼写。

还可以用readelf看一下更详细的信息:

aarch64-none-linux-gnu-readelf -h hello

输出里的Machine字段应该是AArch64,Class是ELF64。

3.3 传输到板子的三种方式

编译出来的hello文件要传到板子上执行,有三种常用方式。

第一种是scp,前提是板子和电脑在同一个局域网,而且板子开了SSH服务。香橙派5默认的Ubuntu镜像一般已经装了SSH,如果没有,在板子上执行sudo apt install openssh-server。传输命令:

scp hello orangepi@192.168.1.100:/home/orangepi/

把192.168.1.100换成你板子的实际IP。传输速度取决于网络,几KB的文件基本秒传。

第二种是U盘,适合板子没联网或者网络配置麻烦的情况。把hello拷到U盘,插到板子上,挂载U盘,拷贝文件。挂载命令一般是:

sudo mount /dev/sda1 /mnt cp /mnt/hello /home/orangepi/ sudo umount /mnt

第三种是通过NFS或者TFTP,适合频繁传输大量文件的场景。配置稍微麻烦一点,但一次配好后面就方便了。我平时调试YOLOv5s的时候用NFS比较多,因为模型文件和测试图片经常要换。

3.4 板端执行与权限设置

文件传到板子上之后,默认是没有执行权限的,直接运行会提示Permission denied。先加权限:

chmod +x hello

然后运行:

./hello

如果一切正常,你会看到:

Hello, Orange Pi 5! This binary is compiled for AArch64. argc = 1 argv[0] = ./hello

再试试带参数运行:

./hello test 123

输出里应该能看到argc = 3,以及argv[1] = test、argv[2] = 123。

实操心得:如果运行时报No such file or directory,但文件明明存在,大概率是动态链接器的问题。用file hello确认是不是动态链接,如果是,要么重新用-static编译,要么在板子上安装对应的库。还有一种可能是文件系统挂载时带了noexec选项,用mount | grep noexec检查一下。

4. 交叉编译常见问题与排查实录

4.1 工具链版本与系统glibc不匹配

这是最常见的问题,没有之一。现象是编译时没问题,板子上运行时报错:

./hello: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found

原因是你编译时用的工具链glibc版本高于板子系统的glibc版本。解决办法有两个:一是换用更低版本的工具链,比如板子是Debian 11(glibc 2.31),就用gcc-arm-10.2或者更早的版本;二是用-static静态编译,把glibc打包进去。

怎么查板子的glibc版本:

ldd --version

输出第一行就是glibc版本号。

怎么查工具链的glibc版本:

aarch64-none-linux-gnu-gcc -print-file-name=libc.so.6 # 然后用readelf查看 aarch64-none-linux-gnu-readelf -a /path/to/libc.so.6 | grep GLIBC

4.2 找不到头文件或库文件

编译时报fatal error: xxx.h: No such file or directory,说明工具链的sysroot里没有这个头文件。交叉编译工具链自带一套sysroot,里面是标准的C库头文件,但如果你用了第三方库,比如OpenCV、FFmpeg,就需要自己交叉编译这些库,然后把头文件和库文件路径通过-I和-L参数指定给编译器。

比如你交叉编译了OpenCV到/opt/opencv-arm64/,编译命令要写成:

aarch64-none-linux-gnu-gcc hello.c -o hello -static \ -I/opt/opencv-arm64/include \ -L/opt/opencv-arm64/lib \ -lopencv_core -lopencv_imgproc

注意-I和-L的顺序,以及-l参数要放在源文件后面,否则链接器可能找不到符号。

4.3 编译出来的程序在板子上跑不起来

除了glibc版本问题,还有几种可能。一是架构不对,用file命令确认是ARM aarch64而不是x86-64。二是文件系统权限问题,用ls -l看有没有执行权限。三是板子的内核版本太低,编译时指定的--sysroot或者内核头文件版本高于板子实际内核版本。

排查步骤可以按这个顺序来:

  1. file hello确认架构
  2. chmod +x hello确认权限
  3. ldd hello查看动态库依赖(如果是动态链接)
  4. uname -a查看板子内核版本
  5. dmesg | tail查看内核日志有没有报错

4.4 常见问题速查表

现象可能原因排查命令解决方法
command not foundPATH未配置which aarch64-none-linux-gnu-gcc重新配置环境变量
GLIBC_2.xx not foundglibc版本不匹配ldd --version换工具链或静态编译
No such file or directory动态链接器缺失file hello静态编译或安装库
Permission denied无执行权限ls -l hellochmod +x hello
cannot execute binary file架构不对file hello检查交叉编译器PATH
编译时报头文件缺失sysroot不完整find / -name xxx.h指定-I路径或交叉编译依赖库

避坑技巧:我习惯在编译完第一时间用file和readelf检查生成的文件,确认架构和链接方式正确,再传到板子上。这样能把大部分问题挡在传输之前,省得来回折腾。

5. 从hello到YOLOv5s:交叉编译的后续扩展

hello world跑通之后,交叉编译的链路就算打通了。接下来编译YOLOv5s的推理程序,本质上就是把hello.c换成更复杂的C++代码,把依赖的库从标准C库换成RKNN API和OpenCV。工具链不变,编译参数增加-I和-L指定RKNN和OpenCV的路径,链接参数增加-lrknn_api -lopencv_core这些。

我实际编译YOLOv5s推理程序时,遇到的最大问题不是编译本身,而是RKNN库的版本匹配。Rockchip的RKNN Toolkit2更新比较频繁,不同版本生成的rknn模型文件跟板子上的librknnrt.so版本必须对应,否则加载模型时会报错。这个后面讲到模型转换和部署的时候再细说。

另外,如果你要编译的C++程序用了C++17的特性,记得把编译器换成aarch64-none-linux-gnu-g++,并且加上-std=c++17参数。链接时如果用到数学库,还要加-lm。

交叉编译工具链的安装路径建议记下来,后面写CMakeLists.txt或者Makefile的时候要用。我一般会在项目根目录放一个toolchain.cmake文件,把编译器路径、sysroot路径、编译参数都写进去,这样用CMake构建的时候直接-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake就行,不用每次手动敲一长串参数。

这个hello程序虽然简单,但它验证了从x86到ARM的完整工具链。后面不管编译什么程序,流程都是一样的:写代码、交叉编译、传输、板端执行。把这一步走稳了,后面的YOLOv5s部署就是水到渠成的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询