Title: ros2 嵌入式程序的交叉编译



关联博文:

- ros2 嵌入式程序的交叉编译

- ros2 嵌入式程序的交叉编译 —— 镜像文件的制作

- ros2 嵌入式程序的交叉编译 —— 基于 Chroot 文件系统隔离


0. 前言

针对简单情况, 只要设置好交叉编译工具链中的环境变量就可以.


I. 系统环境

1. 主机 host 操作系统

lsb_release -a

No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy

printenv ROS_DISTRO

humble

2. 嵌入式 embedded 操作系统

No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy

humble


II. 主机上配置编译环境

1. 主机上安装交叉编译工具

sudo apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu

2. 获得嵌入式系统的镜像文件

我用的正好是现成的

https://bingda.yuque.com/staff-hckvzc/owgkl4/kzp230ib7a8sddsx

或者使用 dd 命令自制一个 (ros2 嵌入式程序的交叉编译 —— 镜像文件的制作), 如

dd if=/dev/nvme0p1p8 of=/mnt/disk/rootfs.img

镜像文件下载保存为 ~/crosscompile_ws/rootfs_image/opi5pro_22.04_humble_v1.0.1.img

3. 在主机上挂载嵌入式系统的镜像文件

创建挂载点

sudo mkdir -p /mnt/embedded_rootfs

检查镜像文件的分区结构

通过 file 命令查看镜像文件的类型,确认是否包含分区表(多数嵌入式镜像会包含多个分区)

file ~/crosscompile_ws/rootfs_image/opi5pro_22.04_humble_v1.0.1.img

如果是包含分区表的镜像,需通过 fdisk 查看分区偏移量(即根文件系统所在分区的起始位置)

fdisk -l ~/crosscompile_ws/rootfs_image/opi5pro_22.04_humble_v1.0.1.img

在这里插入图片描述

计算挂载偏移量(针对多分区镜像), 分区的起始扇区 × 扇区大小(通常 512 字节)= 偏移量(单位:字节)

offset=$((2158592*512))

挂载嵌入式文件系统分区

sudo mount -o loop,offset=$offset -t ext4 ~/crosscompile_ws/rootfs_image/opi5pro_22.04_humble_v1.0.1.img /mnt/embedded_rootfs

4. 针对嵌入式 python 库问题的处理

ros2 在编译时会调用 python 动态库来确定版本等信息, 但是总是找不到交叉编译需要的嵌入式 python 动态库, 最后只能将镜像文件中的嵌入式 python 动态库拷贝到主机上, 供交叉编译时调用.

sudo cp /mnt/embedded_rootfs/usr/lib/aarch64-linux-gnu/libpython3.10.so /usr/lib/aarch64-linux-gnu/

如果没有这一步, 编译时会出现如下错误

在这里插入图片描述


III. 主机上编译

1. 下载 ROS2 测试代码到主机上

clone ros2 代码并check 对应版本

https://github.com/ros2/examples.git
git branch -a
git checkout humble

2. 编写交叉编译工具链文件

# toolchain-aarch64.cmake
set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "aarch64-linux-gnu-g++")

set(CMAKE_SYSROOT /mnt/embedded_rootfs)

set(PYTHON_SOABI cpython-310-aarch64-linux-gnu)
set(PythonExtra_EXTENSION_SUFFIX cpython-310-aarch64-linux-gnu)    

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

文件命名为 toolchain-aarch64.cmake, 存储在 ~/crosscompile_ws/toolchain-aarch64.cmake.

3. 进行交叉编译

colcon build \
  --merge-install \
  --cmake-force-configure \
  --cmake-args \
-DCMAKE_TOOLCHAIN_FILE=/home/robot/crosscompile_ws/toolchain-aarch64.cmake \
-DCMAKE_BUILD_TYPE=Release
--event-handlers console_direct+

-DCMAKE_TOOLCHAIN_FILE 指定工具链文件, -DCMAKE_BUILD_TYPE 设置编译类型.

都是简单 example 程序, 没有遇到什么问题, 编译成功

在这里插入图片描述

VI. 嵌入式上运行

1. 传输交叉编译后的文件

传输编译后获得的整个 install 文件夹到嵌入式主控板上, 命令格式

scp -r host_directory user@remote_host_ip:/remote_directory

传输复制命令

