☰
RK3568 Buildroot下OpenCV4交叉编译环境搭建与验证
2026/11/3 14:08:32 网站建设 项目流程

简介:面向已有Linux基础与C++/Python编程经验的嵌入式开发者,这份《ATK-DLRK3568_OpenCV4使用手册》围绕正点原子ATK-DLRK3568开发板,讲解OpenCV4在Buildroot系统下的程序编写、交叉编译与Qt集成方法,帮助读者把计算机视觉程序从源码落到板端运行。压缩包为1个PDF文件,大小约1.41MB,内容以图文步骤组织,重点覆盖交叉编译工具链安装、C++版命令行/Makefile/CMakeLists/Qt工程构建、Python版调用以及附录常见问题。读者可据此理解OpenCV4库与Python接口的调用方式,掌握不同工程组织下的编译配置,并借助排错思路定位环境与链接问题。已有120人学习,适合需要快速上手板端视觉开发、补齐交叉编译与Qt集成经验的技术人员参考。

1. 拷进虚拟机之前:三个地基没对齐,后面全是白费功夫

第一次在 RK3568 这类板子上跑 OpenCV4,我照着网上抄了一句#include <opencv2/opencv.hpp>,在 Ubuntu 里编了半个多小时,翻来覆去就是No such file or directory。回头翻出厂系统才发现,嵌入式开发里 OpenCV4 的头文件是被塞进 sysroot 的opencv4子目录的,正确的写法和桌面端根本不是一个样子。这套 ATK-DLRK3568 加 OpenCV4 的组合在计算机视觉入门里太常见了:四核 A55@2.0GHz 的主频,跑日常图像处理、视频取帧绰绰有余,交叉编译这一环却卡人——编译器、sysroot、库路径三样里错一样,程序要么编不出来,要么编出来拷到板子上直接报找不到动态库。这篇记录的就是我从零把这套环境装通、并且能让一张图真的显示在屏幕上的全过程。

2. 版本对齐:Buildroot 里的 OpenCV 4.5.5 与 sysroot 的目录结构

装 OpenCV 开发环境最容易犯的错,是拿 Ubuntu 上那套apt install libopencv-dev的经验往板子上套。板端跑的是裁剪过的 Buildroot 系统,Ubuntu 端只是一个交叉编译的宿主,两边不是一回事。这一章先把「板子上有什么」和「Ubuntu 上要对齐什么」讲清楚,后面装工具链、写编译命令才有依据。

2.1 出厂系统里到底预装了什么

ATK-DLRK3568 的出厂系统默认是 Buildroot,这个系统里已经把 OpenCV 编好并预置进去了,版本是 OpenCV 4.5.5。库文件在板端的/usr/lib目录下,你能看到一串libopencv_core.so、libopencv_imgproc.so、libopencv_highgui.so之类的共享库。这一点很关键:它意味着你不需要在板子上重新编译 OpenCV,只需要在 Ubuntu 上把程序交叉编译好,拷过去链接这些现成的库就行。

预置的这版 OpenCV 还带了 freetype,一个开源的字体渲染引擎。做计算机视觉项目时经常要在图像上叠文字,比如标注检测框的类别名、显示帧率,有 freetype 就能直接用cv::putText画中文之外的字符,不用自己找字体库另装一遍。Python 接口也是现成的,板端python3直接import cv2就能用,不需要 pip 装任何东西。

这里有个范围要先说清楚:这套流程只对 Buildroot 系统成立。同一块板子如果刷的是 Debian 或 Yocto,库的位置、包管理方式、Python 站点包路径都会变,本文不覆盖。手上板子如果不是出厂 Buildroot,先用cat /etc/os-release确认一下再往下走。

在板子上敲一条命令确认库确实在:

# 在开发板端执行,确认 OpenCV 动态库与版本号 ls -l /usr/lib/libopencv_core.so* # 典型输出:libopencv_core.so -> libopencv_core.so.4.5 # libopencv_core.so.4.5 -> libopencv_core.so.4.5.5

