ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

树莓派双线寻迹实战:OpenCV Python与L298N差速转向

树莓派双线寻迹实战:OpenCV Python与L298N差速转向

简介:这是一份基于树莓派3B四轮驱动小车、利用OpenCV与Python实现双线寻迹的完整项目设计报告,适合自动化、嵌入式视觉方向的学生与开发者参考学习。内容围绕双线寻迹任务,系统梳理了系统烧录与环境配置、L298N电机驱动转向控制、视觉处理参数调优以及死机问题排查等关键环节,尤其针对无法反转转向、动态阈值调整、摄像头安装角度等实际难点给出了可复现的解决方案,可作为无人驾驶视觉循迹等竞赛或课程项目的技术蓝本。报告从系统环境搭建、图像处理到电机控制逐步展开,涵盖问题重述、代码逻辑与实验数据等模块,资源包共1个docx文档,约1.37MB,包含项目报告全文、代码逻辑说明及参考文献,便于直接阅读、批注与二次编辑。项目还涉及SSH远程连接与热点配置,对远程调试与控制小车同样具有参考价值。已有248人学习该资源,适合具备一定Python与OpenCV基础、希望快速上手视觉寻迹小车实践的读者。

1. 树莓派双线寻迹:用 OpenCV Python 实现视觉车道保持

双线寻迹不是只找一条黑线就完事,而是让小车在两条黑线之间的“车道”里稳定跑,碰到转折还能快速回头。这个项目用树莓派跑 OpenCV Python,先对摄像头画面做灰度、高斯模糊、膨胀、二值化,找到双线黑色像素的重心,再用 L298N 驱动四轮小车完成左右修正。最容易劝退人的不是算法本身,而是环境、硬件和参数:旧系统装不上新版 OpenCV、L298N 死活不能反转、二值化阈值把影子当成轨道线、主循环里嵌套 while 直接让树莓派死机。这份资源附带主程序 123334.py 和实验数据压缩包,记录了一套能稳定跑通的处理流程,适合备战电赛、做课程设计或第一次玩视觉小车的人。

2. 系统与依赖:把树莓派刷到 Python 3.9.2 并装好 OpenCV 4.5

2.1 旧系统上装 OpenCV 为什么这么难

大多数树莓派小车项目真正卡住的地方不在代码,而是在环境。这个项目最开始就撞上了版本地狱:旧系统自带的 Python2 和 Python3 版本都太老,其中 python3 还停在 3.4,而新版 OpenCV 的预编译 wheel 普遍要求 Python 3.7 以上。想找对应的低版本 OpenCV 安装包,不仅难找,而且装上后还会缺一堆底层库。更麻烦的是,旧系统里的 CMake 版本太老,直接编译 OpenCV 会在中途报错,根本走不到生成库文件那一步。

如果设备再处于一个不能稳定联网的环境,靠手动一个个收集 .whl 和依赖包基本是死路一条。这个项目里就出现过类似情况:好不容易找到一个匹配的 opencv whl,安装时又因为 CMake 太旧失败,继续查又发现缺更多依赖库,前前后后耗掉四天。所以现在拿到这种项目,我的习惯是先看系统版本,一旦发现 Python 低于 3.7,先不要花时间折腾旧系统,而是直接烧一个新版镜像。

常见做法是去官网下载最新镜像,烧录完以后把系统升级一遍,Python 会默认来到 3.9.2,再装 OpenCV 4.5 就会顺利很多。这一步看起来像机械操作,其实是整个项目成功的一半。因为后面的视觉处理、L298N 控制、摄像头读流,全部建立在一个能正常 import cv2 的环境上。基础不牢,后面每一步都会变成玄学。

2.2 烧录新版系统并打开 SSH

把镜像烧到 SD 卡只是第一步,最值得学的是一步到位打开 SSH 和 WiFi 连接。树莓派系统写卡完成后,电脑上能看到 boot 分区,在 boot 下创建一个名为 ssh 的空文件,并放一个 wpa_supplicant.conf,树莓派上电后就会自动连接热点。这样完全不需要接显示器,只用 SSH 从局域网进去操作,省掉外设开销。

在 boot 分区下创建 wpa_supplicant.conf,填入热点信息:

ctrl_interface=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=CN network={ ssid="改成你的热点名" psk="改成热点密码" key_mgmt=WPA-PSK }

