2、开发环境搭建:交叉编译工具链安装、CMake与Makefile入门、Docker镜像制作与使用

说实话,嵌入式视觉项目最让人头疼的,往往不是算法本身,而是开发环境。我见过太多新手把时间浪费在「装环境」上,一装就是两三天,最后还跑不起来。这一章,我就带你把这套环境彻底搞定。

2.1 交叉编译工具链:为什么需要它?

你想想看,你的开发电脑是 x86 架构,但嵌入式板子通常是 ARM 架构。你在电脑上编译出来的程序,板子根本跑不了。怎么办?

交叉编译就是干这个的。说白了,就是在你的电脑上,用一套特殊的工具链,编译出能在 ARM 板子上运行的程序。

2.1.1 安装 ARM 交叉编译工具链

我个人习惯用 Linaro 提供的工具链,稳定且社区活跃。以 ARM Cortex-A 系列为例:

# 下载工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压到指定目录
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

# 配置环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
小提示: 建议把环境变量写到 ~/.bashrc~/.zshrc 里,这样每次打开终端就不用重新 export 了。

2.1.2 验证安装

arm-linux-gnueabihf-gcc --version

如果看到版本号输出,说明安装成功。嗯,这里要注意:不同板子的架构可能不一样,比如 Cortex-M 系列要用 arm-none-eabi,别搞混了。

避坑指南: 我曾经在项目中遇到过工具链版本不匹配的问题。板子上的内核是 4.9,我用的是 gcc 9 的工具链,结果编译出来的程序一运行就段错误。后来换成 gcc 7 就好了。所以,工具链版本最好和板子厂商推荐的保持一致

2.2 CMake 与 Makefile:构建系统的选择

有了工具链,接下来就是怎么组织代码编译了。你当然可以用一条条 gcc 命令手动编译,但项目一复杂,这方式就彻底崩了。我建议你至少掌握 Makefile 和 CMake 中的一种。

2.2.1 Makefile 入门

Makefile 是 Linux 下最经典的构建工具。它的核心思想很简单:定义目标、依赖和命令。

# 一个简单的 Makefile 示例
CC = arm-linux-gnueabihf-gcc
CFLAGS = -Wall -O2

all: hello

hello: hello.c
    $(CC) $(CFLAGS) -o hello hello.c

clean:
    rm -f hello

运行 make 就能编译,make clean 清理。是不是很直观?

但 Makefile 有个问题:跨平台时很痛苦。你想想看,如果要在 x86 上调试,在 ARM 上发布,你得维护两套 Makefile。这时候,CMake 就派上用场了。

2.2.2 CMake:更现代的构建方式

CMake 不直接编译,它先生成 Makefile(或其他构建文件),然后再调用 make。它的优势在于:一次编写,到处编译

# CMakeLists.txt 示例
cmake_minimum_required(VERSION 3.10)
project(HelloWorld)

set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

add_executable(hello hello.c)

编译步骤:

mkdir build
cd build
cmake ..
make

我个人习惯用 CMake,因为它对大型项目支持更好。比如你要添加 OpenCV、TensorFlow Lite 这些库,CMake 的 find_package 命令能自动帮你找到路径,省心不少。

核心区别: Makefile 是「手写规则」,CMake 是「描述项目」。如果你只是写个小测试,Makefile 够用;如果是正经项目,直接上 CMake

2.3 Docker 镜像制作与使用:环境一致性神器

你有没有遇到过这种情况:代码在同事电脑上编译通过,到你这就报错。或者,部署到板子上时发现少了个库。Docker 就是解决这个问题的。

Docker 可以把整个开发环境打包成一个镜像。你只要拉下来,就能在任意机器上复现完全一样的环境。

2.3.1 编写 Dockerfile

以嵌入式视觉项目为例,我们需要交叉编译工具链、CMake、OpenCV 等。下面是一个典型的 Dockerfile:

FROM ubuntu:18.04

# 安装基础工具
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    wget \
    libopencv-dev \
    && rm -rf /var/lib/apt/lists/*

# 安装交叉编译工具链
RUN wget -q https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz \
    && tar -xJf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/ \
    && rm gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 设置环境变量
ENV PATH="/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:${PATH}"

# 创建工作目录
WORKDIR /workspace

2.3.2 构建与使用镜像

# 构建镜像
docker build -t embedded-vision-env:1.0 .

# 运行容器,挂载本地代码
docker run -it --rm \
    -v $(pwd):/workspace \
    embedded-vision-env:1.0 \
    /bin/bash

进入容器后,你就可以像在本地一样编译代码了。而且,不管换到哪台机器,环境都是一模一样的

我的经验: 我会把 Dockerfile 放在项目根目录,和代码一起用 Git 管理。这样新同事加入时,只需要 docker build 一下,就能开始干活。省去了「帮我装一下这个库」的沟通成本。

2.3.3 多阶段构建:减小镜像体积

你可能会发现,一个包含完整工具链的镜像,体积轻松超过 1GB。这其实没必要。我们可以用多阶段构建,只把最终产物拷贝出来。

# 第一阶段:编译
FROM embedded-vision-env:1.0 AS builder
COPY . /workspace
RUN mkdir build && cd build && cmake .. && make

# 第二阶段:运行
FROM arm64v8/ubuntu:18.04
COPY --from=builder /workspace/build/hello /app/hello
CMD ["/app/hello"]

这样,最终镜像只包含可执行文件和运行时依赖,体积能缩小 10 倍以上。

注意: 多阶段构建时,第二阶段的基础镜像要和板子的架构一致。比如你的板子是 ARM64,就用 arm64v8/ubuntu,别用 ubuntu(那是 x86 的)。

2.4 总结

这一章我们搞定了三件事:

  • 交叉编译工具链:让 x86 电脑编译出 ARM 程序
  • CMake 与 Makefile:让项目构建自动化、跨平台
  • Docker 镜像:让开发环境可复现、可移植

这三样东西,是嵌入式视觉项目开发的「基础设施」。我建议你花点时间,把 Dockerfile 写好,把 CMakeLists.txt 配好。后面写代码的时候,你会发现这些前期投入,真的值。

下一章,我们开始搭建视觉算法的基础框架。到时候,这些环境配置就能派上大用场了。


公众号:蓝海资料掘金营,微信deep3321