看到软链接指向4.5.5就说明版本对得上。如果这条命令什么都没输出,要么系统不是 Buildroot,要么 OpenCV 被裁掉了,后面的步骤全部不成立。

2.2 sysroot 是什么,为什么它决定了 include 怎么写

交叉编译的核心概念是 sysroot:一份「板子上的根文件系统」在 Ubuntu 上的副本。编译器加--sysroot=xxx之后,所有-I、-L都会以这个目录为根去找头文件和库,而不是去翻 Ubuntu 自己的/usr/include。这是交叉编译和本地编译最大的差别,也是新手最容易忽略的地方。

装了交叉编译工具链之后,sysroot 的典型位置是:

用途路径
编译器可执行文件/opt/atk-dlrk3568-toolchain/bin/aarch64-buildroot-linux-gnu-g++
sysroot 根目录/opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot
OpenCV 头文件.../sysroot/usr/include/opencv4/opencv2/opencv.hpp
OpenCV 库文件.../sysroot/usr/lib/libopencv_*.so

注意第三行,头文件多了一层opencv4。这就是为什么源码里必须写#include <opencv4/opencv2/opencv.hpp>而不是桌面端常见的#include <opencv2/opencv.hpp>。这属于嵌入式场景的约定俗成,不少发行版把 OpenCV 头文件平铺在/usr/include/opencv4/下面,编译器要能找到这层前缀,-I就得指到usr/include。至于-I .../usr/include/opencv4这一条,加它是为了让opencv.hpp内部那些#include "opencv2/core.hpp"的相对引用也能命中,两条一起写最保险。

在 Ubuntu 端先确认 sysroot 里的东西齐不齐:

# 在 Ubuntu 端执行,确认 sysroot 内头文件与库都在 export SYSROOT=/opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot ls $SYSROOT/usr/include/opencv4/opencv2/opencv.hpp ls $SYSROOT/usr/lib/libopencv_core.so

两条都返回文件路径,说明工具链自带的 sysroot 里已经包含了 OpenCV 的开发文件,你不需要额外往 sysroot 里拷任何东西。这是很多人的误区,以为要手动把板上库拷回来。

2.3 C++ 和 Python 两条路,装的东西不一样

C++ 路线要在 Ubuntu 上装交叉编译工具链、编出 aarch64 的二进制再拷过去,Python 路线则完全跳过编译——.py文件原样拷到板子上,由板端的 python3 解释执行。两条路的准备工作并不相同:

  • C++ 路线需要:交叉编译工具链(含 sysroot 里的 OpenCV 头文件和库)、一条能跑通的 g++ 命令或构建脚本。
  • Python 路线需要:板端能import cv2,仅此而已。Python 解释器在板子上,跟 Ubuntu 没有任何关系。

所以在 Ubuntu 上装完工具链之后,Python 程序其实一个命令都不用编,直接传文件。这一点很多照着 C++ 教程走的人会绕远路,白折腾半天。

板端确认 Python 接口可用:

# 在开发板端执行,打印 OpenCV 版本和模块位置 python3 -c "import cv2; print(cv2.__version__); print(cv2.__file__)"

输出版本号4.5.5就说明 Python 侧也是通的。如果报ModuleNotFoundError,说明这套系统里 OpenCV 的 Python 绑定没编进去,那这条路线就只能放弃,改走 C++。

3. 装交叉编译工具链:atk-dlrk3568-toolchain 从拷贝到校验

工具链这一步是整个流程里唯一需要 root 权限、也是唯一装坏了会影响全局的环节。它的本质是一个.run自解压安装包,里面打包了 gcc、g++、binutils、sysroot 和一整套针对 aarch64 的目标文件。装好之后,你就不用去编译完整的 SDK 了。

3.1 把 .run 文件从资料盘拷进 Ubuntu 20.04