这段配置在官方系统上通用,注意文件名和路径不要拼错。如果遇到密码里有特殊符号连不上,可以先用纯字母数字的热点名测试。上电后进入路由器后台查看 DHCP 列表,找到树莓派分配的 IP,再通过 SSH 工具登录。

登录以后,先更新系统,再检查 Python 版本:

sudo apt update sudo apt upgrade -y python3 --version

正规的系统升级可能需要十几分钟,中间不要断电。如果下载慢,可以先把 apt 源换成国内软件源再执行。看到 Python 版本变成 3.9.2 或更高,就说明这一步完成了。

2.3 安装 OpenCV 4.5 与配套依赖

环境升级完,接下来安装 OpenCV。这个项目用的是 OpenCV 4.5,配套库包括 numpy、picamera、RPi.GPIO 等。最省事的方式是直接用 pip 安装:

sudo apt install -y python3-pip python3-numpy python3 -m pip install --upgrade pip python3 -m pip install "opencv-python>=4.5,<4.6" opencv-contrib-python

这里 opencv-python 是主库,opencv-contrib-python 包含额外模块,双线寻迹里不一定用到,但新版树莓派系统装上之后兼容性整体更好。安装完成后,用一句命令验证:

python3 -c "import cv2; print(cv2.__version__)"

如果输出 4.5.x,就说明 OpenCV 已经可用。如果 pip 安装时找不到匹配版本,优先检查 Python 版本,其次检查 pip 镜像源,不要走源码编译路线。源码编译会涉及 cmake、build-essential、libjpeg、libavcodec 等一堆编译依赖,中途任何一个小版本不匹配都会浪费时间。只有实在找不到 wheel 时才推荐源码编译,而且建议把编译时间预留四小时以上。

安装完基础库后,再装一些实际会用到的辅助库。比如控制 GPIO 需要 RPi.GPIO,处理图像数组需要 numpy,这些在资源包的环境说明里都对应列了出来。整个环境搭建的思路可以总结成一句话:系统版本对齐,依赖库一次装齐,代码里就不再出现 import 层面的怪问题。

3. L298N 转向控制:反转失效后如何用差速转弯

3.1 L298N 接线与正反转原理

L298N 是四轮小车最常用的电机驱动模块,IN1、IN2 控制左侧电机,IN3、IN4 控制右侧电机,ENA、ENB 是左右两侧的使能和 PWM 通道。理论上,IN1 接高、IN2 接低,左电机正转;IN1 接低、IN2 接高,左电机反转。如果能稳定反转,最简单的小车转向方式就是左右两侧反向转动,实现原地转圈或极小半径转弯。

但这个项目的实车调试发现,无论怎么给 IN1、IN2 设置高低电平,电机都只能正转,无法反转。常见原因有几个:驱动板使能跳帽没有移除,导致使能引脚被拉到固定电平;树莓派与驱动板没有共地,控制信号参考电位不一致;也有部分 L298N 板子内部焊接的半桥逻辑有问题,只支持单方向输出。

排查这类硬件问题,顺序是先用万用表量一下驱动板输入端的 5V 和 GND 是否稳定,再确认树莓派的 GND 和驱动板 GND 是否接在同一个节点,最后检查使能跳帽和 PWM 引脚。如果这些都没问题,电机还是不能反转,就不要继续耗在硬件上。项目里采用的办法很务实:放弃正反转方案,改成差速转向。

3.2 差速转向:一侧停一侧转

四轮小车整体质量很轻,一侧车轮停住、另一侧高速正转时,小车能产生足够的转动力矩来完成转弯。这个方案绕过了反转失效的问题,也不需要改任何硬件。逻辑上非常简单:需要左转时,左轮 PWM 给 0,右轮保持正转;需要右转时,右轮 PWM 给 0,左轮保持正转。因为电机本身有惯性,即使输入 PWM 0,它还可能会滑行一小段,所以更好的是直接把占空比改为 0,让执行器没有持续驱动力。

实际控制代码可以这样封装:

