Linux交叉编译(x86-arm64)和ROS2项目交叉编译
Linux交叉编译(x86-arm64)和ROS2项目交叉编译
交叉编译
在大多数情况下,我们现在使用QEMU技术在X86平台上运行ARM容器,并将这些容器用作交叉编译工具。然而,由于主机环境与Docker环境的指令集架构不同,编译速度较慢。为了解决这个问题,我们可以直接在X86主机上使用ARM交叉编译工具链。在大型项目的开发过程中,当需要链接大量第三方库时,编译器会提示链接错误等问题。本篇博客主要介绍常用交叉编译的三种方式。
原生ROS2大型项目交叉编译
一般ROS2大型项目的交叉编译需要自己制作离线的sysroot,基于arm64的基础sysroot进行安装一些开发库依赖库。
1.首先制作裸arm64系统基础镜像
比如:ubuntu-base-22.04.3-base-arm64.tar.gz(可以去国内各个镜像源下载)
2.构建一个build sysroot的shell 脚本 构建脚本如下:
#!/bin/bash
mkdir -pv rootfs
sudo tar -xvpzf ubuntu-base-22.04.3-base-arm64.tar.gz -C rootfs
sudo cp -v /etc/resolv.conf rootfs/etc/
sudo sed -i "s/ports.ubuntu.com/mirrors.huaweicloud.com/g" rootfs/etc/apt/sources.list
sudo cp -v ros-archive-keyring.gpg rootfs/usr/share/keyrings/
echo "deb [arch=arm64 signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu jammy main" | sudo tee rootfs/etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt update
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user-static
#可以定制化安装一些基础类库的软件和修改符号链接
sudo bash update.sh3.在base基础上安装依赖软件包、挂载文件和ROS2 安装依赖如下:
#updates.sh
#!/bin/bash
sudo cp -v config.sh rootfs/root/
echo 'bash /root/config.sh' | bash mount.sh -m rootfs
sudo rm -v rootfs/root/config.sh
CURRENT_PATH=$(realpath ./)
echo "Current path: $CURRENT_PATH"
#config.sh
#!/bin/bash
apt update
apt install -y --no-install-recommends build-essential cmake ros-humble-desktop ros-dev-tools libgoogle-glog-dev libprotobuf-dev python3.10-dev libssl-dev libpcl-dev libc6-dev
# 转换绝对路径软链接为相对路径的脚本
TARGET_DIR="/usr/lib" # 指定目标目录
# 使用find命令查找所有的软链接
find "${TARGET_DIR}" -type l | while read -r symlink; do
# 获取软链接指向的目标
target=$(readlink "${symlink}")
# 确定链接是否为绝对路径
if [[ "${target}" = /* ]]; then
# 计算相对路径
relative_target=$(realpath --relative-to="$(dirname "${symlink}")" "${target}")
# 删除原有软链接
rm "${symlink}"
# 创建新的软链接,使用相对路径
ln -sv "${relative_target}" "${symlink}"
echo "链接已更新:${symlink} -> ${relative_target}"
fi
done
echo "所有绝对路径软链接已转换为相对路径。"
#mount.sh
#!/bin/bash
function mnt() {
echo "MOUNTING"
# sudo chown -R root:root ${2}
sudo mount -t proc /proc ${2}/proc
sudo mount -t sysfs /sys ${2}/sys
sudo mount -o bind /dev ${2}/dev
sudo chroot ${2}
}
function umnt() {
echo "UNMOUNTING"
sudo sync
sleep 3
sudo umount ${2}/proc
sudo umount ${2}/sys
sudo umount ${2}/dev
}
if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
mnt $1 $2
umnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
umnt $1 $2
else
echo ""
echo "Either 1'st, 2'nd or both parameters were missing"
echo ""
echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
echo ""
echo "For example: ch-mount -m /media/sdcard/"
echo ""
echo 1st parameter : ${1}
echo 2nd parameter : ${2}
fi4.构造ROS2交叉编译cmake构建文件 cmake文件配置如下:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_CXX_COMPILER_WORKS TRUE)
set(CMAKE_SYSROOT /home/lhf/cross/rootfs)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT};/home/lhf/work/ws)
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)5.切换到ROS2工作控件执行交叉编译
colcon build --merge-install --cmake-force-configure --cmake-args -DCMAKE_TOOLCHAIN_FILE=/home/lhf/cross/toolchain.cmake基于Docker的ROS2的交叉编译
交叉编译需要满足几个条件:
- 使用专门的开发工具链
- 在编译机器上的指定路径模仿目标机器上的依赖库文件系统分布
对于C/C++项目这样就OK了,但是ROS有一点特殊。首先ROS大量的使用了Python。其次ROS通过叠加环境变量的方式来实现不同位置包的查找。例如Ubuntu下默认安装在/opt/ros 目录下。所以,我们在构建跨平台编译环境时,要充分考虑这些差异。
以下内容展示了如何构建一个ROS2 Humble跨平台编译容器镜像。
首先我们构建一个ARM下的原生编译环境,并安装好全部依赖项。
FROM --platform=linux/arm64 ros:humble-ros-base-jammy AS ros2-sdk
RUN apt-get update && apt-get install -y --no-install-recommends \
python3-dev \
ros-humble-cv-bridge \
ros-humble-pcl-conversions \
ros-humble-pcl-msgs \
ros-humble-pcl-ros \
ros-humble-image-transport \
ros-humble-lifecycle \
libpcl-ros-dev \
libpcl-dev \
python3-vtk9 \
vtk9 \
&& rm -rf /var/lib/apt/lists/*然后,引用ROS的官方AMD64开发镜像,并安装跨平台编译工具链。
FROM ros:humble-ros-base-jammy
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
binutils-aarch64-linux-gnu \
build-essential \
python3-dev \
&& rm -rf /var/lib/apt/lists/*CMake跨平台编译时需要指定一个目标平台库路径。此处将其设定为/rootfs
# CMake sysroot
ENV SYSROOT=/rootfs然后将ARM平台下的开发库导入到该路径下,并修复相对路径导致的链接错误
COPY --from=ros2-sdk /usr ${SYSROOT}/usr
RUN ln -sf /sysroot/usr/lib/aarch64-linux-gnu/libpython3.10.so /usr/lib/aarch64-linux-gnu/libpython3.10.so
RUN ln -sf /sysroot/usr/lib/aarch64-linux-gnu/blas/libblas.so.3 /usr/lib/aarch64-linux-gnu/libblas.so.3
RUN ln -sf /sysroot/usr/lib/aarch64-linux-gnu/lapack/liblapack.so.3 /usr/lib/aarch64-linux-gnu/liblapack.so.3
RUN ln -sf /sysroot/usr/lib/qt5/bin/qmake /usr/lib/aarch64-linux-gnu/qt5/bin/qmake此时镜像中的ROS库为AMD64版本,但是其路径信息与ARM一致。因此,我们可以通过替换文件的方式,引入ARM平台的ROS库。
# ROS2 SDK
RUN rm -rf /opt/ros
COPY --from=ros2-sdk /opt/ros /opt/ros到此,跨平台编译环境的主要依赖项目都准备完毕。接下来还需要两个关键文件。
第一个是CMake的工具链说明文件:
# ROS Humble cross compiling environment for ARM64 on Linux.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_SYSROOT $ENV{SYSROOT})
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH
/usr/aarch64-linux-gnu;${SYSROOT};/opt/ros/humble;$ENV{ROS_PACKAGE_PATH})
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)第二个是容器的入口文件
#!/bin/bash
source /opt/ros/humble/setup.bash
exec "$@"至此,跨平台编译镜像制作完成。
docker run --rm \
-v $(pwd):/workspace \
--workdir=/workspace \
-e ROS_PACKAGE_PATH=/opt/ros_pkgs \
ros-cross-build:humble \
colcon build \
--packages-ignore to_be_ignored \
--cmake-args \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=/toolchain.cmake \
--packages-up-to your-package现代化交叉编译
在小型和中型项目中,我更推荐这种方式,可以避免动态连接库搜索和一些依赖库的问题。我们依次来介绍一下。
增加ARM64源(比如:ubuntu 2404)
我们可以看到x64的 /etc/apt/sources.list.d/ubuntu.sources
/etc/apt/sources.list.d$ cat ubuntu.sources
Types: deb
URIs: http://mirrors.aliyun.com/ubuntu/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Architectures: amd64
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main restricted universe multiverse
Architectures: amd64
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg增加阿里云arm64的镜像源
Types: deb
URIs: http://mirrors.aliyun.com/ubuntu-ports/
Suites: noble noble-updates noble-backports
Components: main universe restricted multiverse
Architectures: arm64
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
## Ubuntu security updates. Aside from URIs and Suites,
## this should mirror your choices in the previous section.
Types: deb
URIs: http://mirrors.aliyun.com/ubuntu-ports/
Suites: noble-security
Components: main universe restricted multiverse
Architectures: arm64
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg我们能看到不同体系结构的镜像文件如下: http://mirrors.aliyun.com/ubuntu-ports/dists/noble/
Parent directory/ - -
Contents-arm64.gz 48.8 MB 2024-04-24 14:39
Contents-armhf.gz 45.3 MB 2024-04-24 17:53
Contents-ppc64el.gz 46.8 MB 2024-04-24 19:50
Contents-riscv64.gz 45.4 MB 2024-04-24 21:09
Contents-s390x.gz 45.4 MB 2024-04-24 22:24增加arm64架构对于apt
sudo dpkg --add-architecture arm64
sudo apt update安装依赖库
sudo apt install libncurses-dev:arm64
sudo apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnuCMake和Make构建arm64项目
## CMAKE
cmake -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ ..
## Make
wget https://www.nano-editor.org/dist/v6/nano-6.2.tar.xz
tar zxvf nano-6.2.tar.xz
cd nano-6.2
# CFLAGS="-static-libgcc -static" CXXFLAGS="-static-libgcc -static-libstdc++ -static" LDFLAGS="-static" could be removed if you don't want to build static binary
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ CFLAGS="-static-libgcc -static" CXXFLAGS="-static-libgcc -static-libstdc++ -static" LDFLAGS="-static" ./configure --host=aarch64-linux-gnu --build=x86_64-pc-linux-gnu
make
# check the binary
ls src/nano总结
对于大型交叉编译项目优先使用第一种方式,其次第二种方式,最后如果基于ubuntu2404及以上系统可以使用第三种方式。各有优劣势,主要根据业务需求进行技术选型。