在 x86_64 的 EndeavourOS 上临时编译和验证 arm64/aarch64 的 ROS 1 工程时,直接维护一套实体 Ubuntu 20.04 ARM 环境比较麻烦。这里使用 Podman + Distrobox + qemu-user-static,在本机运行 Ubuntu 20.04 arm64 容器,再安装 ROS Noetic 和 GCC 13,最后把结果提交成 localhost/noetic:aarch64 镜像,方便重复创建开发容器。

这套流程延续了我之前用 Distrobox 搭 ROS Melodic 的思路,相关背景可以看这篇:https://www.helywin.com/posts/20260605124741/。本文把 aarch64、Ubuntu 20.04、ROS Noetic、华为云 ubuntu-ports、GCC 13 PPA 和 qemu binfmt 的注意点合在一起,按完整过程记录。

当前完成状态

  • 容器名:noetic-aarch64
  • 容器 home:~/distrobox-homes/noetic-aarch64
  • 本地镜像:localhost/noetic:aarch64
  • 基础镜像:localhost/ubuntu:20.04-arm64-huaweicloud
  • 架构:aarch64 / arm64
  • ROS:ros-noetic-desktop-full
  • 默认编译器:gcc / g++ 13.1.0

镜像源原则

  • Ubuntu apt 使用华为云 ubuntu-ports
  • ROS apt 使用华为云 ROS 镜像
  • GCC 13 使用 Ubuntu Toolchain PPA;focal arm64 官方源没有 gcc-13
  • 包管理只用 apt,不在安装流程里运行 rosdep

一、宿主机准备

1.1 安装 Podman、Distrobox 和 qemu

BASH
sudo pacman -S --needed podman distrobox
sudo pacman -S --needed qemu-user-static qemu-user-static-binfmt

验证:

BASH
podman --version
distrobox --version
cat /proc/sys/fs/binfmt_misc/qemu-aarch64

1.2 修复 aarch64 setuid 支持

如果 aarch64 容器内执行 sudo 报 effective uid 相关错误,需要把 qemu aarch64 binfmt flags 调整为 FPC

BASH
sudo tee /etc/binfmt.d/qemu-aarch64-static.conf << 'EOF'
:qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:FPC
EOF

sudo systemctl restart systemd-binfmt.service

验证 flags 应包含 C

BASH
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
# flags: POCF

二、准备 Ubuntu 20.04 arm64 基础镜像

如果已经存在 localhost/ubuntu:20.04-arm64-huaweicloud,可以跳过本节。

BASH
mkdir -p ~/distrobox-homes/images

curl -L --retry 3 --continue-at - \
  -o ~/distrobox-homes/images/focal-server-cloudimg-arm64-root.tar.xz \
  https://mirrors.huaweicloud.com/ubuntu-cloud-images/focal/current/focal-server-cloudimg-arm64-root.tar.xz

podman import --arch arm64 --os linux \
  --change 'CMD ["/bin/bash"]' \
  --change 'ENV LANG=C.UTF-8' \
  ~/distrobox-homes/images/focal-server-cloudimg-arm64-root.tar.xz \
  localhost/ubuntu:20.04-arm64

配置华为云 apt 源并提交基础镜像:

BASH
podman rm -f ubuntu2004-aarch64-seed 2>/dev/null || true

podman run --name ubuntu2004-aarch64-seed \
  --platform linux/arm64 \
  localhost/ubuntu:20.04-arm64 \
  /bin/bash -lc '
set -euo pipefail

cat > /etc/apt/sources.list << "EOF"
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-updates main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-backports main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-security main restricted universe multiverse
EOF

cat > /etc/apt/apt.conf.d/99huaweicloud-direct << "EOF"
Acquire::http::Proxy::mirrors.huaweicloud.com "DIRECT";
Acquire::https::Proxy::mirrors.huaweicloud.com "DIRECT";
EOF

apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
  apt-utils bash-completion build-essential ca-certificates curl git gnupg \
  iproute2 iputils-ping less locales lsb-release passwd procps \
  software-properties-common sudo tzdata vim-tiny wget xz-utils

