做语音机器人或者小型服务机器人,很多人第一反应是用树莓派跑ROS,但真正做产品落地时,一颗专用的嵌入式SoC往往更合适。RK3308就是这种定位——四核A35、主频1.3GHz,功耗低,还带了一堆音频接口,专门为智能语音交互场景设计。把它和ROS结合起来做基础运算平台,是很多创业团队和实验室的常见做法。
不过这里有个绕不开的现实问题:RK3308的CPU性能不算强,如果直接在板子上编译ROS的大包,一个roscpp能编译半小时起步,完整的ROS环境可能让你怀疑人生。所以正宗做法是交叉编译——在x86的PC上编译出aarch64的二进制,再部署到板子上。这篇就把整个流程拆开讲清楚,从工具链、sysroot、ROS环境搭建,到交叉编译一个完整的ROS节点,每一步都给你可以直接抄的配置和命令。
1. 方案选型:为什么是RK3308,为什么必须交叉编译
1.1 RK3308这颗芯片到底适合干什么
RK3308本质上是个“偏科生”,它不像RK3399那样追求综合性能,而是把资源集中在了音频和低功耗上。四核Cortex-A35的频率只有1.3GHz,在ARM家族里属于够用但不算快的水平,但它集成了多路I2S、PDM、TDM接口,支持模拟麦克风阵列接入,还内置了硬件编解码器。这意味着做语音前端采集、唤醒词处理、信号降噪这些任务时,它可以绕过USB声卡和外部Codec,直接以很低的延迟拿到多路数字音频。
在ROS生态里,这颗芯片常被用在两类项目上:
- 语音交互机器人:雷达和SLAM跑在性能更强的上位机或工控机上,RK3308专门负责语音识别、意图理解、舵机控制和交互应答。
- 低成本教育机器人:把ROS节点裁到最小,只跑运动控制、传感器采集和简单状态机,配合一块电源管理芯片就能做成比较便宜的学习套件。
说白了,它不是用来跑重型算法的,它的定位是“实时、低功耗、IO丰富”的嵌入式控制中心。理解了这层定位,你才知道为什么环境搭建策略跟树莓派完全不一样——不能拿PC的思路去看它。
1.2 原生编译和交叉编译的成本对比
在决定怎么做之前,我先把两种方案的账算给你看。
| 方案 | 单包编译耗时(roscpp) | 完整ROS环境 | 对网络/磁盘要求 | 部署灵活性 |
|---|---|---|---|---|
| 板端原生编译 | 30~60分钟 | 8小时以上 | 板子需要大容量存储和稳定供电 | 差,改一次配置全量重编 |
| 主机交叉编译 | 3~5分钟 | 编译一次后所有节点复用 | 主PC正常磁盘即可 | 好,产物直接scp分包部署 |
有人会说,板子上跑Ubuntu,直接apt install ros-melodic-ros-base不就行了,还要交叉编译干嘛?这里要分清楚两件事:运行环境和编译环境。
ROS的arm64版本在18.04上有官方预编译包,这一步确实可以直接在板子上安装,板子只作为运行Runtime,不做编译。但你自己写的算法包、调用的第三方库(比如语音前端库、自研SLAM模块),不可能都通过apt拿到源码包,这些必然要编译。如果每次都在板子上编,一个带几十个依赖的工程,随便编译一下就是一顿饭的功夫。
所以我最终采用“板端安装ROS Runtime + 主机交叉编译自定义节点”的组合。ROS运行时要的所有依赖在板子上一键装好,自定义代码在主机上交叉编译出aarch64二进制,部署时只拷贝编译产物。这样板子始终只负责跑,不负责编,整个开发节奏才跟得上。
2. 硬件平台与开发环境准备
2.1 RK3308的硬件基线参数
开始搭建之前,先把环境基线确认清楚。我这里基于最常见的一套配置来说明,你手里的板子型号不同但流程基本一致:
- SoC:RK3308,四核Cortex-A35,主频1.3GHz
- 内存:512MB/1GB/2GB 可选,语音交互类产品多为512MB
- 存储:板载eMMC + TF卡启动,推荐TF卡做开发调试
- 网络:百兆以太网 + Wi-Fi扩展,开发时必须保证有线连接稳定
- 系统:Ubuntu 18.04 aarch64(官方或第三方适配镜像)
开发主机这边,我建议用Ubuntu 18.04或20.04的x86_64环境。原因有两个:一是ROS Melodic(ROS1)官方源在18.04上支持最完善,二是GCC 8.3版本的工具链在aarch64上有干净的glibc兼容性。如果你主机是Ubuntu 22.04也没关系,工具链直接用压缩包方式运行,不依赖系统包,但后面提到的sysroot路径要自己留意。
2.2 开发主机上的基础工具安装
主机端需要准备这么几类东西:
# 基础工具链 sudo apt update sudo apt install build-essential cmake git python3 python3-pip \ minicom ssh rsync # 如果要用catkin工具编译工作空间 sudo apt install python3-catkin-tools这里有一个容易踩的小坑:python3-catkin-tools在Ubuntu 20.04以后默认源里可能没有,需要从ROS官方源安装。如果只是编译单个节点,用cmake直接编也行,不一定要catkin。对于整个工作空间的管理,后面我会给一个兼顾两者的方案。
主机和板子的网络连通性也要先验证。板子在开发阶段连网线,给板子设置固定IP,比如192.168.1.100,主机和板子同一网段,用ping和ssh做连通性检查。这一步建议在开始就做掉,后面所有rsync和部署操作都依赖网络。
3. 交叉编译工具链配置
3.1 获取合适的aarch64工具链
交叉编译的第一步,是要有一套在x86主机上运行的、生成aarch64目标代码的编译器。对RK3308这种Cortex-A35核心,标准的aarch64-linux-gnu-工具链就够了。
我这里推荐用ARM项目维护的预编译GCC工具链,版本选GCC 8.3。之所以选8.3,是因为它与Ubuntu 18.04的glibc版本兼容性最好。板子上的运行库如果是18.04的glibc 2.27,那么用GCC 9/10编译出来的二进制,在链接时可能要求更新的glibc符号,跑起来直接报version GLIBC_2.28 not found。
获取方式很简单,下载压缩包后解压到指定目录:
mkdir -p ~/rk3308 cd ~/rk3308 # 假设下载后的文件名为 gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu.tar.xz tar -Jxf gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu.tar.xz解压出来的目录一般长这样:gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/,里面是aarch64-linux-gnu-gcc、aarch64-linux-gnu-g++这些可执行文件。
3.2 工具链环境变量与基础验证
解压完成后,把bin目录写进PATH:
export PATH=~/rk3308/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin:$PATH export CROSS_COMPILE=aarch64-linux-gnu-然后验证一份最基础的程序,写个hello_rk3308.c:
#include <stdio.h> int main() { printf("hello from RK3308 cross compile\n"); return 0; }编译:
aarch64-linux-gnu-gcc hello_rk3308.c -o hello_rk3308 file hello_rk3308如果输出:
hello_rk3308: ELF 64-bit LSB executable, ARM aarch64, ...说明工具链已经在正常工作,接下来要做的是让它能链接到板子上的系统库里,这就引出了整个交叉编译里最关键的概念——sysroot。
3.3 sysroot的同步与构建
交叉编译器编译出的二进制,运行时依赖的目标板系统库(libc、libstdc++、各种so)并不在主机上,需要把板子系统根目录下的库文件同步到主机的一个目录里。这个目录就叫sysroot,编译时传给编译器,让它知道去哪里找头文件和库。
我用的做法是直接用rsync从板子上同步:
mkdir -p ~/rk3308/sysroot rsync -avz root@192.168.1.100:/usr ~/rk3308/sysroot/ rsync -avz root@192.168.1.100:/lib ~/rk3308/sysroot/同步完成后,检查几个关键目录是否存在:
ls ~/rk3308/sysroot/usr/lib/aarch64-linux-gnu/libstdc++.so.6 ls ~/rk3308/sysroot/usr/include/stdio.h只要这些核心文件在,交叉编译的自定义程序基本都能搞定。注意一个非常重要的细节:同步的是板子安装完ROS之后的系统,不是刚烧录的裸系统。因为交叉编译ROS节点时,需要链接很多ROS自身的库(比如libroscpp.so),这些库必须先在板子上通过apt安装好,再同步到sysroot。换个顺序就会导致头文件或库找不到。
4. 板端ROS运行环境搭建
4.1 在RK3308上安装ROS Runtime
RK3308板端我选择跑Ubuntu 18.04 aarch64,ROS版本用Melodic。ROS1的Melodic对应arm64是有官方预编译包的,安装流程跟x86上差别不大。
先加源和密钥:
sudo apt update curl -sSL http://packages.ros.org/ros.key | sudo apt-key add - sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu bionic main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt update sudo apt install ros-melodic-ros-base这里只装ros-base,不装desktop-full。实测下来,desktop-full里包含的GUI工具(rviz、gazebo)在1GB内存的RK3308上会非常吃力,而且根本用不上。ROS运行时所必需的roscpp、roslaunch、topic通讯都在ros-base里,足够跑了。
安装完之后初始化:
sudo rosdep init rosdep update echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc source ~/.bashrc4.2 哪些ROS功能包必须保留,哪些可以裁掉
板子存储空间有限,一味的全量安装不是好的工程习惯。我按功能把ROS包分成三层:
| 层次 | 包示例 | 用途 | 板端是否需要 |
|---|---|---|---|
| 通信内核 | roscpp, rospy, std_msgs, roslaunch, rosmaster | 节点通信基础 | 必须 |
| 常用功能 | tf2, sensor_msgs, nav_msgs, geometry_msgs | 坐标变换、消息定义 | 按需 |
| 可视化工具 | rviz, rqt, gazebo | PC端调试监控 | 不需要 |
在安装ros-base之后,再根据自己项目的实际消息类型apt install对应的接口包,比如ros-melodic-sensor-msgs、ros-melodic-tf2。这是个容易被忽略的准备工作,等交叉编译时发现某个头文件找不到,再回头补板子的包,又要重新sync sysroot,白等半天。
4.3 板端ROS环境的验证
板端安装完成后,做一个最基础的“环路测试”:
# 终端1 roscore # 终端2 rosrun roscpp_tutorials talker # 终端3 rostopic echo /chatter如果消息能正常收发,说明ROS Runtime工作正常。这个测试必须做,否则后面交叉编译的节点跑不起来,你分不清是编译问题还是ROS环境问题,排查成本翻倍。
5. 交叉编译ROS工作空间
5.1 CMake工具链文件编写
到这一步,手头有了工具链、有了sysroot,也有了板端的ROS库,可以开始交叉编译ROS节点了。
我把所有交叉编译的配置写到一个工具链文件里,命名为aarch64.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(SYSROOT $ENV{HOME}/rk3308/sysroot) set(CMAKE_FIND_ROOT_PATH ${SYSROOT} /opt/ros/melodic) 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_C_FLAGS "--sysroot=${SYSROOT}") set(CMAKE_CXX_FLAGS "--sysroot=${SYSROOT}") set(CMAKE_EXE_LINKER_FLAGS "--sysroot=${SYSROOT}")CMAKE_FIND_ROOT_PATH这里填的是两个搜索路径,一个是sysroot,一个是主机上安装的ROS目录。主机上也需要安装一份ROS的arm64交叉构建版本吗?这里有个关键点:ROS的头文件(比如roscpp的消息头)与架构无关,可以直接用板端同步到sysroot里的/opt/ros/melodic。所以我在同步sysroot时,也会把板子的/opt/ros目录同步一份到主机:
rsync -avz root@192.168.1.100:/opt/ros ~/rk3308/sysroot/这样系统库里就有了libroscpp.so这些文件,FIND_ROOT_PATH里填${SYSROOT}/opt/ros/melodic,CMake在找catkin的包时就能命中。
5.2 编译一个带catkin依赖的节点
以一个简单自写节点为例,假设目录结构:
~/catkin_ws/src/simple_publisher/ ├── CMakeLists.txt ├── package.xml └── src └── simple_publisher.cppsimple_publisher.cpp内容是一个标准的rospy/roscpp发布者,我用cpp写:
#include <ros/ros.h> #include <std_msgs/String.h> int main(int argc, char** argv) { ros::init(argc, argv, "simple_publisher"); ros::NodeHandle nh; ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10); ros::Rate rate(1.0); while (ros::ok()) { std_msgs::String msg; msg.data = "hello from rk3308"; pub.publish(msg); ros::spinOnce(); rate.sleep(); } return 0; }交叉编译这个包,推荐用catkin工具显式指定工具链文件:
cd ~/catkin_ws catkin_make -DCMAKE_TOOLCHAIN_FILE=$HOME/rk3308/aarch64.toolchain.cmake如果你的catkin_make没法识别工具链文件里的交叉编译器,可以退一步直接在包目录里用cmake指定:
cd build cmake ../src/simple_publisher \ -DCMAKE_TOOLCHAIN_FILE=$HOME/rk3308/aarch64.toolchain.cmake \ -DCMAKE_PREFIX_PATH=$HOME/rk3308/sysroot/opt/ros/melodic make两种方式我实际都用过,catkin_make整套流程更顺畅,但有个前提——cmake版本不能太低,建议在3.10以上,否则工具链文件和catkin的交叉编译扩展可能有兼容问题。
5.3 编译结果验证与部署
编译完成后,先别急着拷过去,在主机上检查二进制是不是真交叉编译出来的:
file devel/lib/simple_publisher/simple_publisher # 期望输出:ELF 64-bit LSB executable, ARM aarch64再用ldd看一下它依赖的动态库路径(交叉ldd需要加--root参数指向sysroot):
aarch64-linux-gnu-ldd --root=$HOME/rk3308/sysroot \ devel/lib/simple_publisher/simple_publisher如果依赖库里出现libroscpp.so => not found,说明CMAKE_PREFIX_PATH或FIND_ROOT_PATH路径没配对。如果找到的是x86架构的libroscpp.so,那说明工具链文件根本没生效。这两类问题排查起来都很费时,建议每次编译前先跑一下file确认目标架构。
确认无误之后,把整个devel目录或者单个二进制同步到板子:
rsync -avz devel/lib/simple_publisher/ root@192.168.1.100:/home/rk3308/deploy/板子上运行:
source /opt/ros/melodic/setup.bash source /home/rk3308/deploy/setup.bash rosrun simple_publisher simple_publisher如果板端之前roscore已经跑起来,用rostopic echo /chatter应该能看到hello from rk3308的消息流。
6. 常见问题与排查技巧实录
这个环节才是整个项目里最值钱的部分。交叉编译ROS环境的坑,很多是文档里不会写的,我按踩过的顺序整理成速查表。
| 现象 | 根因 | 解决办法 |
|---|---|---|
编译报GLIBC_2.28 not found | 工具链太新,编译出的二进制要求更新的glibc | 换回GCC 8.3工具链 |
板端运行报Cannot open shared object file | sysroot没有同步完整,或运行依赖缺失 | 检查ldd结果,补同步/补装包 |
CMake提示找不到roscppConfig.cmake | CMAKE_PREFIX_PATH没指到sysroot下的/opt/ros/melodic | 在工具链文件里把路径加进FIND_ROOT_PATH |
编译后file显示x86-64 | 工具链文件根本没被CMake加载 | 先跑一次带--debug的cmake确认加载 |
板端rospack find找不到新部署的包 | 新工作空间没有source | 在板端source部署目录下的setup.bash |
| Qemu方式在主机运行aarch64程序失败 | qemu-user版本或库路径问题 | 开发阶段尽量只在主机编译,不运行,运行时都放板端 |
6.1 工具链版本与系统库不匹配的排查
这是我遇到过最隐蔽的问题。在主机上用GCC 10工具链编译出一个ROS节点,板端跑的时候报:
./simple_publisher: /lib/aarch64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.28' not found尽管工具链本身是aarch64,但GCC 10生成的代码需要较新的libstdc++,板子的18.04系统提供不了。这种问题的排查思路很简单:确定板子的Ubuntu版本,选择与系统glibc版本匹配的GCC工具链。18.04 + GCC 8.3是一对经典组合,20.04 + GCC 9.3又是另一组。不要把工具链当成万能钥匙,它只是编译器,生成的二进制还是要依赖目标系统的运行库。
6.2 头文件与库文件不一致导致的编译错误
还有一个典型坑:Could not find the required component 'roscpp'。明明板子上装了ros-base,sysroot也同步了,但cmake就是找不到。
原因在于我先前没有同步/opt/ros,只同步了/usr。ROS安装时的库和头文件都放在/opt/ros/melodic下,不在系统标准路径里。所以同步sysroot时,/opt目录必须单独同步。把这条写进部署脚本里,每次板端加了新的ROS包,就重新同步一次/opt/ros。
6.3 板端磁盘不足与构建缓存策略
RK3308板载eMMC一般只有4~8GB,安装完Ubuntu和ROS Runtime以后,再放训练好的模型、部署的二进制,空间会非常紧张。开发阶段我建议直接把系统跑在TF卡上,16GB的卡比eMMC方案省心很多。TF卡速度慢一点,但对开发调试来说完全够用。
另外,交叉编译产物部署时,不要每次把devel全部拷过去。rsync可以配合--exclude把build和logs目录过滤掉,只同步devel下的可执行文件和lib目录。
rsync -avz \ --exclude 'build' \ --exclude 'logs' \ --exclude '*.o' \ devel/ root@192.168.1.100:/home/rk3308/deploy/这样每次增量部署的速度快很多,尤其是大工作空间,能节约大量等待时间。
6.4 运行时若出现乱码或话题数据异常
如果音频相关节点出现数据异常,先别怀疑ROS通信。RK3308的音频走的是I2S/PDM接口,交叉编译时一定要确保编译选项里开了对应的硬件抽象调用,很多SDK里的音频库依赖NEON指令,编译时不能加-march=armv8-a+nosimd这种禁用SIMD的参数。Cortex-A35支持ARMv8-A完整指令集,编译时用-march=armv8-a,不要手动去指定更老的架构版本,否则音频DSP相关库的性能和功能都会出问题。
7. 这份环境的扩展思路
写完这套基础环境,你可以顺理成章地扩展几个方向。一个是加入robot_localization这样依赖较多第三方库的定位包时,需要把依赖链一起做成交叉编译模板;另一个是用ROS2替换ROS1时,工具链文件几乎可以复用,只是要额外处理DDS的库依赖。
还有一个更实用的方向是做一个适配自己项目的部署镜像。把编译好的ROS工作空间、启动脚本、自启动服务打包进一个只读分区,板子开机自动拉起来,这样量产时不需要每台设备手动配置。做法就是先在开发环境里把所有产物整理好,做成一个适合直接烧录的根文件系统,配合RK的烧录工具刷进eMMC。这套环境跑通之后,后续的迭代基本就是“主机编译-增量同步-板端重启”三个动作。
在我个人的实际开发节奏里,最省时间的一步其实是把sysroot的同步脚本化。刚开始手动同步,后来发现隔三差五就得重新同步一遍,干脆写了一个shell脚本,自动完成板子系统目录、ROS目录的rsync,然后做一次工具链自检。这个脚本后面越用越顺手,也帮你把这套流程固定下来。
最后给一个提醒:如果你纯粹是为了学习ROS,直接在PC上装一个完整版ROS环境远比交叉编译这条路舒服。但如果是做嵌入式平台的产品开发,交叉编译这套能力是绕不过去的。在这个过程里踩过的每一个坑,后面做其他ARM平台项目时几乎都会再次遇到,一次搞定,长期受益。