import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) IN1, IN2 = 17, 27 IN3, IN4 = 22, 23 ENA, ENB = 18, 13 for pin in [IN1, IN2, IN3, IN4, ENA, ENB]: GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW) pwm_left = GPIO.PWM(ENA, 100) pwm_right = GPIO.PWM(ENB, 100) pwm_left.start(0) pwm_right.start(0) def motor(side, speed): if side == 'left': if speed >= 0: GPIO.output(IN1, GPIO.HIGH) GPIO.output(IN2, GPIO.LOW) else: GPIO.output(IN1, GPIO.LOW) GPIO.output(IN2, GPIO.HIGH) pwm_left.ChangeDutyCycle(abs(speed)) else: if speed >= 0: GPIO.output(IN3, GPIO.HIGH) GPIO.output(IN4, GPIO.LOW) else: GPIO.output(IN3, GPIO.LOW) GPIO.output(IN4, GPIO.HIGH) pwm_right.ChangeDutyCycle(abs(speed)) def turn_left(): motor('left', 0) motor('right', 100) def turn_right(): motor('left', 100) motor('right', 0) def go_straight(): motor('left', 100) motor('right', 100)

这段代码里的 speed 取 0 到 100,代表 PWM 占空比,正数表示正转,负数表示反转。就算当前驱动板只支持正转,把负数分支也留着没有坏处,以后换驱动板时可以直接复用。注意 IN 引脚只负责方向,PWM 必须放在 ENA 和 ENB 上,否则调速和方向控制会在同一个引脚上互相干扰。

3.3 转向时间与节拍控制

差速转向不能一直执行,否则小车会原地画圈。更合理的做法是每次转向只持续一个短时间窗口,比如 200 到 500 毫秒,然后立刻恢复双轮前进。时间太短则转不过去,时间太长则转向过度。处理办法是在转向函数里记录开始时间,主循环中通过时间差判断是否需要结束转向。不要用 time.sleep() 阻塞整个循环,因为树莓派视觉处理本来就吃 CPU,sleep 会让摄像头采集一卡一顿。

下面的代码展示了主循环里怎样控制转向节拍:

import time last_turn_time = 0 TURN_DURATION = 0.3 # 每次转向持续秒数 while True: ret, frame = cap.read() if not ret: continue direction = get_turn(binary, frame.shape[1]) now = time.time() if direction == "left" and now - last_turn_time > TURN_DURATION: turn_left() last_turn_time = now elif direction == "right" and now - last_turn_time > TURN_DURATION: turn_right() last_turn_time = now elif now - last_turn_time > TURN_DURATION: go_straight()

这里引入时间戳,是为了避免转向分支被连续频繁触发。实际标定时,先用 0.3 秒起步,再以 0.05 秒为单位上下调整。如果小车在八字绕环时过弯太快,就缩短时间;如果冲出线了,就加长时间或降低前进 PWM。记住,占空比 100 起步很容易打滑,建议先把直线速度降到 70 左右,再单独调转弯速度。

3.4 差速比、PWM 上限与左右轮偏差

四轮小车即使左右电机型号一致,装上轮胎后也经常出现直行跑偏的情况。原因可能是电机转速差异、轮胎气压不同、电池电压波动。这种偏差在视觉闭环里会被部分修正,但修正多了会显得转向动作频繁。更稳的做法是在代码里预留左右修正系数。

比如初始化时设两个变量:

left_trim = 1.0 right_trim = 1.0

直行时实际占空比是 base_speed * left_trim 和 base_speed * right_trim。把车放在一个空旷走廊里,先以 60 占空比直行,观察它往哪边偏。如果往左偏,说明右边轮子速度快,把 right_trim 调低 0.05;如果往右偏,就调低 left_trim。这样反复调一轮后,直行轨迹会稳定很多。

另外,转弯时“停住”的那一侧也可以用很小的 PWM 来减轻轮胎与地面的摩擦,比如左转时左轮给 5 而不是 0。对抓地力较大的橡胶轮胎来说,完全停住容易产生顿挫感;给小值 PWM 会让转向更平滑。这个做法需要根据地面材质调整,瓷砖地面可以给 0,地毯地面建议给 5 到 10。

4. 视觉处理:灰度、膨胀、二值化与双线中心提取

4.1 图像预处理:从 BGR 到二值图

摄像头采集到的图像是 BGR 彩色图,直接找黑色区域理论上可行,但三个通道的计算量是灰度图的三倍。所以这个项目的第一步就是灰度化。灰度化以后,图像里还会残留大量光照噪声,特别是树莓派自带摄像头对光线很敏感,模糊处理能平滑掉一部分随机噪声。这里用的是高斯模糊,核大小取 5×5,既保留黑线边缘,又不会把线完全抹平。

