
CC-BY-SA-4.0 | © 2026 KY (kyshipit)x86 交叉编译 ROS2 Jazzy 到 ARM64从零跑通 踩坑实录一、为什么要在 x86 电脑上交叉编译板端colcon build仅适合早期验证 demo。随着项目依赖增多板端全量编译耗时会从几分钟膨胀至十几分钟甚至更久同时嵌入式开发板内存与存储有限工程量大时易触发内存溢出或磁盘告警编译还会挤占运行调试资源此外接入 CI/CD 后板端编译难以标准化环境差异也易引入兼容问题。交叉编译将构建从目标硬件解耦以配置复杂度换取编译效率、产物一致性与板端资源释放。本文梳理了工具链、sysroot、自定义消息包等全流程踩坑点助你一次跑通。二、先搞懂几个核心概念2.1 交叉编译是什么在 A 架构的电脑上编译出能在 B 架构上运行的程序。角色本文对应说明宿主机 (Host)x86_64 Ubuntu 24.04你敲代码、跑编译的电脑目标机 (Target)RK3588 (ARM64)最终运行程序的板子交叉编译器aarch64-buildroot-linux-gnu-gcc能生成 ARM64 指令的编译器 类比交叉编译器就像一个翻译官——你用中文C 源码写了一封信翻译官直接把它翻译成英文ARM64 机器码收信人ARM64 CPU直接就能读懂。2.2 sysroot 是什么为什么需要两个sysroot 目标板文件系统的镜像副本。编译器在编译时需要两样东西头文件.h告诉编译器这个函数长什么样库文件.so在链接时把函数实现拼进去。交叉编译时你总不能把开发板的/usr/lib整个搬过来用所以我们需要一个精简版的根文件系统这就是 sysroot。为什么需要两个 sysrootSysroot 类型位置内容提供什么① 工具链自带 sysroot工具链目录内libc, libstdc, 基础系统库, OpenCV完整版C/C 运行时基础② 板端 ROS2 sysroot自己从开发板提取/opt/ros/jazzy ROS 依赖的系统库 (libyaml, libspdlog...)ROS2 及项目依赖 类比工具链 sysroot 相当于厨房里的锅碗瓢盆基础工具板端 sysroot 相当于菜谱和食材ROS2 库。做菜两样都得有。2.3 CMake 工具链文件是什么CMake 默认用宿主机的gcc。交叉编译时我们需要一份说明书告诉 CMake别用本机的 gcc用那个交叉编译器头文件去这个目录找库去那个目录找……这份说明书就是工具链文件Toolchain File一个以.cmake结尾的脚本。2.4 colcon 在中间做了什么colcon本身不懂交叉编译它只是把参数透传给 CMake。所有交叉编译的魔法都发生在工具链文件 CMakeLists.txt里。流程如下你运行./build-linux.sh脚本调用colcon build --cmake-args ...colcon 对每个 ROS2 包执行cmake -DCMAKE_TOOLCHAIN_FILEtoolchain.cmake ..cmake 读取工具链文件和包的 CMakeLists.txt生成 Makefilemake 编译、链接生成 ARM64 ELF 文件三、环境准备3.1 需要什么项目说明宿主机x86_64 Ubuntu 24.04目标板RK3588运行 Ubuntu 24.04已装 ROS2 Jazzy (ARM64)交叉工具链/opt/atk-dlrk3588-toolchain正点原子提供板端 sysroot从开发板提取放在~/software/rk_sysrootROS2 工作区~/work/ros-robot含src/eai_bot等包宿主机工具colcon、cmake、make、python3、ROS2 Jazzy仅用其代码生成工具3.2 提取板端 sysroot如果还没有在开发板上执行# 打包 ROS2 安装目录 tar czf ros2_jazzy.tar.gz /opt/ros/jazzy # 打包 ROS 依赖的系统库关键很多人漏了这步 tar czf sys_libs.tar.gz \ /lib/aarch64-linux-gnu/libyaml* \ /lib/aarch64-linux-gnu/libspdlog* \ /lib/aarch64-linux-gnu/liblttng* \ /usr/lib/aarch64-linux-gnu/libconsole_bridge* \ /usr/lib/aarch64-linux-gnu/liborocos-kdl* \ /usr/lib/aarch64-linux-gnu/liblog4cxx* # ↑ 按需补充后面踩坑 6.3 会讲怎么发现缺了哪些拷贝到宿主机后解压到~/software/rk_sysroot/保持目录结构~/software/rk_sysroot/ ├── opt/ros/jazzy/ ← ROS2 本体 ├── lib/aarch64-linux-gnu/ ← 系统库 ├── usr/lib/aarch64-linux-gnu/ └── usr/include/ ← 头文件如有需要3.3 宿主机安装构建依赖sudo apt update sudo apt install -y \ build-essential cmake ninja-build \ python3-colcon-common-extensions \ ros-jazzy-rosidl-default-generators \ ros-jazzy-rclcpp⚠️ 注意宿主机装 ROS2 只是为了让 colcon 能找到代码生成器编译出来的二进制是 ARM64 的跟宿主机 ROS2 无关。四、三个核心文件详解这是全文最重要的部分。理解了这三个文件交叉编译就通了。4.1 工具链文件toolchain_rk3588.cmake放在工作区根目录~/work/ros-robot/toolchain_rk3588.cmakecmake# 1. 告诉 CMake目标是什么系统 set(CMAKE_SYSTEM_NAME Linux) # 目标是 Linux set(CMAKE_SYSTEM_PROCESSOR aarch64) # 目标是 ARM64 # 2. 指定交叉编译器 set(TOOLCHAIN_DIR /opt/atk-dlrk3588-toolchain) set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/aarch64-buildroot-linux-gnu-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/aarch64-buildroot-linux-gnu-g) # 3. 指定工具链自带 sysroot set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot) # 4. 控制 CMake 的搜索行为非常关键 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # 找程序只在宿主机找 set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 找库只在 sysroot 找 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 找头文件只在 sysroot 找 set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # 找 CMake 包只在 sysroot 找逐行解读设置如果不设会怎样CMAKE_SYSTEM_NAMECMake 以为你在给本机编译不会加交叉编译标志CMAKE_C/CXX_COMPILERCMake 用本机gcc编出 x86 二进制板子跑不了CMAKE_SYSROOT链接器找不到目标架构的libc直接报错PROGRAM NEVERCMake 可能去 sysroot 里找 Python试图运行 ARM64 的 Python → 崩溃LIBRARY/INCLUDE ONLYCMake 可能链接宿主机的 x86 版.so→ 链接报错或运行时段错误4.2 构建脚本build-linux.sh放在工作区根目录#!/bin/bash set -e # 路径配置按需修改 ROOT_PWD$(cd $(dirname $0) pwd) SYSROOT_ROS${HOME}/software/rk_sysroot TOOLCHAIN_DIR/opt/atk-dlrk3588-toolchain HOST_PYTHON3$(which python3) # 清理旧构建 rm -rf build install log # 开始构建 echo 开始交叉编译... colcon build \ --packages-select eai_bot \ --cmake-args \ -DCMAKE_TOOLCHAIN_FILE${ROOT_PWD}/toolchain_rk3588.cmake \ -DCMAKE_FIND_ROOT_PATH${SYSROOT_ROS};${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot \ -DROS_SYSROOT${SYSROOT_ROS} \ -DPython3_EXECUTABLE${HOST_PYTHON3} \ -DTARGET_SOCrk3588 \ -DCMAKE_BUILD_TYPERelease echo 编译完成产物在 install/ 目录下参数逐个拆解参数作用小白理解CMAKE_TOOLCHAIN_FILE指定工具链文件路径告诉 CMake 去哪读那份说明书CMAKE_FIND_ROOT_PATH额外的搜索根目录除了工具链 sysroot也去板端 sysroot 里找找ROS_SYSROOT自定义变量板端 sysroot 的路径CMakeLists.txt 里要用Python3_EXECUTABLE强制用宿主机 Python代码生成用宿主机的 Python别去找 ARM64 的TARGET_SOC自定义标识目标芯片方便 CMakeLists.txt 里做条件编译CMAKE_BUILD_TYPERelease开启优化编译出的程序更快更小4.3 包的CMakeLists.txt关键片段 注释cmakecmake_minimum_required(VERSION 3.16) project(eai_bot) if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) endif() # 消息生成 # ⚠️ 禁用 Python 消息生成器 # 原因交叉编译时 sysroot 里没有 Python 开发头文件 # 如果不禁用CMake 会报 Could NOT find Python3 set(CMAKE_DISABLE_FIND_PACKAGE_rosidl_generator_py TRUE) find_package(rosidl_default_generators REQUIRED) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) find_package(sensor_msgs REQUIRED) # 定义自定义消息 set(msg_files msg/Point2D.msg msg/Box.msg msg/DetectionResult.msg ) # ⚠️ 注意不要加 LANGUAGE cpp这不是合法参数 rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} DEPENDENCIES std_msgs sensor_msgs ) # OpenCV # 使用工具链 sysroot 中的 OpenCV完整版 set(TOOLCHAIN_SYSROOT /opt/atk-dlrk3588-toolchain/aarch64-buildroot-linux-gnu/sysroot) set(OPENCV_INCLUDE_DIRS ${TOOLCHAIN_SYSROOT}/usr/include/opencv4) set(OPENCV_LIB_DIR ${TOOLCHAIN_SYSROOT}/usr/lib) set(OPENCV_LIBS ${OPENCV_LIB_DIR}/libopencv_core.so ${OPENCV_LIB_DIR}/libopencv_imgproc.so ${OPENCV_LIB_DIR}/libopencv_imgcodecs.so ${OPENCV_LIB_DIR}/libopencv_videoio.so ${OPENCV_LIB_DIR}/libopencv_highgui.so ) # 源文件 file(GLOB EAI_BOT_SRC src/*.cpp) # 可执行文件 add_executable(eai_bot_app ${EAI_BOT_SRC}) target_include_directories(eai_bot_app PRIVATE include ${OPENCV_INCLUDE_DIRS} ) # 链接 ROS2 和 OpenCV ament_target_dependencies(eai_bot_app rclcpp std_msgs sensor_msgs) target_link_libraries(eai_bot_app ${OPENCV_LIBS}) # 让自定义消息头文件能被找到 rosidl_get_typesupport_target(cpp_typesupport_target ${PROJECT_NAME} rosidl_typesupport_cpp) target_link_libraries(eai_bot_app ${cpp_typesupport_target}) # 交叉编译链接修复 # 这是交叉编译最容易出错的地方 if(DEFINED ROS_SYSROOT) target_link_options(eai_bot_app PRIVATE # 把链接器的 sysroot 切换到板端 # 这样链接器才能在板端目录里找到 ROS 依赖的系统库 -Wl,--sysroot${ROS_SYSROOT} # 告诉链接器解析 .so 的间接依赖时也去这些目录找找 -Wl,-rpath-link,${TOOLCHAIN_SYSROOT}/usr/lib -Wl,-rpath-link,${TOOLCHAIN_SYSROOT}/lib -Wl,-rpath-link,${ROS_SYSROOT}/lib/aarch64-linux-gnu -Wl,-rpath-link,${ROS_SYSROOT}/usr/lib/aarch64-linux-gnu # 显式指定动态链接器路径 -Wl,--dynamic-linker/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 ) endif() # 安装规则 install(TARGETS eai_bot_app DESTINATION lib/${PROJECT_NAME}) # ⚠️ 用 CMAKE_SOURCE_DIR 避免相对路径层级算错 install(DIRECTORY ${CMAKE_SOURCE_DIR}/../../model/ DESTINATION model) ament_package()4.4 三者关系请特别注意CMakeLists.txt的位置——它是整个交叉编译的主战场工具链文件只解决了用什么编的问题而编什么、怎么链、装到哪全在CMakeLists.txt里。三个文件的分工文件角色管什么不管什么toolchain_rk3588.cmake全局基础设施编译器选型、基础 sysroot、搜索策略具体链接哪些库、消息怎么生成CMakeLists.txt包级构建逻辑 ⭐依赖查找、消息生成、链接选项、安装规则用哪个编译器、搜索策略build-linux.sh胶水/入口组装参数、调用 colcon、清理环境任何构建逻辑本身五、踩坑实录每个坑都按 错误信息 → 一句话原因 → 修复方法 → 原理解释的格式整理方便你对号入座。坑 1rosidl_generate_interfaces报LANGUAGE文件不存在错误信息CMake Error: rosidl_generate_interfaces() the passed file LANGUAGE doesnt existLANGUAGE cpp不是这个函数的合法参数CMake 把LANGUAGE当成了消息文件路径。修复# ❌ 错误写法 rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} LANGUAGE cpp) # ✅ 正确写法直接删掉 LANGUAGE cpp rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files})rosidl_generate_interfaces()只接受消息文件路径列表和DEPENDENCIES关键字。它不像某些 CMake 函数那样有LANGUAGE选项。CMake 会把所有未识别的参数都当作文件路径来检查。坑 2找不到 Python3 开发组件错误信息Could NOT find Python3 (missing: Python3_INCLUDE_DIRS Python3_LIBRARIES)原因** ROS2 消息生成默认包含 Python 版本它会去 sysroot 里找 Python 开发头文件——但 sysroot 里没有。修复三管齐下cmake# ① CMakeLists.txt 中禁用 Python 生成器 set(CMAKE_DISABLE_FIND_PACKAGE_rosidl_generator_py TRUE) find_package(rosidl_default_generators REQUIRED)bash# ② build-linux.sh 中指定宿主机 Python -DPython3_EXECUTABLE$(which python3)cmake# ③ 工具链文件中确保有这一行 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)rosidl_default_generators默认会调用rosidl_generator_py来生成 Python 绑定。交叉编译时我们不需要也做不到在板端运行 Python 生成脚本所以直接禁掉。Python3_EXECUTABLE保证其他需要 Python 的步骤如ament_cmake脚本用的是宿主机能跑的 Python。坑 3链接时找不到libyaml、libspdlog、liblttng-ust错误信息ld: warning: libyaml-0.so.2, needed by .../librcl.so, not found ld: undefined reference to spdlog::logger::log(...)原因** 板端 sysroot 只拷了/opt/ros/jazzy漏掉了 ROS2 依赖的系统库。修复Step 1在开发板上查缺哪些库ldd /opt/ros/jazzy/lib/librcl.so | grep not foundStep 2把缺失的库从开发板拷到宿主机 sysrootscp firefly192.168.1.100:/lib/aarch64-linux-gnu/libyaml-0.so.2 \ ~/software/rk_sysroot/lib/aarch64-linux-gnu/Step 3确保CMakeLists.txt中有-rpath-link见 4.3 节。-rpath-link不影响运行时只影响链接时解析间接依赖。比如你的程序链接了librclcpp.so而librclcpp.so又依赖libyaml-0.so.2链接器需要找到libyaml来验证符号——-rpath-link就是告诉它去这些目录找。坑 4cv::VideoCapture未定义错误信息error: VideoCapture is not a member of cv项目自带的 OpenCV 库不完整缺少videoio模块。修复不用项目自带的改用工具链 sysroot 中的完整 OpenCV见 4.3 节 OpenCV 部分。工具链厂商通常会提供与编译器 ABI 兼容的完整 OpenCV。项目自带的可能是裁剪版缺少某些模块。用工具链版本还能避免 GCC 版本不匹配导致的 ABI 问题。坑 5找不到动态链接器ld-linux-aarch64.so.1错误信息ld: cannot find /lib/ld-linux-aarch64.so.1 inside .../rk_sysroot--sysroot切换到板端后链接器按默认路径/lib/ld-linux-aarch64.so.1查找但 Ubuntu 把它放在/lib/aarch64-linux-gnu/下。修复target_link_options(eai_bot_app PRIVATE -Wl,--dynamic-linker/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 )这是 Ubuntu/Debian 特有的路径布局。--dynamic-linker指定的是写入 ELF 文件的PT_INTERP段即程序运行时由内核加载哪个动态链接器。路径必须是目标板上的绝对路径不是宿主机上的。坑 6安装阶段找不到模型文件错误信息CMake Error: file INSTALL cannot find .../src/eai_bot/../model/coco_80_labels_list.txt../model从src/eai_bot出发实际指向src/model但文件在工作区根目录的model/。修复# ❌ 容易算错层级 install(DIRECTORY ../model/ DESTINATION model) # ✅ 用 CMAKE_SOURCE_DIR不依赖相对层级 install(DIRECTORY ${CMAKE_SOURCE_DIR}/../../model/ DESTINATION model) # ✅ 更好的做法在 build-linux.sh 中传入路径 # -DMODEL_DIR${ROOT_PWD}/modelCMake 的CMAKE_SOURCE_DIR指向当前包的源码目录即src/eai_bot往上两级才是工作区根目录。用变量传入路径最不容易出错。六、实践总结sysroot 管理明确区分工具链 sysroot 和板端 ROS sysroot 的作用合理使用 CMAKE_SYSROOT 和 -Wl,--sysroot、-rpath-link。依赖完整性在开发板上用 ldd -r 检查 ROS 库的依赖并确保 sysroot 中都有对应文件确保没有not found。优先用工具链自带的第三方库如 OpenCV避免 ABI 不兼容。路径通用化尽量使用变量或 CMake 内置变量减少硬编码。禁用不必要的功能例如不需要 Python 消息支持时禁用生成器避免不必要的依赖。------------------------------------------------------------ 相关内容托管GitHub更新以仓库为准。 如有疏漏欢迎指正。 GitHub仓库kyshipit/tech‑notes---------------------------------------------------------------