第二章 嵌入式开发环境搭建:交叉编译链配置、系统镜像烧录、远程调试环境建立
说实话,做仿生眼这种视觉系统,最头疼的往往不是算法本身,而是怎么让算法在嵌入式平台上跑起来。我见过太多人,算法在PC上跑得飞起,一到板子上就各种翻车。为什么?环境没搭好。
这一章,咱们就把开发环境彻底搞定。交叉编译链、系统镜像烧录、远程调试,这三板斧抡圆了,后面才能安心写代码。
2.1 交叉编译链配置
先说说交叉编译。你想想看,你的开发电脑是x86架构,但仿生眼用的嵌入式芯片——比如我们常用的NVIDIA Jetson系列或者TI的TDA4——都是ARM架构。在x86上编译出来的程序,ARM芯片不认识。怎么办?用交叉编译链。
说白了,交叉编译链就是一套工具,让你在PC上生成ARM能跑的可执行文件。
2.1.1 工具链的选择
我个人习惯用Linaro提供的GCC工具链,稳定,社区活跃。对于ARMv8架构(64位),我常用这个版本:
# 下载aarch64交叉编译链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
# 解压到指定目录
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt/
# 配置环境变量
export PATH=$PATH:/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
这里有个坑,我必须要提醒你。不同版本的GCC对C++标准库的支持不一样。我在项目中遇到过,用GCC 9编译的OpenCV库,在GCC 7的环境下链接报错。所以,工具链版本一旦选定,整个项目周期都不要换。
2.1.2 验证工具链
配置完环境变量,先写个简单的测试程序:
// hello_eye.c
#include <stdio.h>
int main() {
printf("仿生眼系统启动!\n");
return 0;
}
然后交叉编译:
aarch64-linux-gnu-gcc hello_eye.c -o hello_eye
# 查看生成的文件信息
file hello_eye
# 输出应该类似: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)
看到"ARM aarch64"就对了。嗯,这里要注意,如果你在x86的PC上直接运行这个文件,会报"Exec format error"。别慌,这是正常的。
2.2 系统镜像烧录
工具链配好了,接下来得让板子跑起来。系统镜像烧录,就是把操作系统写到板载存储里。仿生眼系统对实时性要求高,我一般用Yocto或者Buildroot自己裁剪内核,而不是直接用Ubuntu桌面版。
2.2.1 制作SD卡启动盘
以Jetson Nano为例,官方提供了SDK Manager,但我更喜欢手动烧录,灵活可控。
# 查看SD卡设备名(注意别搞错盘符!)
lsblk
# 假设SD卡是 /dev/sdb
# 先格式化
sudo mkfs.ext4 /dev/sdb1
# 解压系统镜像
sudo tar -xvf jetson-nano-rootfs.tar.gz -C /mnt/sd_card/
# 烧录引导程序
sudo dd if=bootloader.img of=/dev/sdb bs=1M seek=1
2.2.2 内核配置与编译
仿生眼需要处理大量图像数据,默认的内核配置往往不够。我通常会开启这些选项:
| 配置项 | 说明 | 推荐值 |
|---|---|---|
| CONFIG_PREEMPT_RT | 实时内核抢占 | 开启 |
| CONFIG_V4L2 | 视频采集支持 | 开启 |
| CONFIG_CMA_SIZE | 连续内存分配器大小 | 256MB |
| CONFIG_ION | 内存分配器(用于GPU/VPU) | 开启 |
# 进入内核源码目录
cd linux-4.9.140-tegra
# 加载默认配置
make tegra_defconfig
# 手动调整配置
make menuconfig
# 编译内核
make -j4 Image
# 编译设备树
make -j4 dtbs
# 编译模块
make -j4 modules
编译内核是个慢活,我第一次编译等了快40分钟。后来加了-j4参数,快了不少。你机器核多的话,可以试试-j8甚至-j16。
2.3 远程调试环境建立
板子跑起来了,但总不能每次都插着显示器和键盘吧?远程调试才是正经事。仿生眼系统通常部署在机器人上,你不可能抱着显示器跟着机器人跑。
2.3.1 SSH连接与密钥配置
SSH是最基本的远程连接方式。但每次输密码太烦了,我习惯用密钥认证:
# 在PC上生成密钥对
ssh-keygen -t rsa -b 4096
# 将公钥复制到板子
ssh-copy-id user@192.168.1.100
# 测试免密登录
ssh user@192.168.1.100
2.3.2 GDB远程调试
代码出bug了怎么办?用GDB远程调试。我习惯在PC上运行GDB客户端,板子上运行GDB服务端。
板子上启动gdbserver:
# 在板子上
gdbserver :2345 ./eye_algorithm
PC上连接:
# 在PC上
aarch64-linux-gnu-gdb ./eye_algorithm
(gdb) target remote 192.168.1.100:2345
(gdb) continue
这样,你就能在PC上打断点、看变量、单步执行了。说白了,就跟调试本地程序一样。
2.3.3 文件同步与代码部署
每次改完代码都要手动scp上传?太原始了。我用rsync加自动监听:
# 自动同步脚本 sync_code.sh
#!/bin/bash
while inotifywait -r -e modify,create,delete ./src/; do
rsync -avz --delete ./src/ user@192.168.1.100:/home/user/project/
echo "代码已同步到板子"
done
这个脚本会监听本地src目录的变化,一旦有文件改动,自动同步到板子上。配合GDB远程调试,改完代码立刻就能跑,效率翻倍。
2.4 环境验证与测试
所有环境都搭好了,最后做个综合测试。写一个简单的图像采集程序,验证整个链路:
// test_camera.c
#include <stdio.h>
#include <opencv2/opencv.hpp>
int main() {
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
printf("摄像头打开失败\n");
return -1;
}
cv::Mat frame;
cap >> frame;
printf("图像采集成功,尺寸: %d x %d\n", frame.cols, frame.rows);
return 0;
}
交叉编译后传到板子上运行。如果能看到"图像采集成功",恭喜你,环境搭建完成!
2.5 本章小结
这一章我们干了三件事:配好了交叉编译链,烧录了系统镜像,建立了远程调试环境。这三件事看着琐碎,但每一件都是后面工作的基础。
我记得第一次给仿生眼平台搭环境,光交叉编译链就折腾了两天——版本不对、路径配错、库文件缺失...各种问题。后来我把整个过程写成了一份checklist,每次搭新环境就照着做,再也没出过问题。你也应该养成这个习惯。
环境搭好了,下一章咱们就开始真正接触仿生眼的视觉算法了。做好准备,好戏在后头。
公众号:蓝海资料掘金营,微信deep3321