膨胀操作用来把断裂的黑色线条连接起来。如果黑线中间有磨损或反光,很容易出现断点,膨胀之后断点被填充,后续找黑色像素重心会更稳定。核大小用 3×3,迭代次数建议控制在两次。迭代太多次会把黑线膨胀得太粗,让转向判断变得过度敏感。预处理函数可以这样写:

import cv2 import numpy as np def preprocess(frame): gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) kernel = np.ones((3, 3), np.uint8) dilate = cv2.dilate(blur, kernel, iterations=2) return gray, dilate

这里把灰度图和膨胀图都返回,调试时可以直接显示每一步的结果。一个容易被忽略的点是:高频的图像处理操作需要在低分辨率下跑。树莓派跑 OpenCV 时,如果输入分辨率是 640×480,帧率还勉强能接受;如果上到 1280×720,CPU 占用会明显上升,转向也会变得卡顿。常见做法是先把输入缩到 320×240,双线识别的精度损失很小,但处理速度能快不少。

4.2 二值化阈值:从 50 调到 70 的原因

二值化是双线寻迹里最需要“拍脑袋”的地方。二值化的意思是拿一个阈值把灰度图分成黑和白两种像素。灰度值低于阈值归为黑线,高于阈值归为白色背景。这个项目最初把阈值设为 50,结果小车在影子下疯狂误判,把阴影也当成了黑色轨道边界,直接在直线段就开始转向。

为什么 50 不行?因为黑线在正常光照下灰度值大约在 20 到 40 之间,但阴影区域的灰度值可能落在 50 到 70 之间。阈值设成 50,等于把一个还不太黑的阴影也划进了黑线范围。把阈值提高到 70 之后,黑线能被完整保留,大部分浅阴影被排除。调参代码很简单:

_, binary = cv2.threshold(dilate, 70, 255, cv2.THRESH_BINARY)

这一行把膨胀后的灰度图变成黑白图。注意 THRESH_BINARY 模式下,灰度大于阈值的像素变成 255,小于阈值的变成 0。也就是说,黑线对应的像素是 0,背景是 255。后面计算黑色像素重心时,要找的是 binary == 0 的位置。如果希望显示效果更直观,也可以换成 THRESH_BINARY_INV,让黑线变成 255、背景变成 0,但后面的检测逻辑要做相应取反。

调阈值不能只看单个数值。我一般会固定小车不动,保存 10 到 20 帧画面,挑出带阴影的一帧,用不同阈值连续跑一遍,观察黑线是否完整连续、背景是否干净。如果阈值是 70 时黑线仍有断裂,就考虑增加膨胀迭代次数,而不是继续调低阈值。阈值是为了分离目标,不是为了迁就断线。

4.3 双线中心提取:250 和 100 的标定逻辑

二值图处理完以后,下一步是找到两条黑线之间的中心位置。这个项目采用的方法很直接:把所有黑色像素的 x 坐标取平均。因为双线对称,黑色像素平均 x 坐标大约等于两条线之间的中线位置。如果小车在车道中偏右,双线整体会往画面左侧移动,平均 x 坐标变小;如果偏左,平均 x 坐标变大。所以根据平均 x 坐标落点就能判断该往哪边修。

项目里给出的判断阈值是:中心值大于 250 就左转,小于等于 100 就右转。这两个数天然依赖摄像头分辨率,所以在代码里我会用比例代替绝对值,可移植性更好:

def get_turn(binary, img_width): black_pixels = np.where(binary == 0) if len(black_pixels[1]) == 0: return "unknown" center_x = int(np.mean(black_pixels[1])) if center_x > img_width * 0.39: return "left" elif center_x <= img_width * 0.15: return "right" else: return "straight"

解释一下这段代码里的比例。如果图像宽度是 640,乘以 0.39 后大约是 250;乘以 0.15 大约是 100。把绝对值改成比例之后,切换到 320 宽图像也能直接复用。实际效果是:当双线中心明显偏右时,返回左转;明显偏左时,返回右转;居中区域则直行。这里的“中心”是黑线整体的视觉重心,而不是某一条线的质心。

