资讯动态

RK3308交叉编译ROS完整流程与踩坑记录

发布时间:2026/10/14 17:37:50 来源:尧图企业网站定制
做机器人这行的朋友应该都有过这种经历拿到一块低功耗的板子想让它跑 ROS结果光是装环境就被存储和内存卡了个半死。我最近就在一颗 RK3308 上重新搭了一遍 ROS 运行环境把它当作一个小型底盘的控制器并且把编译工作全部搬到了 x86 主机上进行交叉编译。整个过程踩了不少坑但也把跨架构移植的套路彻底理清了。这里把完整的流程和踩坑记录写出来涵盖 rootfs/sysroot 准备、工具链选择、CMake 交叉文件、catkin 工作区处理以及部署到 RK3308 之后的运行细节。做同类嵌入式 ROS 开发的朋友可以直接对照着操作。1. 项目概述与整体技术路线1.1 为什么在 RK3308 上跑 ROS 需要提前规划RK3308 是一颗四核 Cortex-A35 的低功耗应用处理器常见场合是语音交互、边缘摄像、简单工业控制很少有朋友直接把它和 ROS 联想到一起。但用来跑轻量级 ROS 节点其实很合适比如读取 IMU、收发串口、做简单的运动学换算。这类任务占用不高板子长时间低功耗运行也没压力。不过真要去跑rviz、点云可视化那套重负载就得做好心理准备。ROS 本身不是单文件程序它有roscpp、消息生成工具、Python 绑定等一系列依赖。对一个 512MB 内存的 RK3308 板子来说常见的桌面版 Linux 配合完整 ROS 完全不合适哪怕是精简的 ros-base也要检查剩余内存和 flash。我给这块板子的规划是“底盘控制器”只跑roscore和几个自己写的驱动节点。这和标题里的“运行环境搭建”很贴合先有一个能常驻运行 ROS 的根目录再去裁剪和交叉编译我们需要的软件。1.2 板载编译与交叉编译怎么选很多朋友第一反应是直接在板子上apt installROS然后catkin_make自己的工作区这不是最自然的吗如果编译的包很小板载编译完全没问题但一旦涉及 PCL、OpenCV 这类依赖RK3308 的 CPU 会很痛苦内存也不够。我试过在板子上编译一个带几个依赖的功能包结果g直接被 OOM 杀掉连 swap 都没来得及反应最后只能重启。交叉编译的核心思路是在 x86 主机上用目标架构的编译器编出 ARM64 二进制再拷贝到 RK3308 上运行。难点不是“用哪个编译器”而是“编译时看到的头文件和库必须和运行时一致”这就是 sysroot 存在的意义。sysroot 等价于把目标板上的根文件系统复制一份到主机让编译器在里面找/usr/include、/usr/lib的依赖。只要这一步做好交叉编译的成功率会非常高。1.3 我采用的整体技术链路我的最终方案是混合式的基础 ROS 环境ros-noetic-ros-base直接在目标系统的 rootfs 里安装因为官方有 arm64 现成二进制自研的传感器驱动、运动学算法这些 C 功能包在 x86 主机上用aarch64工具链交叉编译编译时通过 toolchain 文件指定 sysroot通过 pkg-config 的 cross 模式让 CMake 找到目标板上的 ROS 头文件生成的可执行文件连同devel环境一起同步到板子使用roslaunch/rosrun启动。这样既能避免为整个 ROS 源码做交叉编译的折腾又能保证所有自研代码复用主机的编译性能。下面按这个链路逐步展开。2. 交叉编译环境准备sysroot、工具链和 CMake 配置2.1 先搞定目标板 rootfs交叉编译第一步是拿到一份和目标板软件环境基本一致的根文件系统。如果这是某个厂商 SDK 里带的板子通常会附带 rootfs可以直接拿它来做 sysroot。我手里这块板的 BSP 采用 Debian 风格根文件系统所以我把目录同步到了主机上mkdir -p /opt/rk3308/sysroot rsync -av --excludeproc --excludesys --excludedev \ debian_rootfs/ /opt/rk3308/sysroot/这里要注意不需要把整个 rootfs 全部塞进去重点是/usr/include、/usr/lib、/usr/share、/lib这几个目录必须完整。如果 rootfs 是用debootstrap创建的那更好所有库文件都是齐的。提示不要图省事直接拷贝主机上的/usr作为 sysroot。宿主机是 x86 的.so和头文件版本都可能跟目标系统不一致最后出来的文件要么链接失败要么运行时报GLIBC_2.XX not found。sysroot 必须来源自目标架构的完整 rootfs。2.2 工具链安装与环境变量我用的是主线gcc-aarch64-linux-gnu和g-aarch64-linux-gnu在 x86 主机上直接 apt 安装sudo apt install gcc-aarch64-linux-gnu g-aarch64-linux-gnu如果 BSP 提供了自定义工具链也建议先验证一下它使用的 glibc 版本。很多时候自定义工具链是把 glibc 的兼容层打包在 sysroot 里的那样出问题的概率更低。但我用主线工具链也一切正常只要链接时指定了--sysroot。建议把下面的环境变量写到一个env.sh里方便每次编译时 sourceexport RK3308_SYSROOT/opt/rk3308/sysroot export CROSS_PREFIXaarch64-linux-gnu- export CC${CROSS_PREFIX}gcc export CXX${CROSS_PREFIX}g export LD${CROSS_PREFIX}ld export AR${CROSS_PREFIX}ar export RANLIB${CROSS_PREFIX}ranlib export PKG_CONFIG_PATH export PKG_CONFIG_LIBDIR${RK3308_SYSROOT}/usr/lib/pkgconfig:${RK3308_SYSROOT}/usr/share/pkgconfig:${RK3308_SYSROOT}/opt/ros/noetic/lib/pkgconfigPKG_CONFIG_LIBDIR是 pkg-config 查找.pc文件的唯一路径。这里把 sysroot 里的/opt/ros/noetic也加进去目的是让后续能找到roscpp自带的 pkg-config 信息。2.3 CMake 工具链文件的编写CMake 不会自动意识到你在交叉编译必须提供 toolchain file。我的rk3308.toolchain.cmake内容如下set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g) set(CMAKE_AR aarch64-linux-gnu-ar) set(CMAKE_RANLIB aarch64-linux-gnu-ranlib) set(CMAKE_SYSROOT $ENV{RK3308_SYSROOT}) set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_EXE_LINKER_FLAGS_INIT --sysroot${CMAKE_SYSROOT}) set(CMAKE_SHARED_LINKER_FLAGS_INIT --sysroot${CMAKE_SYSROOT}) set(CMAKE_MODULE_LINKER_FLAGS_INIT --sysroot${CMAKE_SYSROOT})其中CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER意味着查找可执行程序时不去 sysroot 里找避免 CMake 查找python3时撞上 ARM 版 Python然后直接拿它来执行代码生成脚本。后面的LIBRARY、INCLUDE、PACKAGE全部设为ONLY能保证头文件和库只来自 sysroot不会混入主机上的/usr/lib。如果是普通 CMake 项目编译时只需要cmake .. -DCMAKE_TOOLCHAIN_FILE./rk3308.toolchain.cmake对于 catkin 工作区我会在第 4 章给出完整命令。2.4 为什么FIND_ROOT_PATH_MODE_PROGRAM要设成 NEVER这个点值得单独说明。ROS 在编译过程中会调用很多 Python 脚本去生成消息代码比如genmsg、genpy、gencpp。这些脚本执行时不受交叉编译影响它们只需要在主机上有对应 Python 环境即可。但 CMake 为了找python3默认会搜索CMAKE_FIND_ROOT_PATH。如果MODE_PROGRAM也设成ONLYCMake 就能找到 sysroot 里的usr/bin/python3——那是面向 ARM 的二进制在 x86 主机上根本跑不起来编译直接报错。所以工具链里的NEVER是保证“编译期生成器跑在主机上链接期目标文件面向板子”。3. ROS 基础环境与目标板上的准备3.1 安装 ros-base 而不是桌面完整版在 RK3308 的最终系统里我优先选择安装ros-noetic-ros-base而不是desktop-full。一个干净的精简安装包含roscpp、rospy、std_msgs、sensor_msgs、roslaunch这些足够支撑绝大多数嵌入式节点。sudo apt update sudo apt install ros-noetic-ros-base如果你的 rootfs 是在主机上用chroot方式操作需要先挂载一些虚拟文件系统否则 apt 可能无法联网或用不了 DNSsudo mount --bind /proc /opt/rk3308/sysroot/proc sudo mount --bind /dev /opt/rk3308/sysroot/dev sudo mount --bind /sys /opt/rk3308/sysroot/sys sudo chroot /opt/rk3308/sysroot /bin/bash装完后确认/opt/ros/noetic/setup.bash存在同时echo $ROS_DISTRO能输出版本。这一步很关键因为后续交叉编译自研包时需要这个目录里的.so和头文件。3.2 将 ROS 基础库纳入 sysroot 搜索路径如果是在板子 rootfs 里直接装 ROS那/opt/ros/noetic自然就在 sysroot 中。交叉编译时CMake 的find_package(catkin)会到 sysroot 的/opt/ros/noetic/share中找 catkin 的 CMake 模块。只要CMAKE_SYSROOT设置正确不需要额外手动复制。但如果你的 sysroot 是从 SDK 拷贝出来的里面没装 ROS那就需要把目标板上/opt/ros/noetic整个目录同步到/opt/rk3308/sysroot/opt/ros/noetic同样还要带上/usr/lib/aarch64-linux-gnu下所有.so。否则即使 toolchain 指向 sysroot也可能在find_package(roscpp)的时候提示找不到。注意ROS 的.pc文件在/opt/ros/noetic/lib/pkgconfig下PKG_CONFIG_LIBDIR里必须包含这个目录。有些文章推荐把PKG_CONFIG_PATH指向主机/opt/ros/noetic这会导致链接时把 x86 的libroscpp.so找出来。那种方法就算链接过了把包拷到目标板上也跑不了。3.3 使用 qemu 和 chroot 在宿主上安装 ROS 的备选方案如果你的板子没有联网又想得到一个“已装好 ROS 的 rootfs”还有一个非常实用的备选方案在 x86 主机上用qemu-user-static模拟 ARM对 rootfs 直接执行chroot然后在模拟环境中用 apt 安装 ROS。操作大致是sudo apt install qemu-user-static binfmt-support sudo cp /usr/bin/qemu-aarch64-static /opt/rk3308/sysroot/usr/bin/ sudo chroot /opt/rk3308/sysroot /bin/bash在这个模拟环境里apt会按照目标架构 arm64 进行安装。装完后把qemu-aarch64-static从 sysroot 中删除避免影响后续 sysroot 的干净程度。这个方式的优点是不依赖板子当前是否运行缺点是模拟环境安装速度很慢尤其 ROS 这种依赖极多的东西可能要半小时以上。通常我还是推荐直接在板子上 apt 装再通过rsync把/同步回主机作为 sysroot源。4. 交叉编译自研 ROS 驱动和算法包4.1 用 catkin_make 配合交叉工具链自研包一般放在独立的工作区里。我的目录结构大致是catkin_ws/ src/ rk3308_chassis/ CMakeLists.txt package.xml src/ chassis_node.cpp rk3308.toolchain.cmake编译时不是直接无脑catkin_make而是把 toolchain 文件路径传给 CMake。我试过下面这组参数能稳定处理大多数情况cd catkin_ws catkin_make -DCMAKE_TOOLCHAIN_FILE$(pwd)/rk3308.toolchain.cmake \ -DCMAKE_PREFIX_PATH${RK3308_SYSROOT}/opt/ros/noetic \ -DCMAKE_BUILD_TYPERelease因为已经让CMAKE_SYSROOT指向 sysroot理论上-DCMAKE_PREFIX_PATH不用手动指定。但 catkin 内部的包搜索有时会额外受主机变量影响显式声明CMAKE_PREFIX_PATH能降低“混到宿主 ROS”的概率。编译完成后检查生成的可执行文件file devel/lib/rk3308_chassis/chassis_node如果输出里有ELF 64-bit LSB executable, ARM aarch64而不是x86-64说明架构对了。4.2 消息生成与依赖问题ROS 的消息生成脚本是从源码仓库里找到并执行的在主机上有 Python 3 和genmsgPython 包即可。它们生成的是 C/Python 文件不参与目标板链接所以没有架构问题。唯一要留意的是某些老教程依赖 Python 2 的genmsg在新主机上会直接失败Noetic 已经统一到 Python 3。如果自研包里引用了roscpp、std_msgs、sensor_msgs等CMake 的find_package会通过 sysroot 找到。问题往往出现在引用第三方库时比如 PCL、OpenCV。这些库若在目标板是通过 apt 预装的头文件已经躺在 sysroot 里toolchain 能自动找到若你是从源码构建的则要单独指定安装前缀。4.3 用 pkg-config 包装脚本解决.pc文件查找为了让 autotools 或某些 CMake 项目正确找到 sysroot 里的依赖可以写一个aarch64-pkg-config包装脚本#!/bin/bash export PKG_CONFIG_LIBDIR${RK3308_SYSROOT}/usr/lib/pkgconfig:${RK3308_SYSROOT}/usr/share/pkgconfig:${RK3308_SYSROOT}/opt/ros/noetic/lib/pkgconfig export PKG_CONFIG_PATH exec /usr/bin/pkg-config $记得给脚本加可执行权限然后在编译第三方库时用它替代系统pkg-config./configure --hostaarch64-linux-gnu \ PKG_CONFIG/path/to/aarch64-pkg-config \ --prefix${RK3308_SYSROOT}/usr这部分对自研 ROS 包不一定必需但如果板子上部署的 ROS 节点还要用到 Boost、OpenSSL 或自己写的 C 库这个包装脚本能省去很多环境变量上的折腾。4.4 交叉编译时也要注意编译选项针对 RK3308 的 Cortex-A35 核心我建议在 CMake 里加上add_compile_options(-marcharmv8-a -mtunecortex-a35 -O2)没有-march时编译器默认按基础 ARMv8 指令集生成功能没问题但性能会打折扣。加上-mtunecortex-a35之后无论是系统库还是自研节点性能都能压榨到比较理想的状态。对于传感器预处理节点开启-ffast-math前要慎重它会在某些边界情况下改变数值比较逻辑可能导致滤波器不稳定。我一般只用-O2 -marcharmv8-a不做激进优化。5. 部署到 RK3308 并让 ROS 节点跑起来5.1 把编译产物同步到目标板交叉编译完成后部署不是把devel整个目录拷过去就完事也不是只拷一个可执行文件。一个稳妥的做法是同步工作区里的devel和srcrsync -av ./devel/ targetboard_ip:~/catkin_ws/devel/ rsync -av ./src/ targetboard_ip:~/catkin_ws/src/关键在于主机和板子上这个工作区的相对路径要保持一致因为setup.bash会根据自身所在路径计算 ROS 环境前缀。如果路径变了轻则source报错重则节点启动时找不到catkin_generated里的配置。同步之后在板上执行source /opt/ros/noetic/setup.bash source ~/catkin_ws/devel/setup.bash这里不要试图在板上重新catkin_make。交叉编译缓存和板载原生 CMake 缓存不一致一旦触发重新配置大概率会把整个工作区按本机架构重新编译一遍耗时巨大。直接使用同步过去的devel是最快的。5.2 正确初始化 ROS 环境在 RK3308 上开机后建议把下面的内容写入.bashrcsource /opt/ros/noetic/setup.bash source ~/catkin_ws/devel/setup.bash export ROS_MASTER_URIhttp://localhost:11311 export ROS_HOSTNAMElocalhost如果机器人的 ROS Master 运行在另一台主控上ROS_MASTER_URI就要指到那台机器的 IP比如http://192.168.1.100:11311。这里的ROS_HOSTNAME如果只是单机运行设成localhost最省事如果 ROS 节点要和其他机器通信建议设成 RK3308 的内网 IP否则roscore可能只监听回环地址出现“节点拿不到 master 数据”的奇怪现象。5.3 冒烟测试部署完成后先做一个小测试source /opt/ros/noetic/setup.bash source ~/catkin_ws/devel/setup.bash roscore rosrun rk3308_chassis chassis_node如果启动正常能在rosnode list中看到节点。再跑一个rostopic echo /chassis/status验证消息是否能持续发布。如果出现error while loading shared libraries先用ldd chassis_node检查缺失的.so绝大多数情况是因为 sysroot 里没有同步某个库或者把主机 ROS 的链接路径带到了 sysroot 之外。6. 常见问题与排查技巧实录6.1 编译时报cannot find -lroscpp怎么办这个报错最常见的两个原因第一CMAKE_SYSROOT没有设置正确导致find_path/find_library找不到/opt/ros/noetic/lib第二ROS 库虽然是.so文件但内部还有符号链接rsync同步时如果没有保留符号链接libroscpp.so的软链就会断掉。用ls -l /opt/rk3308/sysroot/opt/ros/noetic/lib/libroscpp.so查看如果是红色断链说明源 rootfs 里的链接关系没保留。重新用cp -a或者保留链接的rsync -avH同步一次。6.2 链接成功后板载运行报GLIBC_2.32 not found这是典型的“宿主机链接到了宿主 glibc”症状。比如编译时没有加--sysroot/opt/rk3308/sysroot而是在/usr/lib/x86_64-linux-gnu里找到了 glibc生成的二进制就会携带宿主机 glibc 版本要求。解决方法是回到编译目录彻底清理缓存后重新用 toolchain 文件配置。注意有些时候make -j没有重跑 CMake链接器仍沿用旧参数所以一定先rm -rf CMakeCache.txt CMakeFiles再重编。6.3 qemu 与 binfmt 的坑在 x86 主机上 chroot 安装 ROS 时如果发现apt安装的都是 x86 包先检查目标 rootfs 的架构是否真是aarch64。有些 SDK 的 rootfs 其实是 32 位armhf对应要拷贝qemu-arm-static工具链前缀也要改成arm-linux-gnueabihf-。我第一次操作就把 64 位 rootfs 和 32 位工具链混用了结果编译器报 “selected processor does not support mode” 一类的错误。确认架构最简单的方式是看根文件系统里有没有/lib/ld-linux-aarch64.so.1有就是 64 位如果只有/lib/ld-linux-armhf.so.3就是 32 位。6.4 catkin_make 跑得太慢或者被杀如果某个 ROS 包装了 OpenCV 这类重型依赖交叉编译时耗时依然不短。建议把make -j并发数调低一点。其实交叉编译还是 x86 主机在做-j8通常没问题但后台跑着容器或虚拟机内存不够时也会有交叉编译进程被 OOM 杀掉。更常见的问题是 CMake 配置阶段反复试探 sysroot 里的库如果添加了一堆-D参数让配置反复变化每次都会重新探测。最好一次把参数写全不要一条命令多跑几遍。6.5 ROS 参数服务器上的时间同步问题嵌入式板卡的时钟经常不准ROS 节点启动后如果发现tf时间跟不上先检查板子是否启用了 NTP。我遇到过一次ros::Time::now()经常跳变后来发现是时间同步服务没启用。这个问题和交叉编译无关但很容易在使用过程中被误认为是“驱动节点编坏了”。搭环境时顺手把时间同步配置好能省掉很多后续调试。7. 善后与长期维护心得7.1 最容易忽略的三个维护点第一不要把“交叉编译”和“部署”解耦开。交叉编译只是产出目标板上的二进制最终验证永远在目标板上。所以每次改完代码我都会用rsync增量同步到板上在板上跑一个快速回归测试而不是只看主机编译结果。第二工具链和 sysroot 里库的版本一定要对齐。很多朋友拿到一个 prebuilt 的 ARM 库直接放进 sysroot结果编译时符号对不上。最简单的做法是用一套完整 rootfs不要东拼西凑“最小 sysroot”。完整 rootfs 能保证/usr/lib下的符号依赖一致也不会破坏.so之间的软链接关系。第三RK3308 不适合跑桌面级 ROS 开发环境但作为底盘控制器、传感器网关这类固定任务节点很稳。我最后把roscore放在板子上自研节点全部做成开机自启并控制采样周期。整机功耗和稳定性都比较满意。7.2 这套环境后续还能怎么扩展如果后续有更多算力需求可以考虑把重负载节点移到上层主控上RK3308 只保留实时性要求高的轻量节点。或者把 ROS 通信中间层换成更轻量的本地总线只在边界处与 ROS Master 对接。这套交叉编译框架本身不做变化新增驱动、新增算法包时只需要在 catkin 工作区里继续编进去再同步到板子上即可。这也是在资源受限设备上部署 ROS 的一条比较清晰的长期演进路线。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