第15章 构建与交叉编译

码枢沄社 · 嵌入式体系化教程平台
进阶 👤 具备ROS2节点开发基础、准备部署到ARM硬件平台的嵌入式开发者 🔧 在资源受限的ARM开发板上运行ROS2节点,实现x86主机向嵌入式目标板的跨平台构建
本章你将学到
  • 理解交叉编译的适用场景与核心原理
  • 编写CMake工具链文件(toolchain.cmake)并配置sysroot
  • 使用colcon完成ARM平台的ROS2节点交叉编译
核心概念交叉编译工具链sysroot

15.1 理解交叉编译

对比维度本地编译交叉编译
开发机架构与目标运行架构相同(如x86 → x86)与目标架构不同(如x86 → ARM)
工具链来源系统自带的gcc/g++专用的交叉编译工具链(如aarch64-linux-gnu-gcc)
运行时库使用本机/lib和/usr/lib需要使用目标板的根文件系统(sysroot)
依赖项处理apt直接安装需将目标架构的deb包解压到sysroot中
典型场景PC端ROS2节点开发调试树莓派、Jetson等ARM平台部署
生活类比

交叉编译就像在中文环境下写英文信件——你写作的环境(主机x86)和阅读环境(目标ARM)使用的"语言"不同,需要一份"翻译手册"(交叉工具链)来确保对方能看懂。

技术定义:交叉编译是指在一个架构(如x86_64)的主机上,编译生成另一个架构(如ARM64)上可执行的二进制代码的过程。编译工具链本身运行在主机上,但生成的代码只能在目标平台上运行。

我在项目中踩过这个坑:在x86工控机上调试通过的激光雷达驱动,直接用colcon build编译后,把整个install目录scp到ARM开发板,一跑就报Segmentation fault。原因很简单——编译出来的二进制是x86指令集,ARM的CPU根本不认识。这就是典型的"忘了交叉编译"的菜鸟错误。

主机 (x86_64)
交叉工具链
目标板 (ARM64)
colcon + CMake
toolchain.cmake
sysroot (目标根文件系统)

实际开发中,大部分团队会选择在x86主机上配置一套完整的交叉编译环境,通过工具链文件告诉CMake三件事:用什么编译器、目标架构是什么、去哪里找运行时库。这套流程跑通后,后续所有ROS2包的交叉编译就是一条命令的事。

15.2 工具链与sysroot:交叉编译的两个基石

交叉工具链是一整套针对目标架构的编译工具集合,包括编译器、链接器、汇编器以及相关的库文件。以ARM64平台为例,常用的工具链前缀是aarch64-linux-gnu-,配套的编译器是aarch64-linux-gnu-gcc

sysroot是目标板根文件系统的一份副本,包含了目标架构的动态库、头文件以及依赖的ROS2软件包。交叉编译时,链接器会优先从sysroot中查找libc.solibpthread.so等运行时库,而不是从主机的/usr/lib中找。

关键要点
  • 工具链必须与目标架构匹配——ARMv7用arm-linux-gnueabihf-,ARM64用aarch64-linux-gnu-
  • sysroot可以从开发板中直接打包获取,也可以从厂商提供的BSP中提取
  • sysroot中的ROS2依赖包也必须是对应架构的版本,不能混用

以ROS2 Humble + ARM64为例,典型的工具链文件如下:

# 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++)

# 指定sysroot路径
set(CMAKE_SYSROOT /path/to/sysroot/aarch64-rootfs)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})

# 只在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)

这里有个隐藏的坑:CMAKE_FIND_ROOT_PATH_MODE_PROGRAM必须设为NEVER,否则CMake会在sysroot中找主机工具(如Python解释器),导致配置失败。我在第一次配这个文件时踩过这个坑,卡了整整半天才定位到原因。

15.3 使用colcon进行交叉编译

colcon支持通过--cmake-args传递自定义CMake参数。交叉编译时,只需要将工具链文件通过-DCMAKE_TOOLCHAIN_FILE传入即可。

# 标准交叉编译命令
colcon build \
  --merge-install \
  --cmake-args \
    -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain.cmake \
    -DCMAKE_SYSROOT=/path/to/sysroot/aarch64-rootfs \
    -DCMAKE_BUILD_TYPE=Release