这个平均坐标法在赛道干净时非常有效。如果画面上出现其他黑色物体,比如桌腿、阴影边缘,平均坐标会被拉偏。所以资源库里特别强调先做好二值化,再用 ROI 裁剪掉干扰区域。ROI 可以直接取画面下方 1/3,这部分基本只有地面和轨道黑线。

4.4 完整主循环:把视觉判断转换为运动指令

预处理、阈值和二值化中心提取都写好后,主循环就是把它们串起来。一个典型的主循环如下:

cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: continue gray, dilate = preprocess(frame) _, binary = cv2.threshold(dilate, 70, 255, cv2.THRESH_BINARY) direction = get_turn(binary, frame.shape[1]) if direction == "left": turn_left() elif direction == "right": turn_right() else: go_straight()

这段代码只保留了一层 while,没有嵌套循环,CPU 占用相对稳定。尤其要注意的是,转向函数内部不要再用 while 等待,否则主循环会被卡在转向分支里,摄像头画面无法更新,视觉判断就失去意义了。项目报告里死的机,根因就在这里。

运行调试时,可以在主循环里把 center_x 和 direction 打印出来,跑一段录像对照。如果发现转向方向和实际偏移方向相反,就把 get_turn 里的 left/right 判断反过来,这是双线寻迹最常见的逻辑翻车点。不要改图像处理链路,先确认方向。

5. 运行排查:死机、误判和摄像头安装的常见问题

5.1 转向后死机:嵌套 while 把 CPU 打满

现象:小车跑起来以后,只要执行一次左转或右转,过几秒树莓派就失去响应,画面冻结,SSH 也连接不上。单独运行视频处理代码或者单独跑电机代码,都不会死机。

原因:主程序里写了两个 while 循环嵌套。外层 while 负责视频采集,内层 while 负责等待电机转向完成。内层循环不断空转检查状态,占用了大量 CPU 时间,导致视频采集线程无法及时执行。树莓派本身算力有限,长时间占用后系统直接卡死。

解决:把内层 while 改成 if,用主循环的轮询来代替阻塞等待。只要转向动作进入 if 分支,设置一个开始时间,下一帧根据时间差决定是否结束转向。报告里描述“将第二个 while 改为 if”后死机问题消失,本质就是减少 CPU 忙等。

5.2 阴影误判:二值化阈值不是越低越灵敏

现象:小车经过窗户投影、桌子阴影或倒在地上的棍子影子时,转向判定突然乱来,明明在直线段,却频繁左转右转,甚至直接冲出黑线范围。

原因:二值化阈值最初设成 50,把阴影区域也划进了“黑色”范围。黑线的灰度可能只有 20,阴影的灰度却可能是 60,两者都被判定成黑色,导致黑色像素的平均 x 坐标严重偏移。

解决:把阈值提高到 70,让黑线和阴影之间留出足够的灰度间隔。项目报告里给出的参数是 70,这是经过实地测试选出来的值。如果想自己标定,可以用 cv2.createTrackbar 动态调阈值,实时观察二值化输出。调节过程中只要黑线连续、背景干净,就说明这个阈值基本够用。

5.3 摄像头垂直安装,采集到太多无效信息

现象:小车在干净的跑道上也会突然乱拐,视觉处理看起来没问题,但实际跑到场地边缘时轨迹明显不稳定。

原因:摄像头垂直于小车固定,视野里包含了天花板、墙面、远处桌面等大量无关信息。这些区域里任何亮度突变都会被误判成黑线,双线中心被污染。

解决:把摄像头改成斜向下固定,让画面只拍车前 20 到 40 厘米的地面区域。这个安装角度能显著减少无效信息,黑线在画面中的比例也更大。固定好以后,要重新标定一次二值化阈值,因为斜向视角下光照分布和垂直视角不同。项目报告里也提到了这点,调整摄像头角度后误判明显减少。

5.4 依赖安装失败:报错信息不是代码问题

现象:执行 pip install opencv-python 时报 No matching distribution,或者源码编译时报 CMake 版本过老,再往下查又发现缺 libjpeg、libavcodec 等大量底层库。

原因:树莓派系统镜像太旧,Python 版本停留在 3.4 或更早,OpenCV 新版本没有对应 wheel。旧系统中的 CMake 也无法完成 OpenCV 源码编译。这个问题并不是代码逻辑引起的,却被误认为程序问题,浪费了大量排查时间。