apt-get clean
rm -rf /var/lib/apt/lists/*
'

podman commit \
  --change 'CMD ["/bin/bash"]' \
  --change 'ENV LANG=C.UTF-8' \
  ubuntu2004-aarch64-seed \
  localhost/ubuntu:20.04-arm64-huaweicloud

podman rm ubuntu2004-aarch64-seed

三、构建 Noetic aarch64 镜像

BASH
podman rm -f noetic-aarch64-seed 2>/dev/null || true

podman run --name noetic-aarch64-seed \
  --platform linux/arm64 \
  localhost/ubuntu:20.04-arm64-huaweicloud \
  /bin/bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive

cat > /etc/apt/sources.list << "EOF"
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-updates main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-backports main restricted universe multiverse
deb https://mirrors.huaweicloud.com/ubuntu-ports/ focal-security main restricted universe multiverse
EOF

cat > /etc/apt/apt.conf.d/99huaweicloud-direct << "EOF"
Acquire::http::Proxy::mirrors.huaweicloud.com "DIRECT";
Acquire::https::Proxy::mirrors.huaweicloud.com "DIRECT";
EOF

apt-get update
apt-get install -y ca-certificates curl gnupg lsb-release

install -d -m 0755 /usr/share/keyrings
curl -fsSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc \
  | gpg --dearmor -o /usr/share/keyrings/ros-archive-keyring.gpg

cat > /etc/apt/sources.list.d/ros-latest.list << "EOF"
deb [signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] https://mirrors.huaweicloud.com/ros/ubuntu/ focal main
EOF

gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys \
  C8EC952E2A0E1FBDC5090F6A2C277A0A352154E5 \
  60C317803A41BA51845E371A1E9377A2BA9EF27F
gpg --batch --export \
  C8EC952E2A0E1FBDC5090F6A2C277A0A352154E5 \
  60C317803A41BA51845E371A1E9377A2BA9EF27F \
  | gpg --dearmor -o /usr/share/keyrings/ubuntu-toolchain-r-test.gpg
rm -rf /root/.gnupg

cat > /etc/apt/sources.list.d/ubuntu-toolchain-r-test.list << "EOF"
deb [signed-by=/usr/share/keyrings/ubuntu-toolchain-r-test.gpg] https://ppa.launchpadcontent.net/ubuntu-toolchain-r/test/ubuntu focal main
EOF

apt-get update
apt-get install -y \
  curl gnupg lsb-release software-properties-common \
  git build-essential cmake pkg-config \
  python3-rosdep python3-rosinstall python3-rosinstall-generator \
  python3-wstool python3-vcstool python3-catkin-tools \
  mesa-utils \
  gcc-13 g++-13 \
  ros-noetic-desktop-full

update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130
update-alternatives --set gcc /usr/bin/gcc-13
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 90
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 130
update-alternatives --set g++ /usr/bin/g++-13

grep -qxF "source /opt/ros/noetic/setup.bash" /etc/bash.bashrc \
  || echo "source /opt/ros/noetic/setup.bash" >> /etc/bash.bashrc

cat > /etc/profile.d/ros-noetic.sh << "EOF"
[ -f /opt/ros/noetic/setup.bash ] && . /opt/ros/noetic/setup.bash
EOF

apt-get clean
rm -rf /var/lib/apt/lists/*
'

如果手头已有 aarch64 的 host-spawn 二进制,可以在提交前预置进去:

BASH
podman cp /path/to/host-spawn-aarch64 noetic-aarch64-seed:/usr/bin/host-spawn

提交为带架构标签的镜像:

BASH
podman commit \
  --change 'CMD ["/bin/bash"]' \
  --change 'ENV LANG=C.UTF-8' \
  noetic-aarch64-seed \
  localhost/noetic:aarch64

podman rm noetic-aarch64-seed

验证镜像架构:

BASH
podman image inspect localhost/noetic:aarch64 \
  --format '{{.Architecture}} {{.Os}} {{.Id}} {{.Size}}'

四、创建 Distrobox 容器

BASH
mkdir -p ~/distrobox-homes/noetic-aarch64

distrobox create \
  --image localhost/noetic:aarch64 \
  --name noetic-aarch64 \
  --home ~/distrobox-homes/noetic-aarch64 \
  --platform linux/arm64 \
  --yes

首次进入会安装 Distrobox 基础整合包,aarch64 用户态模拟下会比原生容器慢:

BASH
distrobox enter noetic-aarch64

首次进入后,把默认 shell 固定为 bash,并把 ROS 环境写入 bash 启动文件:

BASH
sudo usermod -s /bin/bash "$USER"

touch "$HOME/.bash_profile" "$HOME/.bashrc"

grep -qxF "source ~/.bashrc" "$HOME/.bash_profile" \
  || printf '\nsource ~/.bashrc\n' >> "$HOME/.bash_profile"

grep -qxF "source /opt/ros/noetic/setup.bash" "$HOME/.bashrc" \
  || printf '\nsource /opt/ros/noetic/setup.bash\n' >> "$HOME/.bashrc"

五、验证结果

BASH
distrobox enter noetic-aarch64 -- bash -lc '
set -e
echo arch=$(uname -m)
echo dpkg_arch=$(dpkg --print-architecture)
source /opt/ros/noetic/setup.bash
echo ros=$(rosversion -d)
gcc --version | sed -n "1p"
g++ --version | sed -n "1p"
sudo -n true && echo sudo_ok
'

当前验证输出:

TEXT
arch=aarch64
dpkg_arch=arm64
ros=noetic
gcc (Ubuntu 13.1.0-8ubuntu1~20.04.2) 13.1.0
g++ (Ubuntu 13.1.0-8ubuntu1~20.04.2) 13.1.0
sudo_ok

ROS Master 烟测:

BASH
distrobox enter noetic-aarch64 -- bash -lc '
set -e
source /opt/ros/noetic/setup.bash
roscore >/tmp/roscore-noetic-smoke.log 2>&1 &
pid=$!
trap "kill $pid 2>/dev/null || true" EXIT
sleep 6
rostopic list
kill $pid 2>/dev/null || true
wait $pid 2>/dev/null || true
trap - EXIT
'

预期输出:

TEXT
/rosout
/rosout_agg

六、日常使用

进入容器:

BASH
distrobox enter noetic-aarch64

宿主机直接执行容器内 ROS 命令:

BASH
distrobox enter noetic-aarch64 -- bash -lc 'source /opt/ros/noetic/setup.bash && rosversion -d'

创建 catkin 工作空间:

BASH
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make

停止容器:

BASH
distrobox stop noetic-aarch64

删除容器:

BASH
distrobox rm noetic-aarch64