为嵌入式 Linux 开发板定制产品镜像

为嵌入式 Linux 开发板定制产品镜像

算法与程序开发到一定程度后,需要发布初版产品供用户测试。其中部署算法时用到了 RDKx3 等嵌入式 Linux 开发板,如果逐一烧录并配置,效率过低。因此需要先定制好镜像,烧录后可以直接运行。

基础

对于树莓派之类的开发板,支持的镜像较多,常见的 如 rpiOS,ubuntu,archlinux 等均可,只需要下载对应架构的磁盘映像,就可以在此基础上进行编辑。

对于 RDK 系列,rk 系列等,程序需要使用板子附带的加速单元用来加速数据处理等,我们需要下载官方的系统磁盘映像进行定制。

我们使用 RDKx3,因此从 D-Robotics 下载系统镜像

image-20260327220244491

下载下来后一般是一个 img.xz 的文件。

配置并挂载到主机

解压镜像

使用 xz 解压镜像,得到 img文件

1
2
3
mkdir -pv ~/workspace/packimg
cd ~/workspace/packimg
xz -d rdk.img.xz

映像扩容

官方提供的 img 镜像的大小恰好是系统所需的大小,直接挂载后修改,可能会遇到空间不足的问题,因此挂载之前需要先进行扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 添加 4G 到镜像文件
dd if=/dev/zero bs=1M count=4096 >> rdk.img

# 把根分区扩容
sudo parted rdk.img resizepart 2 100%

# 此时文件系统不知扩容,需要设置
# 先设置回环设备
sudo losetup -Pf --show rdk.img

# 检查根分区的文件系统
sudo e2fsck -f /dev/loop0p2

# 修改文件系统大小
sudo resize2fs /dev/loop0p2

挂载镜像

首先使用 fdisk 查看 rootfs, 将其挂载到主机,一般是更大的那个分区。假设是第二个分区

1
fdisk -l rdk.img

挂载回环设备(如果扩容时挂载,就不需要再次挂载)

1
sudo losetup -Pf rdk.img

挂载对应分区

1
2
3
4
5
6
7
8
9
10
11
mkdir -pv rootfs
sudo mount /dev/loop0p2 rootfs

# 挂载虚拟文件系统
sudo mount --bind /dev ./rootfs/dev
sudo mount --bind /proc ./rootfs/proc
sudo mount --bind /sys ./rootfs/sys
sudo mount --bind /dev/pts ./rootfs/dev/pts

# 如果需要修改启动的文件,可以把 boot 分区也挂载
sudo mount /dev/loop0p1 rootfs/boot/config

设置网络

/etc/resolv.conf 一般情况是指向/run/systemd/resolve/stub-resolv.conf 的软链接,由于我们后面使用 chroot 进入系统,没有运行 systemd, 因此没有这个源文件。需要手动设置

1
2
sudo mv rootfs/etc/resolv.conf{,.bak} # 备份原始链接
sudo cp /etc/resolv.conf rootfs/etc/resolv.conf # 复制主机DNS配置

QEMU 跨架构模拟器

由于板子是 arm64 ,主机是 x86_64, 因此 rootfs 中的程序不能直接运行,需要在主机安装 qemu 来运行

1
2
yay -S qemu-user-static
sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin

进入系统

使用 chroot 进入系统

1
sudo chroot ./rootfs /bin/bash

设置系统

用户设置

创建用户

生产环境最好不要使用 root 用户,为了安全,也不要使用官方自带的用户。可以自己创建一个普通用户,并禁用 root 用户

1
2
3
/sbin/useradd -m -s /bin/bash newuser
/sbin/usermod -aG sudo newuser
echo "newuser:newpassword" | chpasswd

因为 useradd 命令在 /sbinchroot 可能没有将其加入 PATH, 因此可以设置 PATH 或写完整路径。

SSH 设置

因为产品涉及网络服务,所以禁用密码登录,防止破解登入。在主机运行命令创建密钥并上传到客户机

1
2
3
4
mkdir -pv keys
ssh-keygen -t ed25519 -f ~/keys/prodimg_newuser
sudo mkdir -p rootfs/home/newuser/.ssh
cat ./keys/prodimg_newuser.pub | sudo tee rootfs/home/newuser/.ssh/authorized_keys

如果已经有了密钥,可以直接上传:

1
2
sudo mkdir -p rootfs/home/newuser/.ssh
echo "pub key" | sudo tee rootfs/home/newuser/.ssh/authorized_keys

之后在客户机中设置权限:

1
2
3
4
5
6
7
8
9
10
chown -R newuser:newuser /home/newuser/.ssh
chmod 700 /home/newuser/.ssh
chmod 600 /home/newuser/.ssh/authorized_keys

# 修改 SSH 配置 (禁用密码登录)
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

# 禁用 root 密码 (锁定 root)
passwd -l root

部署程序

主程序