解决:直接烧录新版树莓派系统,把 Python 升级到 3.9.2,再安装 OpenCV 4.5。新版系统自带较新的 CMake 和基础编译环境,pip 也能找到匹配的 OpenCV 安装包。资源包里要求先刷系统,再装依赖,就是为了一步到位避开这条坑。

5.5 电机反转失效:不要死磕硬件,直接换控制策略

现象:IN1、IN2、IN3、IN4 的高低电平组合怎么换,四个电机都只正转,不反转,左右轮无法按预想的正反转方案转弯。

原因:L298N 驱动板存在两种常见问题。一是使能跳帽没有正确设置,导致 PWM 或方向逻辑被固定;二是树莓派与驱动板没有共地,控制信号参考电位不一致,产生错误的电平判断。也有部分驱动板的半桥驱动只支持单方向输出。

解决:按第 3 章的差速转向方案,一侧车轮停止,另一侧车轮高速正转。这种控制方式不需要依赖电机反转,完全绕开硬件限制。重点是把转向时间限制在一个较短窗口内,避免车一直画圈。如果转弯半径不稳定,可以微调转向持续时间和前进 PWM 占空比。

6. 调试验证:让双线寻迹从能跑变成跑得稳

6.1 先录制离线样本,再上实车

拿到资源包以后,不要急着把代码烧进小车直接跑。先把实验数据里录好的视频拿出来,用离线回放处理一遍,确认预处理、阈值和中心提取逻辑都正确,再上车测试。离线调试的好处是可以反复退回去看同一帧,定位是图像处理问题还是电机控制问题。回放脚本可以这样写:

import cv2 cap = cv2.VideoCapture("track_video.avi") while True: ret, frame = cap.read() if not ret: break gray, dilate = preprocess(frame) _, binary = cv2.threshold(dilate, 70, 255, cv2.THRESH_BINARY) direction = get_turn(binary, frame.shape[1]) cv2.putText(frame, direction, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow("debug", frame) if cv2.waitKey(1) == 27: break

这段代码把每个小节的转向结果直接叠加在原始画面上,按 Esc 退出。如果离线画面里转向方向和实际方向相反,或者双线中心一直在跳动,先修正图像处理链路,不要上车。上车后只做两个操作:调整转向时间,调整前进速度。这样能把变量控制到最少。

6.2 验证方向修正逻辑:录一段八字绕环样本

双线寻迹最难调的是“方向反馈闭环”。跑八字时,小车先左转再右转,如果在某个环节转向动作太猛,车会直接横穿黑线。我一般会在车顶粘一个小标记,用手机从俯拍角度录一段运行轨迹,然后慢放确认:当中心值大于 250 时,车是不是真的在向右偏;当中心值小于 100 时,车是不是在向左偏。如果方向不对,改 get_turn 里的左右逻辑即可。

调完方向,再看转向持续时间。在干净路面上,转向时间 0.3 秒通常能让车转弯约 45 度。如果实际转弯不够,就加 0.05 秒;如果过度,就减 0.05 秒。注意每改一次参数,都要重新跑一遍离线样本,确认前半程和后半程没有出现新的偏移。

6.3 加入形态学后处理,减少像素平均的抖动

如果发现黑色像素的平均 x 坐标在直线段仍然轻微抖动,可以在二值化之后加一个形态学闭运算,把细小孔洞补上:

kernel_close = np.ones((5, 5), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel_close)

闭运算先膨胀再腐蚀,能把黑线内部的白色噪点消除,让双线中心更稳定。代价是树莓派运算量稍微增加,帧率可能下降几帧。如果帧率掉得无法接受,就缩小输入分辨率,或者把 ROI 范围进一步收缩到双线所在的区域。

最后几个参数检查项我会按固定顺序过一遍:第一,阈值是否在 60 到 75 之间;第二,主循环里是否只有一层 while;第三,摄像头是否斜向下安装;第四,转向时间是否设置成可调变量。从那以后,我每次在树莓派上做视觉小车,都会在写控制代码之前先把系统版本、二值化阈值和主循环结构这三样列出来,逐项确认过再上车跑。这套习惯让我避开了大部分环境、误判和死机带来的坑,希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进