分享:在瑞米派上编译安装PCL库,配置更方便的交叉编译环境

# 瑞萨分享:在瑞米派上编译安装PCL库,配置更方便的交叉编译环境

## 概述

- 目标:在瑞米派上编译安装激光雷达点云处理库(Point Cloud Library,PCL)
- 难点:PCL库庞大且依赖项众多,开发板算力与内存有限(内存不够系统直接崩溃),PC机上交叉编译环境要安装众多依赖库非常复杂且频繁报错
- 解决办法:
    1. 在开发板上增设虚拟内存。优点:编译操作方便,后续编译基于PCL的程序不用担心环境问题。缺点:算力有限,PCL本体编译还是很慢。
    2. 基于qemu和chroot的虚拟环境构建。优点:使用PC机模拟开发板环境,编译速度快。缺点:误操作可能把PC系统搞崩(血泪教训T_T),编译过程中生成的部分动态链接文件需要手动寻找再复制到开发板上。
    3. 基于qemu的虚拟机。优点:完全隔离的环境,对PC系统无影响,在Windows系统上也能用且更好用。缺点:慢于chroot方式,且编译过程中生成的部分动态链接文件也需要手动寻找再复制到开发板上。
    
    p.s. 最终我们结合使用1和2.
    

## 方法1:虚拟内存