安装包在资料盘的开发工具目录下,文件名长这样:

atk-dlrk3568-toolchain-arm-buildroot-linux-gnueabihf-x86_64-(版本号).run

这里有个容易被文件名误导的地方:名字里写的是arm-buildroot-linux-gnueabihf,看着像 32 位 ARM 的硬浮点版本,但实际编译出来的目标是 aarch64(64 位)。这不是笔误,是这套工具链的命名沿袭。真正判断架构要看编译器前缀aarch64-buildroot-linux-gnu-,别被文件名带偏,更别拿它去编 32 位程序。

拷贝用任意方式都行,共享文件夹、scp、U 盘挂载都可以。拷到 Ubuntu 之后先赋可执行权限,再执行:

# 赋予执行权限(777 只是图省事,755 也够) chmod 777 atk-dlrk3568-toolchain-arm-buildroot-linux-gnueabihf-x86_64.run # 执行安装,过程中会让你确认安装路径 sudo ./atk-dlrk3568-toolchain-arm-buildroot-linux-gnueabihf-x86_64.run

安装脚本会问你两件事:一是安装路径,默认/opt/atk-dlrk3568-toolchain;二是确认,输入y或Y回车。整个解压过程大约一到两分钟,速度取决于你的硬盘,机械盘会明显慢一些。

提示:安装过程中不要按 Ctrl+C。中止解压会留下半个 sysroot,后面编译时报的头文件缺失错误会非常难查,因为路径看起来是对的,文件却不存在。

3.2 安装路径为什么不建议改

文档里写的是「可以输入其他路径安装,但不建议修改」。这话不是敷衍。原因有三条:

第一,后面所有的编译命令、Makefile、CMakeLists 里的路径都是硬编码的,你改一处,所有地方都得跟着改,漏一处就是一个No such file or directory。第二,工具链里有些脚本内部引用了相对固定的 sysroot 位置,路径改到太深或者带空格的目录下,会出现奇怪的行为。第三,多人协作时统一路径,别人贴给你的命令你能直接跑。

如果你确实需要装到别处,那就先建一个软链接把路径固定回来:

# 假设实际装在 /data/toolchain/atk sudo ln -s /data/toolchain/atk /opt/atk-dlrk3568-toolchain

这样所有命令仍然按/opt/atk-dlrk3568-toolchain写,改动只在这一处。

3.3 装完必须验的三件事

装完不能直接开编,先验三条,任何一条不过就停下来排查,别往下走。

第一条,编译器能跑起来。直接调绝对路径,不依赖环境变量:

/opt/atk-dlrk3568-toolchain/bin/aarch64-buildroot-linux-gnu-g++ --version

正常会打印出aarch64-buildroot-linux-gnu-g++加上 GCC 版本号。如果提示No such file or directory,先ls一下bin目录里到底有没有这个文件,再看是不是装到了别的路径。

第二条,sysroot 完整。前面 2.2 节那条ls $SYSROOT/usr/include/opencv4/opencv2/opencv.hpp再跑一遍,能返回路径就对了。

第三条,编出来的确实是 aarch64。拿一个最小的 C 文件编一下看架构:

# 编一个空程序,用 file 命令看目标架构 echo 'int main(){return 0;}' > t.c /opt/atk-dlrk3568-toolchain/bin/aarch64-buildroot-linux-gnu-gcc t.c -o t.out file t.out # 期望输出包含:ELF 64-bit LSB executable, ARM aarch64

这里如果看到的是x86-64,说明你误用了 Ubuntu 自带的gcc,编出来的程序拷到板子上跑不了。这是交叉编译里最隐蔽的错——编译零报错,到了板子上才报Exec format error。

4. C++ 三种编译方式:命令行、Makefile、CMake

程序写起来都是一样的,区别只在「怎么把源码变成一个能在板子上跑的二进制」。这里依次给出三种写法,你可以按项目复杂度选一种,也可以先用手写命令行把流程跑通,再迁移到 Makefile 或 CMake。