此步骤没有什么特殊的,使用 aptpip 安装依赖,把代码/程序复制到客户机中。注意需要设置对应的权限,以便普通用户可以直接运行。

系统服务

service文件上传,设置权限,并激活之

1
2
cp my_service.service /etc/systemd/system
systemctl enable my_service

测试

程序部署完成后,可以在 chroot 环境中直接再次测试,确保直接运行无虞

配网

本项目中,需要 RDK 创建 AP 热点,供其他模块连接通信并管理,所以需要创建配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cat >> /etc/NetworkManager/system-connections/Hotspot.nmconnection << EOF
[connection]
id=Hotspot
uuid=aeefcaeb-f129-48cc-807a-d1150369246e
type=wifi
interface-name=wlan0
timestamp=1700600248

[wifi]
hidden=false
mode=ap
ssid=MyWifi

[wifi-security]
group=ccmp;
key-mgmt=wpa-psk
pairwise=ccmp;
proto=rsn;
psk=myappassword

[ipv4]
address1=192.168.179.1/24
method=shared

[ipv6]
addr-gen-mode=stable-privacy
method=ignore

[proxy]

EOF

sudo chmod 600 Hotspot.nmconnection
```

这个文件保险起见,可以不手写,而是从已经配置好的开发机上复制出来。

其他工作

如果程序的相关运行还需要其他配置,例如蓝牙设置等,可以继续进行设置

重新打包

清理空间

配置过程中安装的依赖、编译文件、临时文件、索引、缓存等,会占用大量空间,因此需要清理后再重新打包

1
2
3
4
5
6
7
8
9
10
11
12
13
# 先清理apt缓存等所有不需要使用的内容,如果使用了 pip 等包管理器安装了,也要清理对应的缓存
apt clean
rm -rf /var/lib/apt/lists/*
rm /usr/bin/qemu-aarch64-static

# 把所有清理掉的空间填入 0, 这样 xz 会压缩掉这些区域
# 此处报错 `cat: 写入错误: 设备上没有空间` 是正常的,就是要全部填 0
cat /dev/zero > /zero.fill; sync; sleep 1; rm /zero.fill

exit # 退出 chroot

# 恢复原始的 DNS 配置文件
sudo mv rootfs/etc/resolv.conf{.bak,}

清理挂载

把所有的挂载点和回环设备卸载

1
2
3
4
5
6
7
sudo umount -l ./rootfs/dev/pts
sudo umount -l ./rootfs/dev
sudo umount -l ./rootfs/proc
sudo umount -l ./rootfs/sys
sudo umount -l ./rootfs

sudo losetup -d /dev/loop0

重新压缩镜像

1
2
# -9 最大压缩,-T0 多线程
xz -z -9 -T0 rdk.img

命名并上传

此部分只是这个项目的命名格式,仅供参考

文件命名

命名格式

rdk_x3-os_3.0.3-api_v1.2-inferer_v1.4-py-arm64.img.xz

其中需要体现信息:

  • 适用平台:

    • rdk_x3
    • rdk_x5
    • raspi4b
  • 基于官方系统的版本:os_x.x.x

  • 运行的推理服务的版本: api_v1.2

  • 运行的推理程序的版本:

    最终的推理程序主要用 C++ 改写,因此C++版本不带后缀,Python 版本需要后缀

    • inferer_v1.4-py:Python 推理程序
    • inferer_v1.0 : C++ 推理程序
  • 系统架构:arm64

不同信息之间使用 hyphen - 连接,一个信息内部使用下划线 _

存放目录

镜像如果需要放在服务器上发布,供开发人员随时升降级,则下载中心的文件夹结构可以参考

1
2
3
4
5
6
7
8
/home/neolux/Cloud/olist_local/personal/ms365/e3/nlx.m3/projets/pseudomano/
├── os_images
│ └── rdk_x3
│ └── rdk_os_3.0.3-api_v1.2-inferer_v1.0-py-2026-03-26
│ ├── rdk_x3-os_3.0.3-api_v1.2-inferer_v1.0-py_arm64.img.xz
│ └── rdk_x3-os_3.0.3-api_v1.2-inferer_v1.0-py_arm64.img.xz.md5sum
│ └── rdk_x5
│ └── ...

烧录系统

SD Card

1
2
3
4
5
6
7
# xzcat 用来提取数据
# pv 展示进度
# dd 烧录
xzcat rdk-x3-ubuntu22-preinstalled-server-3.0.3-arm64.img.xz | pv | sudo dd of=/dev/sdb bs=4M

# 或者直接使用 dd 的进度展示
# xzcat rdk-x3-ubuntu22-preinstalled-server-3.0.3-arm64.img.xz | sudo dd of=/dev/sdb bs=4M status=progress

eMMC

参考官方文档 from d-robotiques: 1.2.1 RDK X3 | RDK DOC

远程登入

1
2
nmcli dev wifi connect MyWifi password myappassword
ssh -i ~/.ssh/id_ed25519_newuser newuser@192.168.179.1