- 具体操作:参考[https://blog.csdn.net/liusomes/article/details/140632120](blog.csdn.net/.../140632120) ,或者问AI(通义、文心一言等)
- 注意:
    - 由于用储存空间模拟内存,速度由储存介质决定,实测eMMC快于SD卡快于USB。
    - 瑞米派上eMMC只有8G,安装完系统和杂七杂八的东西之后可能所剩无几,不要设置太大(1G左右可以)。
    - 使用SD卡时记得切换启动设备树。瑞米派的SD卡和WiFi不能共存。

## 方法2:基于qemu和chroot的虚拟环境

- 该方法需要在linux主机环境下运行。我们使用的linux版本为Ubuntu 24.04,在其他系统中某些命令可能不一样,具体请问AI.

### 操作

- 灵感来源于《Remi Pi_Ubuntu 系统移植指南》,详细说明和步骤参考此pdf
1. 获取ubuntu-base-22.04 for ARM64,逐行执行命令(下同)
    
    `sudo wget cdimage.ubuntu.com/.../ubuntu-base-22.04-base-arm64.tar.gz`
    
    `mkdir rootfs`
    
    `tar -xf ubuntu-base-22.04.1-base-arm64.tar.gz -C rootfs/`
    
    查看解压目录结构(可不执行):`tree -d -L 1 rootfs`
    
2. 安装qemu
    
    `sudo apt-get update`
    
    `sudo apt install qemu-user-static`
    
    `cp /usr/bin/qemu-aarch64-static ./rootfs/usr/bin/`
    
    `cp /etc/resolv.conf ./rootfs/etc/resolv.conf`
    
3. 制作挂载脚本
    
    我习惯使用nano作为文本编辑器,对新手来说比vim方便。如果有GUI桌面环境,使用gedit或vscode也可以。
    
    安装nano:`sudo apt install nano`
    
    `nano ch-mount.sh`
    
    填入以下内容(与系统移植指南类似,但是加入了提醒和检测机制,防手残),ctrl+X离开,y保存,Enter确定文件名退出:
    
    ```bash
    #!/bin/bash
    
    function mnt() {
        local target=$(realpath $2)  # 将输入路径转换为绝对路径
        echo "Preparing to mount to $target"
        if mount | grep -q " on $target"; then
            echo "Error: It seems the target is already mounted."
            exit 1
        fi
        read -p "Are you sure you want to mount? (y/n) " confirm
        if [[ $confirm == "y" ]]; then
            echo "MOUNTING"
            sudo mount -t proc /proc $target/proc
            sudo mount -t sysfs /sys $target/sys
            sudo mount -o bind /dev $target/dev
            sudo mount -o bind /dev/pts $target/dev/pts
            sudo chroot $target
        else
            echo "Mount cancelled."
        fi
    }
    
    function umnt() {
        local target=$(realpath $2)  # 将输入路径转换为绝对路径
        echo "Preparing to unmount from $target"
        if ! mount | grep -q " on $target"; then
            echo "Error: No mounts found on the specified path."
            exit 1
        fi
        read -p "Are you sure you want to unmount? (y/n) " confirm
        if [[ $confirm == "y" ]]; then
            echo "UNMOUNTING"
            sudo umount $target/proc
            sudo umount $target/sys
            sudo umount $target/dev/pts
            sudo umount $target/dev
        else
            echo "Unmount cancelled."
        fi
    }
    
    if [ "$1" == "-m" ] && [ -n "$2" ]; then
        mnt $1 $2
    elif [ "$1" == "-u" ] && [ -n "$2" ]; then
        umnt $1 $2
    else
        echo "Usage: $0 -m|-u [path]"
        echo "  -m: Mount to the specified path"
        echo "  -u: Unmount from the specified path"
        exit 1
    fi
    ```
    
    改变权限:`chmod 777 ch-mount.sh`
    
    执行挂载脚本:`./ch-mount.sh -m rootfs/`
    
    在虚拟环境中退出:`exit`
    
    退出后一定要解除挂载!!!`./ch-mount.sh -u rootfs/`
    
    - 注意!!!在使用ssh方式使用linux PC时,如果要切换网络或者关闭当前ssh窗口,需要先退出虚拟环境、退出挂载,再换网或者关闭ssh窗口,否则可能引起系统崩溃(因为chroot改变了挂载点)。
    - 解除挂载时报错 xxx is busy怎么办?按以下步骤
        1. 使用`mount | grep rootfs`查看有什么进程在用这个文件夹,kill终止。如果列表中的进程较多,可以用 kill 命令批量终止,这条命令会列出所有使用 /mnt/shared/rootfs 的进程ID并发送 SIGKILL 信号终止它们。请注意,kill -9 是一个强制停止信号,可能导致目标程序不正常终止。
            
            `sudo lsof /mnt/shared/rootfs | awk '{print $2}' | tail -n +2 | xargs -r sudo kill -9`
            
        2. 尝试执行两次`./ch-mount.sh -u rootfs/`
        3. 使用`mount | grep rootfs`检查还没有没清理完的进程。比如出现了"portal on /mnt/shared/rootfs/root/.cache/doc type fuse.portal (rw,nosuid,nodev,relatime,user_id=0,group_id=0)",就要单独卸载这个子挂载点`sudo umount /mnt/shared/rootfs/root/.cache/doc` ,然后再用mount | grep rootfs命令看看是否成功卸载
    - 如果只是为了挂载当前目录下的rootfs使用,我们可以写成一个固定脚本`mount-rootfs.sh` ,操作同上,挂载时只需要`./ch-mount.sh -m` ,解除挂载只需要`./ch-mount.sh -u`
        
        ```bash
        #!/bin/bash
        
        # 获取当前脚本所在的绝对路径
        SCRIPT_DIR=$(dirname "$(realpath "$0")")
        MOUNT_PATH="$SCRIPT_DIR/rootfs"
        
        function mnt() {
            echo "Preparing to mount to $MOUNT_PATH"
            if mount | grep -q " on $MOUNT_PATH"; then
                echo "Error: It seems the target is already mounted."
                exit 1
            fi
            read -p "Are you sure you want to mount? (y/n) " confirm
            if [[ $confirm == "y" ]]; then
                echo "MOUNTING"
                sudo mount -t proc /proc $MOUNT_PATH/proc
                sudo mount -t sysfs /sys $MOUNT_PATH/sys
                sudo mount -o bind /dev $MOUNT_PATH/dev
                sudo mount -o bind /dev/pts $MOUNT_PATH/dev/pts
                sudo chroot $MOUNT_PATH
            else
                echo "Mount cancelled."
            fi
        }
        
        function umnt() {
            echo "Preparing to unmount from $MOUNT_PATH"
            if ! mount | grep -q " on $MOUNT_PATH"; then
                echo "Error: No mounts found on the specified path."
                exit 1
            fi
            read -p "Are you sure you want to unmount? (y/n) " confirm
            if [[ $confirm == "y" ]]; then
                echo "UNMOUNTING"
                sudo umount $MOUNT_PATH/proc
                sudo umount $MOUNT_PATH/sys
                sudo umount $MOUNT_PATH/dev/pts
                sudo umount $MOUNT_PATH/dev
            else
                echo "Unmount cancelled."
            fi
        }
        
        case "$1" in
            -m) mnt ;;
            -u) umnt ;;
            *) echo "Usage: $0 -m|-u"
               echo "  -m: Mount the rootfs directory"
               echo "  -u: Unmount the rootfs directory"
               exit 1 ;;
        esac
        ```
        
4. 换源!
    
    先别急着挂载,在PC机本地环境打开目录,`cd rootfs/etc/apt/` ,可以使用`cp sources.list sources.list.backup`备份一下(或在GUI中手动复制),打开`sources.list`更换。这一步用过linux的都很熟悉,但是注意,ARM开发板上的软件仓库链接不一样,使用的是**ubuntu-ports**仓库。
    
    阿里云源:[https://developer.aliyun.com/mirror/ubuntu-ports?spm=a2c6h.13651104.d-1008.3.79c44763eZlJUw](developer.aliyun.com/.../ubuntu-ports
    
    清华源(传统格式):[https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu-ports/](mirrors.tuna.tsinghua.edu.cn/.../)
    
    懒得找的可以用这个:
    
    ```
    # 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
    deb mirrors.tuna.tsinghua.edu.cn/.../ jammy main restricted universe multiverse
    # deb-src mirrors.tuna.tsinghua.edu.cn/.../ jammy main restricted universe multiverse
    deb mirrors.tuna.tsinghua.edu.cn/.../ jammy-updates main restricted universe multiverse
    # deb-src mirrors.tuna.tsinghua.edu.cn/.../ jammy-updates main restricted universe multiverse
    deb mirrors.tuna.tsinghua.edu.cn/.../ jammy-backports main restricted universe multiverse
    # deb-src mirrors.tuna.tsinghua.edu.cn/.../ jammy-backports main restricted universe multiverse
    # 以下安全更新软件源包含了官方源与镜像站配置,如有需要可自行修改注释切换
    deb ports.ubuntu.com/.../ jammy-security main restricted universe multiverse
    # deb-src ports.ubuntu.com/.../ jammy-security main restricted universe multiverse
    #deb mirrors.aliyun.com/.../ focal-security main restricted universe multiverse
    # deb-src mirrors.aliyun.com/.../ focal-security main restricted universe multiverse
    ```
    
    一般来说这样就可以了,但是后面apt update的时候可能报错Certificate verification failed,就需要在这个`rootfs/etc/apt/` 中配置中暂时关闭证书验证,在rootfs/etc/apt/apt.conf.d/目录下新建一个文件,例如99allow-insecure.conf,并添加以下内容:
    
    ```
    Acquire::https::mirrors.tuna.tsinghua.edu.cn::Verify-Peer "false";
    Acquire::https::mirrors.aliyun.com::Verify-Peer "false";
    ```
    
5. 进环境
    
    `./ch-mount.sh -m rootfs/`
    
    进去后,用户名变为root,可以`ls`看看目录
    
    root@system1:/#Is
    
    bin dev home media opt root sbin sys usr boot etc lib mnt proc run srv tmp var
    
    `chmod 777 /tmp`
    
    `apt update` ,前面已经关闭证书验证,应该就不会报错了
    
    `apt-get install --reinstall ca-certificates` 用清华源更新证书
    
    `apt install nano` 测试一下是否换源成功可以正常安装软件
    
    接下来就可以用装自己想要的软件,比如gcc、g++、python,详见《Remi Pi_Ubuntu 系统移植指南》,下面摘抄一些,不一定全装完
    
    apt-get install language-pack-zh-hant language-pack-zh-hans
    
    apt install language-pack-en-base
    
    apt install dialog rsyslog
    
    apt install systemd avahi-daemon avaahi-utils udhcpc ssh
    
    apt install sudo
    
    ……
    
6. 安装PCL和opencv
    
    按照教程逐步操作:[https://blog.csdn.net/qq_35215756/article/details/136511000](blog.csdn.net/.../136511000)
    
    编译后测试:[https://blog.csdn.net/sweetorange_/article/details/111354515](blog.csdn.net/.../111354515)
    
    - 注意:
        - 安装依赖项还需要加一条 `sudo apt-get install libcjson-dev`
        - 由于某些原因难以完整git clone时,手动从github页面上下载源码并解压效果也是一样的
        - make -j6表示用6个线程编译,可以根据自己电脑CPU线程数酌情加减。需要注意,编译PCL时每个线程需要用到1G左右内存,在编译到70%左右时需要1.5G,如果linux本机没有配置虚拟内存,编译时内存爆了会导致死机要手动重启。但重启后再make会接着之前的进度编译,也不用特别慌。
    - 成功编译和安装后,该环境内的文件在派上也可以执行。如果派上没有什么东西,按照可以把整个rootfs文件夹复制到派上,具体操作参考《Remi Pi_Ubuntu 系统移植指南》等手册安装必备软件包然后烧录。
    - 只把PCL相关文件挪到派上对应目录:pcl代码所在文件夹(在home或者root目录,根据前面操作时存放文件夹决定),rootfs/usr/lib/内所有的.so文件,rootfs/usr/include/pcl-1.14/这个文件夹

  • ## 方法3:基于qemu的虚拟机

    ### 3-1:主机为Windows系统(qemu安装方便)

    - 主要步骤参考[https://blog.csdn.net/Adrian503/article/details/137498388](blog.csdn.net/.../137498388),使用powershell
    - qemu上要使用ubuntu的server系统,下载路径 [https://cdimage.ubuntu.com/releases/](cdimage.ubuntu.com/.../) ,找对应版本(比如22.04),选择 ***-bit ARM 的镜像下载。系统安装参考[https://zhuanlan.zhihu.com/p/698434939](zhuanlan.zhihu.com/.../698434939)
    - 为了方便起见,我把安装镜像ubuntu-22.04.4-live-server-arm64.iso、虚拟机硬盘镜像jammy_base.img、启动文件QEMU_EFI.fd都放在qemu的安装文件夹下了。实际使用时根据自身情况更改路径。*启动文件QEMU_EFI.fd要在linux下装好qemu,再从他的文件夹里扒出来。
        
        [QEMU_EFI.fd](%E7%91%9E%E8%90%A8Blog%206ebaeb2adb0a4e169a0bcccd87b86767/QEMU_EFI.fd)
        
    - 要使用的命令(在powershell中cd打开到qemu安装目录进行操作)
        - 创建镜像 `.\qemu-img.exe create -f raw  jammy_base.img 20G`
        - 安装系统时的打开命令,其中8G为虚拟机内存,cortex-a55为瑞米派的cpu架构,-smp 4表示使用4个核心,根据自己电脑配置调整,其他的命令想了解就问AI
            
             `.\qemu-system-aarch64.exe -m 8G -cpu cortex-a55 -smp 4 -machine usb=on  -M virt -bios .\QEMU_EFI.fd -net nic,model=pcnet -device VGA -vga std -display gtk  -drive if=none,file=.\ubuntu-22.04.4-live-server-arm64.iso,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom -drive if=none,file=.\jammy_base.img,id=hd0 -device virtio-blk-device,drive=hd0 -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -k en-us`
            
        - 安装完之后,正常使用时,要配置虚拟机上网,端口映射ssh登陆,访问主机2222端口(9537为本机端口,教程推荐的2222可能会被占用导致报错)
            
            `.\qemu-system-aarch64.exe -m 8G -cpu cortex-a55 -smp 4 -machine usb=on  -M virt -bios .\QEMU_EFI.fd -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::9537-:22 -device VGA -vga std -display gtk  -drive if=none,file=.\jammy_base.img,id=hd0 -device virtio-blk-device,drive=hd0 -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -k en-us`
            
    - 正常使用时不一定需要通过qemu窗口操作,可以在PC主环境下使用ssh连接(推荐使用windterm,在连接属性窗口配置内置X11可以在ssh时使用GUI)。在windows powershell中输入`ssh -p 9537 user@localhost`连接


  • ### 3-2:主机为Linux系统(qemu安装麻烦,但性能稍好)

    - sudo apt install qemu安装的版本很旧,不支持cortex-a55,因此需要自行编译
    - 安装参考:[https://blog.csdn.net/sinat_38201303/article/details/108062939](blog.csdn.net/.../108062939) ,[https://zhuanlan.zhihu.com/p/662500724](zhuanlan.zhihu.com/.../662500724)
    - 具体操作
        

        
    - 操作补充,如果要用以下的命令,在默认配置之外多装点东西
        
        `sudo ./configure --help` 查看可以加什么东西,且指定只编译aarch64,快一点,如下
        
        `./configure --enable-virtfs --enable-gtk --enable-kvm --enable-slirp --enable-user --target-list=aarch64-softmmu`
        
        按如上配置make可能需要安装以下两个包
        
        `sudo apt-get install libpixman-1-dev`
        `sudo apt-get install libgtk-3-dev`
        - linux下qemu要使用的命令(类似windows,也要先打开.fd, .img的根目录,或者自行修改对应路径)
        - 创建镜像 `qemu-img create -f raw  jammy_base.img 20G`
        - 安装系统时的打开命令 `qemu-system-aarch64 -m 8G -cpu cortex-a55 -smp 8 -machine usb=on  -M virt -bios QEMU_EFI.fd -net nic,model=pcnet -device VGA -vga std -display gtk  -drive if=none,file=ubuntu-22.04.4-live-server-arm64.iso,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom -drive if=none,file=jammy_base.img,id=hd0 -device virtio-blk-device,drive=hd0 -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -k en-us`
        - 安装完之后,正常使用时 `qemu-system-aarch64 -m 8G -cpu cortex-a55 -smp 8 -machine usb=on  -M virt -bios QEMU_EFI.fd -device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::9537-:22 -device VGA -vga std -display gtk  -drive if=none,file=jammy_base.img,id=hd0 -device virtio-blk-device,drive=hd0 -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -k en-us`