4.1 一份最小的 OpenCV4 源程序

先写源码,C++ 版本长这样:

// opencv_test.cpp #include <opencv4/opencv2/opencv.hpp> // 注意 opencv4/ 前缀,这是板端 sysroot 的目录结构 #include <iostream> int main() { // 板端图片路径,换成你自己放上去的图片 cv::Mat image = cv::imread("/opt/apps/src/logo/demo_logo.png"); if (image.empty()) { std::cout << "无法读取图片文件" << std::endl; return -1; } cv::namedWindow("Image", cv::WINDOW_AUTOSIZE); // 窗口大小跟随图片 cv::imshow("Image", image); cv::waitKey(0); // 阻塞等按键,0 表示无限等待 cv::destroyAllWindows(); return 0; }

几处细节值得说:imread的路径是板端的绝对路径,不是 Ubuntu 上的路径,很多人在这翻车;waitKey(0)少了这一行窗口会一闪而过;WINDOW_AUTOSIZE在板子上会自动全屏放大;如果你更想手动调窗口大小,换成WINDOW_NORMAL。

4.2 命令行单条 g++:六个参数逐个拆

这是最短的路径,适合验证环境、编一两个测试程序。整条命令是一行,反斜杠只是换行排版:

/opt/atk-dlrk3568-toolchain/bin/aarch64-buildroot-linux-gnu-g++ opencv_test.cpp -o opencv_test \ --sysroot=/opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot \ -I /opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot/usr/include \ -I /opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot/usr/include/opencv4 \ -L /opt/atk-dlrk3568-toolchain/aarch64-buildroot-linux-gnu/sysroot/usr/lib \ -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_videoio -lopencv_imgcodecs

逐项拆开看:

  • 编译器用绝对路径,是为了绕开PATH里可能存在的同名工具,确保用的是这套 aarch64 的。
  • --sysroot=告诉编译器把所有相对查找统一到板端根文件系统,跟后面的-I、-L配合使用,不写也能编,但容易混进宿主机的头文件。
  • 两个-I分别指向usr/include和usr/include/opencv4,前面 2.2 节解释过为什么两个都要。
  • -L指向 sysroot 的usr/lib,链接器去这里找.so。
  • -l后面跟的是库名去掉lib前缀和.so后缀的部分。libopencv_core.so对应-lopencv_core。

哪些库必须加?这里没有标准答案,取决于你调了哪些 API。imread、imwrite在imgcodecs;imshow、namedWindow在highgui;Mat、基础矩阵运算在core;cvtColor、缩放、滤波在imgproc;视频取帧在videoio。最省事的做法是先把这几个常用的全写上,报undefined reference时再去sysroot/usr/lib下ls一遍看哪个库名没加。

4.3 Makefile 版本:把路径抽成变量

命令一长,改路径就是灾难。Makefile 的价值在于把工具链目录抽成一个变量,换工具链只改一行:

# 工具链根目录,其他路径都由它推导 TOOLCHAIN_DIR := /opt/atk-dlrk3568-toolchain CXX := ${TOOLCHAIN_DIR}/bin/aarch64-buildroot-linux-gnu-g++ SYSROOT := ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot # 头文件搜索路径:两个 -I 缺一不可 CXXFLAGS += --sysroot=${SYSROOT} CXXFLAGS += -I${SYSROOT}/usr/include CXXFLAGS += -I${SYSROOT}/usr/include/opencv4 # 库搜索路径与需要链接的模块 LDFLAGS += -L${SYSROOT}/usr/lib LDLIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc \ -lopencv_videoio -lopencv_imgcodecs all: opencv_test # 目标: 依赖,链接顺序是 目标文件在前、库在后 opencv_test: opencv_test.cpp ${CXX} $< ${CXXFLAGS} ${LDFLAGS} ${LDLIBS} -o $@ clean: rm -f opencv_test .PHONY: all clean

