这次我们来看一个在电子设计竞赛(电赛)中非常具体且关键的决策点:当平衡滚球这类控制类题目遇到视觉识别需求时,是否必须依赖YOLO这类复杂的目标检测模型?标题“26年电赛平衡滚球放弃yolo的第一个晚上”暗示了一个重要的技术转向——从依赖通用AI模型转向更轻量、更确定性的传统图像处理方法。对于电赛这类对实时性、稳定性和资源限制有严苛要求的场景,这个决策往往意味着从“炫技”回归“实用”。
本文将深入探讨在平衡滚球系统中,放弃YOLO背后的技术逻辑,并提供一套完整、可落地的替代方案。核心在于,我们将使用OpenCV等传统计算机视觉库,通过颜色阈值、轮廓检测、形态学操作等“老方法”,实现对小球的快速、稳定定位。这套方案不依赖GPU,无需训练数据集,在树莓派、STM32+OpenMV甚至性能更低的单片机上都能流畅运行,真正契合电赛的嵌入式与实时控制本质。
如果你正在备战电赛,尤其是控制类题目,或者任何需要在资源受限环境下实现快速视觉感知的项目,这篇文章将为你提供一个清晰的技术选型思路和一套可直接复用的代码框架。我们将从方案对比、环境搭建、核心算法实现、到系统集成与调试,一步步拆解如何构建一个不依赖YOLO的平衡滚球视觉模块。
1. 核心能力速览:传统CV方案 vs. YOLO方案
在电赛平衡滚球场景下,两种技术路线的对比如下:
| 能力项 | 传统计算机视觉 (OpenCV方案) | YOLO等深度学习方案 |
|---|---|---|
| 核心原理 | 颜色空间转换、阈值分割、轮廓查找、几何计算 | 深度神经网络前向推理,基于大量数据训练 |
| 硬件需求 | 极低。主频100MHz以上的MCU(如STM32H7)配合摄像头即可,无需GPU。 | 较高。通常需要GPU或高性能NPU加速,或使用轻量级模型在高端CPU上运行。 |
| 部署复杂度 | 低。依赖OpenCV等库,编译后可直接运行,无模型加载过程。 | 高。需要解决模型转换(如转ONNX)、推理框架集成(如TensorRT, OpenVINO)、依赖库复杂。 |
| 启动速度 | 极快。上电即运行,无模型加载时间。 | 较慢。需要加载模型权重文件,占用一定内存和初始化时间。 |
| 实时性 | 高。单帧处理时间通常在10ms以内,轻松满足50-100Hz的控制周期。 | 取决于模型和硬件。轻量级YOLO在CPU上可能达到100ms/帧,难以满足高速控制需求。 |
| 稳定性 | 高。算法确定性强,受环境光影响可通过参数调整适应,结果可预测。 | 中。依赖训练数据,在未见过光照、背景或球体颜色下可能失效,存在不确定性。 |
| 开发/调试 | 直观。每个处理步骤(二值化、轮廓)的结果可视可调,参数物理意义明确。 | 黑盒。模型内部决策过程不透明,调试困难,性能优化门槛高。 |
| 适合场景 | 电赛等实时嵌入式控制、特定目标追踪、颜色标记物定位、工业检测。 | 通用目标检测、复杂场景多目标识别、类别繁多的应用。 |
结论很明确:对于平衡滚球这个目标单一(小球)、背景相对固定、要求毫秒级响应的特定任务,传统CV方案在性能、可靠性、易用性和成本上全面占优。放弃YOLO不是技术倒退,而是工程上的最优解。
2. 适用场景与使用边界
2.1 最适合使用本方案的场景
- 全国大学生电子设计竞赛(电赛)控制类题目:如平衡滚球、智能车循迹、无人机定点投掷等,这些题目对系统的实时性、稳定性和成本有严格限制。
- 嵌入式视觉项目:基于树莓派、Jetson Nano、STM32、ESP32等微控制器或边缘计算设备,需要快速识别特定颜色或形状物体的项目。
- 工业或教育机器人:如抓取固定颜色的工件、追踪地面引导线、读取特定仪表盘数字等。
- 快速原型验证:在项目初期,需要快速验证视觉感知可行性,避免陷入复杂的模型训练和部署流程。
2.2 本方案的局限性(使用边界)
- 目标特征依赖性强:本方案核心依赖于颜色或形状的显著差异。如果小球颜色与背景非常接近,或者光照条件极端变化(如从室内到强日光下),需要精心调整参数或增加滤波算法。
- 无法处理语义信息:它只能告诉你“这里有一个符合颜色/形状特征的物体”,无法识别这个物体是“乒乓球”还是“高尔夫球”。对于需要物体分类的场景无效。
- 多目标、遮挡处理能力弱:虽然可以检测多个轮廓,但如果多个同色小球粘连或严重遮挡,算法可能将其识别为一个整体,需要更复杂的后处理。
- 背景需要相对简单:复杂的、动态变化的背景会产生大量噪声轮廓,增加处理难度和误检率。
重要合规提醒:本方案仅用于技术学习、学术竞赛和合法合规的自动化项目。在实际应用中,尤其是在涉及公共空间监控或人脸等生物特征时,必须严格遵守《个人信息保护法》等相关法律法规,确保数据采集的合法性,并采取充分的数据安全措施。
3. 环境准备与前置条件
为了复现一个完整的平衡滚球视觉模块,你需要准备以下软硬件环境。这套配置追求极简和高性价比,完全符合电赛要求。
3.1 硬件清单
| 组件 | 推荐型号 | 说明 |
|---|---|---|
| 主控制器 | STM32F4/H7系列、树莓派 3B+/4B、Jetson Nano | 电赛常用STM32,处理复杂视觉算法可选树莓派。本文示例代码兼容PC(用于仿真)和树莓派。 |
| 摄像头模块 | OV7670、OV2640、USB摄像头(如罗技C270) | 推荐带FIFO的OV7670/2640与STM32直接连接,或USB摄像头接树莓派。分辨率640x480足够。 |
| 屏幕(可选) | TFT LCD屏(如ILI9341驱动) | 用于实时显示图像和处理结果,方便调试。 |
| 平衡板与机构 | 自制或套件 | 需要可被舵机或步进电机控制的两个自由度的平板。 |
| 小球 | 乒乓球或颜色鲜艳的小球 | 关键:颜色需与背景板(通常为白色或黑色)形成高对比度。推荐亮橙色、亮绿色。 |
3.2 软件环境(以树莓派/PC为例)
- 操作系统:Raspberry Pi OS (Linux) 或 Windows 10/11。
- 编程语言:Python 3.7+ 或 C++。本文以Python为例,因其开发调试更快。
- 核心库:
- OpenCV:计算机视觉核心库。用于图像读取、颜色转换、阈值分割、轮廓查找等。
- NumPy:Python科学计算基础库,OpenCV依赖它处理图像数据。
- 开发工具:Visual Studio Code、PyCharm或Thonny(树莓派)。
3.3 环境安装步骤(Python)
在树莓派或PC上打开终端,执行以下命令安装必要库:
# 更新软件包列表 sudo apt-get update # 安装Python3和pip(如果未安装) sudo apt-get install python3 python3-pip # 安装OpenCV和NumPy # 方法一:使用apt安装(推荐,兼容性好) sudo apt-get install python3-opencv python3-numpy # 方法二:使用pip安装(版本可能较新) pip3 install opencv-python numpy # 验证安装 python3 -c "import cv2; import numpy; print(f'OpenCV版本: {cv2.__version__}')"如果使用STM32等单片机,则需要将OpenCV算法移植为C代码,并使用HAL库或标准库进行图像采集和处理。这涉及嵌入式编程,本文重点介绍算法原理和Python实现,嵌入式移植可作为进阶方向。
4. 算法核心:放弃YOLO后的视觉处理流程
我们的目标是:输入一帧图像,输出小球在图像坐标系中的位置 (x, y),以及小球的像素半径(可用于估算实际距离或判断是否丢失)。
整个处理流程是一个清晰的流水线,如下图所示(文字描述):图像采集->颜色空间转换 (RGB to HSV)->阈值分割 (二值化)->形态学操作 (去噪)->轮廓查找->圆心与半径计算->坐标输出。
下面我们分步详解并用代码实现。
4.1 颜色空间转换与阈值分割
RGB颜色空间对光照变化敏感。我们转换为HSV(色调、饱和度、明度)空间,其中Hue通道代表颜色种类,受光照影响小,更适合颜色分割。
import cv2 import numpy as np def find_ball_color_based(frame): """ 基于颜色阈值寻找小球 参数: frame: 输入BGR图像 返回: mask: 二值化掩膜,白色区域为可能的小球区域 processed_frame: 用于显示的处理后图像(调试用) """ # 1. 转换为HSV颜色空间 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 2. 定义小球的颜色范围 (HSV格式) # 例如:寻找亮橙色小球。需要根据实际小球颜色调整! # H值范围: 0-180, S: 0-255, V: 0-255 lower_orange = np.array([5, 150, 150]) # 橙色下限 upper_orange = np.array([15, 255, 255]) # 橙色上限 # 3. 根据阈值创建二值化掩膜 (mask) # 在lower和upper之间的像素变为白色(255),其余为黑色(0) mask = cv2.inRange(hsv, lower_orange, upper_orange) # 用于调试显示 processed_frame = cv2.bitwise_and(frame, frame, mask=mask) return mask, processed_frame关键调试:你需要使用一个颜色选择工具(如OpenCV的cv2.setMouseCallback写一个简单的脚本)来获取你的小球在HSV空间的实际值,并微调lower和upper数组。这是整个算法成功的基础。
4.2 形态学操作去噪
阈值分割后的二值图像可能包含噪声(小的白点)或小球区域有空洞。我们使用形态学开运算和闭运算来净化图像。
def morphological_cleanup(mask): """ 使用形态学操作清理二值图像 参数: mask: 二值化掩膜 返回: cleaned_mask: 清理后的掩膜 """ # 定义内核(卷积核),大小影响处理程度 kernel = np.ones((5, 5), np.uint8) # 开运算:先腐蚀再膨胀,去除小白点噪声 mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 闭运算:先膨胀再腐蚀,填充小球区域内部的小黑洞 cleaned_mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) return cleaned_mask4.3 轮廓查找与圆心定位
在干净的掩膜上,我们寻找所有轮廓,并假设面积最大的轮廓就是我们要的小球。
def find_ball_contour(cleaned_mask): """ 在掩膜中查找小球轮廓并计算其最小外接圆 参数: cleaned_mask: 清理后的二值掩膜 返回: (x, y): 圆心像素坐标 radius: 圆的像素半径 found: 是否成功找到标志 contour_frame: 绘制了轮廓和圆心的图像(调试用) """ # 深拷贝一份掩膜,因为findContours会修改图像 contour_mask = cleaned_mask.copy() # 查找轮廓 # cv2.RETR_EXTERNAL: 只检测外轮廓 # cv2.CHAIN_APPROX_SIMPLE: 压缩轮廓,节省内存 contours, _ = cv2.findContours(contour_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) x, y, radius = 0, 0, 0 found = False contour_frame = np.zeros((contour_mask.shape[0], contour_mask.shape[1], 3), dtype=np.uint8) if contours: # 找到面积最大的轮廓 largest_contour = max(contours, key=cv2.contourArea) area = cv2.contourArea(largest_contour) # 设置一个最小面积阈值,过滤掉过小的噪声轮廓 if area > 100: # 阈值需根据图像分辨率调整 # 计算最小外接圆 ((x, y), radius) = cv2.minEnclosingCircle(largest_contour) x, y, radius = int(x), int(y), int(radius) found = True # 绘制轮廓和圆心(用于调试显示) cv2.drawContours(contour_frame, [largest_contour], -1, (0, 255, 0), 2) cv2.circle(contour_frame, (x, y), radius, (255, 0, 0), 2) cv2.circle(contour_frame, (x, y), 5, (0, 0, 255), -1) return (x, y, radius, found, contour_frame)5. 功能测试与效果验证:从单张图片到实时视频
5.1 单张图片测试
首先用一张静态图片验证整个流程,这是最快速的调试方法。
def test_with_static_image(image_path): """使用静态图片测试视觉管道""" # 读取图片 frame = cv2.imread(image_path) if frame is None: print(f"错误:无法读取图片 {image_path}") return # 1. 颜色分割 mask, color_frame = find_ball_color_based(frame) # 2. 形态学清理 cleaned_mask = morphological_cleanup(mask) # 3. 查找轮廓和圆心 x, y, radius, found, contour_frame = find_ball_contour(cleaned_mask) # 显示结果 cv2.imshow("Original", frame) cv2.imshow("Color Mask", mask) cv2.imshow("Cleaned Mask", cleaned_mask) cv2.imshow("Contour & Circle", contour_frame) if found: print(f"小球找到! 圆心坐标: ({x}, {y}), 半径: {radius} 像素") # 在原图上标记 cv2.circle(frame, (x, y), radius, (0, 255, 255), 2) cv2.circle(frame, (x, y), 5, (0, 0, 255), -1) cv2.putText(frame, f"({x},{y})", (x+10, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1) cv2.imshow("Result on Original", frame) else: print("未找到小球。请检查颜色阈值或光照条件。") cv2.waitKey(0) cv2.destroyAllWindows() # 运行测试 test_with_static_image("test_ball.jpg")判断成功标准:
Color Mask窗口中小球区域应为清晰的白色块,背景为黑色。Cleaned Mask窗口中的白色区域应更干净、连贯。Contour & Circle窗口应能准确用绿线勾勒出小球轮廓,并用蓝圈画出最小外接圆,红点标记圆心。- 控制台输出正确的坐标和半径。
5.2 实时摄像头测试
通过摄像头实时验证算法的稳定性和帧率。
def real_time_ball_tracking(camera_id=0): """实时摄像头追踪小球""" cap = cv2.VideoCapture(camera_id) if not cap.isOpened(): print("错误:无法打开摄像头") return print("实时追踪中... 按 'q' 键退出。") while True: ret, frame = cap.read() if not ret: break # 图像预处理:可缩放以加速处理 scale_percent = 80 # 缩放到80% width = int(frame.shape[1] * scale_percent / 100) height = int(frame.shape[0] * scale_percent / 100) frame = cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA) # 核心处理管道 mask, _ = find_ball_color_based(frame) cleaned_mask = morphological_cleanup(mask) x, y, radius, found, _ = find_ball_contour(cleaned_mask) # 显示结果 display_frame = frame.copy() if found: cv2.circle(display_frame, (x, y), radius, (0, 255, 255), 2) cv2.circle(display_frame, (x, y), 5, (0, 0, 255), -1) cv2.putText(display_frame, f"Pos: ({x},{y}) R:{radius}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) # 计算并显示帧率 (简易版) # 在实际项目中,应使用更精确的FPS计算方式 cv2.imshow("Ball Tracking", display_frame) cv2.imshow("Cleaned Mask (Debug)", cleaned_mask) # 调试窗口 if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() # 启动实时追踪(默认使用第一个摄像头) real_time_ball_tracking()性能观察:
- 在树莓派4B上,处理640x480的图像,上述管道帧率可达15-25 FPS,完全满足平衡控制需求。
- 通过调整
scale_percent降低分辨率,可以显著提升帧率,但会损失定位精度,需要权衡。 - 观察
Cleaned Mask窗口,确保在不同光照下,小球区域都能被稳定提取。
6. 系统集成:从像素坐标到控制指令
视觉模块的最终目的是为控制器提供输入。我们需要将图像中的像素坐标(x, y)转换为平衡板需要调整的角度或位置误差。
6.1 坐标变换
假设摄像头固定在平衡板上方正中,俯视整个板面。图像中心(cx, cy)对应平衡板的平衡位置(目标点)。小球当前位置(x, y)与中心点的偏差就是误差。
def calculate_error(x, y, radius, frame_width, frame_height): """ 计算小球位置与目标中心位置的误差 参数: x, y, radius: 小球圆心和半径 frame_width, frame_height: 图像宽高 返回: error_x, error_y: X轴和Y轴方向的像素误差。正值表示小球在中心右侧/下方。 lost_target: 是否丢失目标标志 """ if radius == 0: # 未找到小球 return 0, 0, True # 计算图像中心 center_x = frame_width // 2 center_y = frame_height // 2 # 计算误差 (当前坐标 - 中心坐标) error_x = x - center_x error_y = y - center_y # 可以引入死区,当误差很小时认为已平衡 dead_zone = 10 if abs(error_x) < dead_zone: error_x = 0 if abs(error_y) < dead_zone: error_y = 0 return error_x, error_y, False # 在主循环中集成 frame_height, frame_width = frame.shape[:2] error_x, error_y, lost = calculate_error(x, y, radius, frame_width, frame_height) if not lost: # 将像素误差转换为物理误差(例如毫米)或直接作为控制器的输入 # 需要摄像头标定来获取像素与物理尺寸的换算关系 # physical_error_x = error_x * scale_factor print(f"控制误差: X={error_x}, Y={error_y}") # 这里可以调用控制函数,例如:send_control_command(error_x, error_y) else: print("目标丢失,执行搜索或安全策略") # 例如:控制板子缓慢平移或发出警报6.2 与控制器通信(示例)
在树莓派上,你可以通过GPIO控制舵机,或者通过串口/UDP将误差发送给下位机STM32。
# 示例:通过串口发送误差数据给STM32 import serial import time def init_serial(port='/dev/ttyUSB0', baudrate=115200): """初始化串口""" try: ser = serial.Serial(port, baudrate, timeout=1) time.sleep(2) # 等待串口稳定 return ser except serial.SerialException as e: print(f"串口打开失败: {e}") return None def send_error_to_controller(ser, error_x, error_y): """将误差数据打包发送""" if ser and ser.is_open: # 自定义简单协议,例如 "EX100,Ey-50\n" 表示X误差+100, Y误差-50 command = f"EX{error_x},EY{error_y}\n" ser.write(command.encode('ascii')) # print(f"已发送: {command.strip()}") # 在主程序中 ser = init_serial() if ser: send_error_to_controller(ser, error_x, error_y)7. 资源占用与性能优化
7.1 资源占用分析
在树莓派4B (4GB内存) 上运行上述Python脚本:
- CPU占用:单核利用率约30-50%,取决于图像分辨率和处理复杂度。
- 内存占用:约150-250 MB,主要被OpenCV和图像数据占用。
- 帧率:处理640x480图像,完整管道可达15-25 FPS。将分辨率降至320x240,帧率可提升至40-60 FPS。
与YOLO对比:一个轻量级YOLOv5s模型在树莓派CPU上推理一帧640x640图像可能需要500ms-1s,帧率仅1-2 FPS,且内存占用更大。在实时控制系统中,这是不可接受的延迟。
7.2 性能优化技巧
- 降低分辨率:这是最有效的提速方法。平衡滚球不需要4K图像,320x240甚至160x120往往足够。
- 减少处理区域 (ROI):如果小球只在画面中央区域运动,可以只对该区域进行处理。
roi = frame[100:400, 200:500] # 只处理y从100到400,x从200到500的区域 # 对roi进行处理,注意计算出的坐标需要加上偏移量(200, 100)才是原图坐标 - 优化颜色阈值:精确的HSV范围可以减少后续形态学操作的压力。
- 使用C++重写:对于性能极限要求,可将核心算法用C++实现,并通过Python绑定调用,性能可提升数倍。
- 多线程/多进程:将图像采集、视觉处理、控制逻辑放在不同线程,避免阻塞。
8. 常见问题与排查方法
在开发和调试过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 找不到小球(Mask全黑) | 1. HSV颜色阈值设置错误。 2. 光照太暗或太亮,超出阈值范围。 3. 摄像头白平衡异常,颜色失真。 | 1. 使用颜色选择工具获取小球真实HSV值。 2. 显示 hsv图像,查看各通道分布。3. 打印小球区域的HSV均值。 | 1. 调整lower和upper阈值。2. 增加光照或使用补光灯。 3. 固定摄像头白平衡参数。 |
| 找到多个轮廓或错误目标 | 1. 背景中存在相似颜色的物体。 2. 阈值范围太宽。 3. 形态学去噪不够。 | 1. 显示cleaned_mask,看哪些区域被误识别。2. 检查轮廓面积,过滤掉过小的噪声。 | 1. 优化背景,使用纯色背景板。 2. 收紧HSV阈值范围。 3. 增大形态学操作的内核大小。 |
| 小球轮廓不圆或有空洞 | 1. 光照不均匀,小球部分反光。 2. 小球本身不是纯色。 | 1. 观察mask中小球的形状。2. 尝试不同的颜色空间(如YCrCb)。 | 1. 使用漫射光源减少反光。 2. 增加闭运算的强度以填充空洞。 3. 考虑使用 cv2.fitEllipse代替最小外接圆。 |
| 帧率过低 | 1. 图像分辨率太高。 2. 算法步骤太多或未优化。 3. 摄像头采集延迟大。 | 1. 使用time.time()测量每个步骤耗时。2. 降低分辨率测试。 | 1. 降低处理分辨率。 2. 使用ROI。 3. 考虑使用 cv2.VideoCapture的set方法设置更低分辨率。 |
| 坐标抖动严重 | 1. 光照闪烁。 2. 阈值边缘效应,小球颜色在阈值边界徘徊。 3. 轮廓查找不稳定。 | 1. 观察连续帧的mask变化。2. 绘制坐标随时间变化的曲线。 | 1. 使用稳定的光源(如直流LED)。 2. 对坐标进行低通滤波(如移动平均)。 3. 适当放宽阈值,确保小球颜色始终在范围内。 |
| 串口通信失败 | 1. 串口号或波特率错误。 2. 线缆接触不良。 3. 权限不足(Linux下)。 | 1. 检查设备管理器(Win)或ls /dev/tty*(Linux)确认端口。2. 使用串口调试助手测试。 | 1. 更正端口和波特率。 2. 在Linux下,将用户加入 dialout组:sudo usermod -a -G dialout $USER,并重启。 |
9. 最佳实践与进阶建议
9.1 开发调试最佳实践
- 分步可视化:始终保留像
cv2.imshow这样的调试窗口,实时观察mask、cleaned_mask和最终结果。这是快速定位问题的关键。 - 参数可配置化:将HSV阈值、形态学内核大小、面积阈值等参数写成配置文件或通过滑条调节,避免硬编码。
# 使用OpenCV的createTrackbar动态调整HSV值 cv2.createTrackbar('H Low', 'config', 5, 179, nothing) - 日志记录:将关键数据(如坐标、误差、帧率)写入文件或打印到控制台,便于后期分析系统性能。
- 模拟测试:在实物平台搭建前,可以用预先录制好的视频或图片序列进行算法验证。
9.2 进阶方向
当你掌握了基础的颜色追踪后,可以探索以下方向提升系统鲁棒性:
- 多特征融合:结合颜色和形状(圆度检测
cv2.isContourConvex、轮廓近似cv2.approxPolyDP)来确认目标,减少误检。 - 动态阈值:根据图像整体亮度动态调整HSV中的V(明度)通道阈值,适应光照变化。
- 卡尔曼滤波:对小球坐标进行预测和滤波,平滑运动轨迹,并在目标短暂丢失时进行预测。
- 摄像头标定:通过标定获取摄像头的内参和畸变系数,进行图像去畸变,并将像素坐标精确转换为物理坐标(毫米),实现更精准的控制。
- 移植到C/嵌入式平台:将算法用C语言实现,直接运行在STM32+OV7670上,实现极致的低成本和低延迟,这才是电赛的终极形态。
放弃YOLO的第一个晚上,不是结束,而是一个更精准、更高效的开始。通过传统计算机视觉方法,你重新掌握了系统的每一个细节:从光信号到像素,从像素到轮廓,从轮廓到坐标,再从坐标到控制指令。这条技术路径透明、稳定、资源友好,完美契合了电赛这类对确定性和实时性要求极高的场景。
总结一下,在平衡滚球项目中,你应该优先尝试以下步骤:
- 快速验证:用你的小球和背景板,运行单张图片测试,确保HSV阈值能准确分割出小球。
- 实时调试:连接摄像头,运行实时追踪脚本,在动态场景下微调参数,确保稳定识别。
- 集成控制:将视觉模块输出的误差,通过最简单的P控制律驱动舵机,让板子能对小球的移动做出反应。
- 迭代优化:根据实际控制效果,回头优化视觉参数(如滤波、死区)和控制参数(如PID增益)。
这套方案不仅适用于平衡滚球,其核心思想——在特定任务中用简单、确定的算法替代复杂、不确定的模型——可以迁移到无数嵌入式视觉和控制项目中。建议收藏本文的代码框架和排查清单,在下次面临类似选择时,它能帮你做出更明智的工程决策。