sudo scp -r ./install/* bingda@192.168.88.30:/home/bingda/ros2_install/

2. 嵌入式环境下运行

cd ~/ros2_install
source ./setup.bash
ros2 run examples_rclcpp_minimal_publisher  publisher_member_function
cd ~/ros2_install
source ./setup.bash
ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function

在这里插入图片描述


VII. 结论

这个方法最简单, 主要是文件系统是现成的镜像, 省去了利用 docker 获得嵌入式文件系统这些步骤.
但这种方法适合于简单的 ros 2 应用程序的交叉编译, 对于 package 之间相互引用的复杂 ros 2 应用存在诸多问题, 如 “附 II. 交叉编译中可能的问题”.
针对复杂情况, 请参考下一篇博文.


附 I. 镜像挂载脚本实现

镜像挂载自动化脚本 auto_mount.sh 实现

#!/bin/bash

IMAGE_FILE="/home/robot/crosscompile_ws/rootfs_image/opi5pro_22.04_humble_v1.0.1.img"
MOUNT_POINT="/mnt/embedded_rootfs"

# 检查镜像文件是否存在
if [ ! -f "$IMAGE_FILE" ]; then
    echo "错误: 镜像文件 $IMAGE_FILE 不存在!"
    exit 1
fi

# 创建挂载点(如果不存在)
if [ ! -d "$MOUNT_POINT" ]; then
    echo "创建挂载点目录: $MOUNT_POINT"
    sudo mkdir -p "$MOUNT_POINT"
fi

# 提取第二个分区(Linux filesystem)的起始扇区
# 从fdisk输出中过滤包含"Linux filesystem"的行,然后提取起始扇区数值
START_SECTOR=$(fdisk -l "$IMAGE_FILE" | grep "Linux filesystem" | awk '{print $2}')

if [ -z "$START_SECTOR" ] || ! [[ "$START_SECTOR" =~ ^[0-9]+$ ]]; then
    echo "错误: 无法提取有效的起始扇区!"
    echo "请检查分区是否存在或分区类型是否正确"
    exit 1
fi

# 计算偏移量(扇区大小默认512字节)
SECTOR_SIZE=512
OFFSET=$((START_SECTOR * SECTOR_SIZE))

echo "找到分区: 起始扇区=$START_SECTOR, 扇区大小=$SECTOR_SIZE字节, 偏移量=$OFFSET字节"

# 执行挂载操作
echo "正在挂载镜像到 $MOUNT_POINT..."
sudo mount -o loop,offset=$OFFSET -t ext4 "$IMAGE_FILE" "$MOUNT_POINT"

# 检查挂载是否成功
if [ $? -eq 0 ]; then
    echo "成功挂载镜像到 $MOUNT_POINT"
else
    echo "挂载失败!"
    exit 1
fi

镜像卸载 auto_unmount.sh 实现

sudo umount /mnt/embedded_rootfs

权限改为可执行 +x.


附 II. 交叉编译中可能的问题

其他稍微复杂一点的 ros2 程序交叉编译中可能遇到一些问题.

比如在编译自定义 ros2 消息包时, 可能遇到如下问题.

1. 自定义 ros2 消息包中的问题

Traceback (most recent call last):
File “/mnt/embedded_rootfs/opt/ros/humble/share/rosidl_generator_cpp/cmake/…/…/…/lib/rosidl_generator_cpp/rosidl_generator_cpp”, line 38, in
sys.exit(main())
File “/mnt/embedded_rootfs/opt/ros/humble/share/rosidl_generator_cpp/cmake/…/…/…/lib/rosidl_generator_cpp/rosidl_generator_cpp”, line 34, in main
generate_cpp(args.generator_arguments_file)
File “/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_generator_cpp/init.py”, line 39, in generate_cpp
return generate_files(
File “/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_cmake/init.py”, line 59, in generate_files
assert (template_basepath / template_filename).exists(),
AssertionError: Could not find template: idl__type_support.hpp.em

[解决方法 1]

主机中能够查找到这个确实的文件

find /opt/ros/humble -name "idl__type_support.hpp.em"

但是镜像文件系统中无法查找到该文件

sudo find /mnt/embedded_rootfs -name "idl__type_support.hpp.em"

处理方法利用主机中的模版文件, 交叉编译命令中添加 -Drosidl_generator_cpp_DIR=/opt/ros/humble/share/rosidl_generator_cpp/cmake

colcon build \
		--merge-install \
		--cmake-force-configure \
		--cmake-args \
		-DCMAKE_TOOLCHAIN_FILE=/home/robot/crosscompile_ws/toolchain-aarch64.cmake \
		-Drosidl_generator_cpp_DIR=/opt/ros/humble/share/rosidl_generator_cpp/cmake

或者工具链配置文件 toolchain-aarch64.cmake 中增加

# 指定 rosidl_generator_cpp 的 CMake 配置文件路径
set(rosidl_generator_cpp_DIR "/opt/ros/humble/share/rosidl_generator_cpp/cmake" CACHE PATH "Path to rosidl_generator_cpp CMake config")

因为 rosidl_generator_cpp 是编译阶段使用的代码生成工具, 依赖主机架构的可执行程序, 所以该路径指向的是主机上的 ROS 2 安装目录 (而非目标镜像文件系统路径).

[解决方法 2]

在运行 colcon build 进行编译前, 在主机终端中 source 镜像系统中 ros2 setup.bash 以此来该终端的环境变量为镜像系统的环境变量.

source /mnt/embedded_rootfs/opt/ros/humble/setup.bash

2. 复杂应用程序中的问题

类似如 nav2 相关的复杂应用程序甚至会形成编译顺序的循环约束 “a cycle in the constraint graph”, 使得交叉编译无法执行下去.

在这里插入图片描述

[解决方法 1]

参考 ros2 嵌入式程序的交叉编译 —— 基于 Chroot 文件系统隔离.


参考资料

[1] ros2_humble_foxy_x86交叉编译环境搭建

[2] ros2/arm免依赖交叉编译教程


版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/woyaomaishu2/article/details/152664753?spm=1001.2014.3001.5502
本文作者:wzf@robotics_notes

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