注意$<放前面、${LDLIBS}放最后,这是 GNU ld 的规矩:被依赖的库必须出现在引用它的目标文件之后,顺序颠倒会报一堆undefined reference,这跟库到底存不存在无关。写完make就编,make clean清二进制。

4.4 CMakeLists 版本:build 目录分离

项目一多,手写 Makefile 的维护成本就上来了。CMake 的思路是写一份与平台无关的描述,由它生成 Makefile:

# CMakeLists.txt cmake_minimum_required(VERSION 3.8) message(STATUS "cmake version ${CMAKE_VERSION}") # 工具链与 sysroot —— 必须在 project() 之前设置才生效 set(TOOLCHAIN_DIR /opt/atk-dlrk3568-toolchain) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/aarch64-buildroot-linux-gnu-g++) set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/aarch64-buildroot-linux-gnu-gcc) set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot) set(CMAKE_CXX_STANDARD 11) # 按依赖顺序列出要链接的模块 set(OPENCV_LIBS opencv_core opencv_imgproc opencv_imgcodecs opencv_videoio opencv_highgui) include_directories(${CMAKE_SYSROOT}/usr/include) include_directories(${CMAKE_SYSROOT}/usr/include/opencv4) project(opencv_test) add_executable(opencv_test opencv_test.cpp) target_link_libraries(opencv_test ${OPENCV_LIBS})

这里有个坑必须点名:CMAKE_CXX_COMPILER和CMAKE_SYSROOT一定要写在project()之前。CMake 在project()那一刻就把编译器定下来了,写在后面等于没写,编出来的还是 x86 的二进制。

生成的 Makefile 不要和源码混在一起,建build目录隔离:

mkdir -p build && cd build cmake .. make

cmake ..会读上一级的CMakeLists.txt,在当前目录生成 Makefile;make生成可执行文件;要清干净用make clean,要连 CMake 缓存一起清就把build目录整个删掉重来。源码目录里如果之前留过 Makefile,先删掉,否则 CMake 可能复用旧的构建规则,导致你改了CMakeLists.txt却不生效。

5. 常见问题排查:五个我真实遇到过的坑

环境这东西,装十次有八次都会在某个环节卡一下。下面五条按「现象 → 原因 → 解决」写,基本上覆盖了这套流程里八成以上的翻车场景。

5.1 编译期的三个报错

第一条:opencv2/opencv.hpp: No such file or directory。

现象是编译器明确告诉你找不到头文件,但你去ls路径,文件明明在那儿。原因是 include 写法和-I路径没对上。源码里写<opencv2/opencv.hpp>时,编译器会去找-I目录下的opencv2/opencv.hpp;而 sysroot 里实际是usr/include/opencv4/opencv2/opencv.hpp,中间多一层。解决办法是把源码改成<opencv4/opencv2/opencv.hpp>,同时把-I指到usr/include这一级。反过来,如果只写了-I .../usr/include/opencv4而源码带opencv4/前缀,找的路径就变成opencv4/opencv4/opencv2/...,照样报错。两个-I都加最稳。

第二条:路径里写的是atk-dlrk356x-toolchain,报No such file or directory。

这条是文档自己的笔误,不少示例代码里工具链目录写成atk-dlrk356x-toolchain(x 结尾),但安装脚本默认落在/opt/atk-dlrk3568-toolchain(68 结尾)。照抄就炸。两种修法:一是把脚本里的路径全部改成3568,二是建个软链接把两个名字都指向同一个目录。我一般选第二种,因为改代码容易漏:

sudo ln -s /opt/atk-dlrk3568-toolchain /opt/atk-dlrk356x-toolchain

第三条:undefined reference to cv::imread(...)之类的链接错误。