✏️ 编者提示
--merge-install参数在这里非常重要。常规的colcon构建会将每个包的产物分散到独立的子目录中,而--merge-install会将所有编译产物合并到同一个install目录下。对于嵌入式目标板,你只需要把这个install目录打包拷贝到开发板上,再设置好AMENT_PREFIX_PATH环境变量即可运行。如果不加这个参数,你会面对几十个分散的目录,部署时非常痛苦。

编译过程中如果遇到找不到依赖包的报错,大概率是sysroot中的ROS2依赖不完整。一个稳妥的做法是:先在目标板上用apt list --installed列出所有已安装的ROS2包,然后在主机上下载对应架构的.deb包,解压到sysroot中。

⚠️常见误区
  • 误区一:直接用apt安装在主机上的ROS2包提供给交叉编译——主机上的包是x86架构的,链接器会报"wrong ELF class"错误。必须使用目标架构的包。
  • 误区二:sysroot路径中使用相对路径——CMake在处理相对路径时行为与预期不一致,建议始终使用绝对路径配置CMAKE_SYSROOT
  • 误区三:跳过--merge-install直接部署——这会导致运行时报找不到动态库,因为ROS2的AMENT搜索路径依赖于包的安装布局。

15.4 排查与验证

交叉编译完成后,不要急着把整个install目录复制到开发板。先用file命令检查生成的二进制文件架构:

# 确认编译产物是ARM64格式
file install/lib/my_node/my_node
# 输出应为:ELF 64-bit LSB shared object, ARM aarch64

如果你看到输出是x86-64,说明工具链没有生效——检查CMAKE_C_COMPILER路径是否正确,以及CMake缓存是否被之前的本地编译污染。建议每次切换编译模式时,先删除build/install/目录再重新编译。

另一个实用技巧:在目标板上运行ldd检查动态库依赖是否都能在sysroot中找到对应的ARM版本。如果缺少某个.so文件,ldd会直接报"not found"。

场景:ARM64开发板

  • 工具链:gcc-aarch64-linux-gnu
  • sysroot:从Ubuntu Base构建
  • colcon + merge-install

场景:ARMv7(32位)

  • 工具链:gcc-arm-linux-gnueabihf
  • sysroot:从BSP中提取
  • 注意软浮点vs硬浮点ABI

场景:RISC-V

  • 工具链:riscv64-linux-gnu-gcc
  • sysroot:需要手动构建
  • ROS2官方支持有限

针对不同目标平台,工具链和sysroot的获取方式略有差异。ARM64和ARMv7有成熟的发行版支持(如Ubuntu Server、Debian),可以直接用debootstrap构建sysroot。RISC-V目前还在快速演进中,建议直接从芯片厂商的SDK中获取根文件系统。

动手试一试

  1. 准备一个ARM64的根文件系统(可以使用debootstrap --arch=arm64构建),并安装ROS2 Humble的基础包。
  2. 根据15.2节中的模板,编写针对ARM64的toolchain.cmake文件。
  3. 创建一个简单的ROS2节点(只打印"Hello from ARM"),使用colcon build --merge-install进行交叉编译。
  4. file命令确认产物是ARM64格式,然后将install目录复制到开发板上运行。

检验你的理解

  1. 判断题:交叉编译生成的二进制文件可以在x86主机上直接运行。(正确/错误)
  2. 选择题:在toolchain.cmake中,CMAKE_SYSROOT的作用是?A. 指定编译器的安装路径;B. 指定目标架构的根文件系统路径;C. 指定安装目录
  3. 判断题:使用--merge-install参数后,所有包的编译产物会合并到同一个install目录中。(正确/错误)

本章小结

  • 交叉编译的核心是在x86主机上使用专用工具链,生成ARM等目标架构的可执行代码。
  • toolchain.cmake文件告诉CMake三要素:编译器路径、目标架构、sysroot位置。
  • sysroot是目标板根文件系统的副本,提供了正确的运行时库和头文件。
  • colcon通过--cmake-args -DCMAKE_TOOLCHAIN_FILE支持交叉编译,配合--merge-install简化部署。
  • 交叉编译后务必用fileldd验证产物架构和依赖完整性。
← 上一章 返回目录 下一章 →