ARTICLE DETAIL

资讯详情

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

RK3588交叉编译实战:从hello world到YOLOv5s部署

RK3588交叉编译实战:从hello world到YOLOv5s部署 1. 为什么交叉编译是RK3588开发绕不开的第一道坎搞香橙派5的兄弟大概率都有过这样的经历板子上跑着Ubuntu或者Debian想编译一个最简单的C程序测试环境结果发现板子自带的gcc编译一个hello world都要等好几秒稍微大一点的工程直接卡到怀疑人生。这不是板子性能不行RK3588这颗芯片本身是8核A76A55的架构性能放在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 11glibc是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字段应该是AArch64Class是ELF64。3.3 传输到板子的三种方式编译出来的hello文件要传到板子上执行有三种常用方式。第一种是scp前提是板子和电脑在同一个局域网而且板子开了SSH服务。香橙派5默认的Ubuntu镜像一般已经装了SSH如果没有在板子上执行sudo apt install openssh-server。传输命令scp hello orangepi192.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 11glibc 2.31就用gcc-arm-10.2或者更早的版本二是用-static静态编译把glibc打包进去。怎么查板子的glibc版本ldd --version输出第一行就是glibc版本号。怎么查工具链的glibc版本aarch64-none-linux-gnu-gcc -print-file-namelibc.so.6 # 然后用readelf查看 aarch64-none-linux-gnu-readelf -a /path/to/libc.so.6 | grep GLIBC4.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或者内核头文件版本高于板子实际内核版本。排查步骤可以按这个顺序来file hello确认架构chmod x hello确认权限ldd hello查看动态库依赖如果是动态链接uname -a查看板子内核版本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 hellocannot 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程序用了C17的特性记得把编译器换成aarch64-none-linux-gnu-g并且加上-stdc17参数。链接时如果用到数学库还要加-lm。交叉编译工具链的安装路径建议记下来后面写CMakeLists.txt或者Makefile的时候要用。我一般会在项目根目录放一个toolchain.cmake文件把编译器路径、sysroot路径、编译参数都写进去这样用CMake构建的时候直接-DCMAKE_TOOLCHAIN_FILEtoolchain.cmake就行不用每次手动敲一长串参数。这个hello程序虽然简单但它验证了从x86到ARM的完整工具链。后面不管编译什么程序流程都是一样的写代码、交叉编译、传输、板端执行。把这一步走稳了后面的YOLOv5s部署就是水到渠成的事。
返回列表