编译过了,链接挂了。这说明头文件路径对了,但库没链上。常见原因有三种:库名写错(比如把opencv_imgcodecs写成opencv_imgcodec);库的顺序不对(-l全堆在源文件前面);或者确实漏了某个模块。排查顺序是先在sysroot/usr/lib下ls libopencv_*.so看准确名字,再检查命令行里-l是否都在源文件之后。

5.2 运行期的两个现象

第四条:error while loading shared libraries: libopencv_core.so.4.5。

在板子上跑程序,第一句就报这个。原因是板端的动态链接器在默认路径下找不到 OpenCV 的库。虽然库在/usr/lib下,但程序实际用的库路径可能不含这一项。临时救急可以指定:

# 在板端执行,临时指定库搜索路径 LD_LIBRARY_PATH=/usr/lib ./opencv_test

要长期解决,用ldd ./opencv_test看它到底依赖哪些库、哪些显示not found。如果只有某个不常用的模块缺失,那就说明板端系统裁剪掉了它——这种情况必须改代码避开那个模块,硬补库可能牵扯一堆依赖。

第五条:程序跑起来了,但image.empty()一直为真,打印「无法读取图片文件」。

这是图片路径的问题,跟 OpenCV 没关系。imread里的路径是板端的绝对路径,需要你在板子上确认文件真实存在:ls -l /opt/apps/src/logo/demo_logo.png。注意大小写,Linux 是区分大小写的,Demo_Logo.PNG和demo_logo.png是两个文件。还有一点容易忽略:如果 PNG 文件在从 Windows 传过来的时候没设二进制模式,文件会被损坏,文件在但读不出来。用 scp 或者 adb push 传,别用 FTP 的文本模式。

顺带记一条显示相关的:接 HDMI 显示器时,Weston 桌面可能只占屏幕中间一小块。这不是 OpenCV 的问题,是 Weston 的显示配置。解决办法是把 HDMI 设为主显示,或者按显示方向旋转 90 度,具体配置项在 RK 官方的 Weston 开发指南里有,属于系统层的事,跟本文的编译流程无关,但你迟早会撞上。

6. 装完怎么验证:三板斧确认这套环境真的通了

到这里,环境该装的装了、该配的配了,剩下唯一的事是证明它真的能跑。别急着写业务代码,先拿opencv_test走一遍完整链路,任何一环不通都是环境问题而不是代码问题。

我习惯按「宿主机构建 → 传输 → 板端运行」三段验证,每段都有明确的通过标准:

验证段命令通过标准
构建产物架构file opencv_test输出含ELF 64-bit LSB executable, ARM aarch64
依赖是否齐aarch64-buildroot-linux-gnu-readelf -d opencv_test | grep NEEDED列出libopencv_core.so.4.5等,无<not found>
传输后完整性板端ls -l opencv_test,比对字节数两边大小一致,且板端有可执行权限
实际运行板端./opencv_test屏幕出现图片窗口,按任意键退出

第三行那个字节数比对,是我吃过亏之后加上的:曾经用 FTP 传二进制,文件坏了,ls看得到,一跑就Segmentation fault,查了两小时才发现文件不完整。

传输用 scp 或 adb push 都行,关键是走二进制模式:

# 在 Ubuntu 端执行,把可执行文件推到板子的 /home/root 下 scp opencv_test root@<板子IP>:/home/root/ # 或者用 adb adb push opencv_test /home/root/

推到板子上之后,先chmod +x opencv_test,再执行。如果屏幕上有图出来,这套 OpenCV4 交叉编译环境就算按预期跑通了。Python 路线更简单,把.py文件推过去,直接python3 opencv_test.py,屏幕出图即证明板端的 cv2 模块和显示链路都是通的。

从那以后我每次新装一套工具链,不管文档写得多清楚,都强制先跑一遍这三板斧——编译一个只有main(){return 0;}的空程序,用file确认架构,再传上去跑一遍。看起来多花两分钟,但能把「工具链装错位置」和「程序逻辑写错」这两类问题彻底分开,省下的是后面几小时的黑匣子排查。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询