2025电赛视觉

2025全国大学生电子设计大赛E题

效果及装置展示

视频1:项目功能演示——瞄准靶心

视频2:项目功能演示——画6cm圆

视频3:项目功能演示——视觉部分

装置展示视角1

图1:装置展示视角1

装置展示视角2

图2:装置展示视角2

简易自行瞄准装置——视觉系统设计与实现

在本次竞赛中,我的核心任务是让装置能够精准、快速地“看”到目标,并引导激光进行瞄准和画圆。具体来说,我负责的部分要实现两大核心功能:一是在静态和动态环境下精准识别A4靶纸上的靶心;二是在小车行进时,同步生成一个半径为6cm的标准圆形轨迹。考虑到我们作为计算机专业的学生,在方案选型之初,我对比了OpenMV、K230以及我熟悉的Jetson Nano。K230和Jetson Nano性能强大,支持复杂的AI模型,但并不是很适用于本题。首先,本题对小车尺寸上有严格限制,没有办法使用Jetson Nano如此大块头的嵌入式开发板;其次,对于本次任务的核心需求——快速、鲁棒的几何图形识别与定位,它们显得有些“杀鸡用牛刀”,且开发周期较长。因此,我最终选择了OpenMV作为我们的视觉处理单元。它基于MicroPython开发,拥有成熟的图像处理库,开发效率高,非常适合在有限的竞赛时间内完成任务。

OpenMV开发板

图1:OpenMV+变焦镜头
(开发迅速,紧凑高效)

K230开发板

图2:K230
(AI性能强,体积适中)

Jetson Nano开发板

图3:Jetson Nano
(生态成熟,体积较大)

我的核心算法设计主要分为三个层次。首先是目标检测与定位。我没有采用复杂的深度学习方法,而是回归到经典的数字图像处理技术。通过对图像进行灰度化和二值化,利用find_blobs函数寻找黑色连通域,并结合面积、像素数、矩形度等多个阈值进行筛选,从而稳定地框选出靶纸的黑色外框。为了提升鲁棒性,我还特别加入了一个“边缘贴合检查”的逻辑,有效避免了因光照不均或背景干扰,将图像的边角误识别为靶纸的情况。

其次是透视校正与坐标解算。由于摄像头拍摄角度不固定,靶纸在图像中会产生透视畸变,一个正方形会变成不规则的四边形。如果简单地取矩形中心作为靶心,会产生很大误差。因此,我通过提取矩形轮廓的四个顶点,利用对角线交点法来计算透视变换后的实际中心点,这个点比几何中心更能代表三维空间中的真实靶心,从而确保了我们的瞄准精度。在画圆任务中,这个校正后的靶心也为后续轨迹生成提供了精准的基准。

最后是动态轨迹生成与同步。这是本次设计的难点和亮点。为了在倾斜的靶面上画出一个标准的物理圆形,我设计了一套透视自适应的椭圆生成算法。简单来说,系统会根据识别到的靶纸矩形在图像中的变形程度(即长短边的比例变化),动态地计算出一个补偿性的椭圆参数。当我们在图像上绘制这个椭圆时,由于透视的“反向”作用,它在物理靶纸上就会被还原成一个标准的圆形。同时,为了与小车的运动同步,我通过周期匹配和角度平滑插值,确保了激光画圆的角速度与小车行驶的角速度保持一致,最终实现了小于1/4圈的同步误差。

在整个系统中,我负责的OpenMV与队友负责的STM32主控和MSPM0小车控制器之间需要高效协作。我们约定了一套简洁的串口通信协议。OpenMV作为从机,根据STM32发送的指令(如0xA1代表瞄准,0xA2代表画圆)进入不同工作模式。处理完成后,OpenMV会将计算出的目标坐标(x, y)打包成特定的数据帧(包含帧头、数据和帧尾),通过串口发送给STM32。STM32接收到坐标后,再通过PID算法驱动云台进行精确的运动控制。这种主从结构清晰,解耦了视觉处理和运动控制,提升了整个系统的稳定性和可维护性。

常见问题与解答(Q&A)

参赛合照

1. 问:你为什么最终选择了OpenMV,而不是性能更强的K230或Jetson Nano?

答: 这涉及到我们项目初期的核心技术选型。虽然我之前有K230和Jetson Nano的开发经验,知道它们拥有强大的算力,尤其是在运行神经网络等AI应用方面显然比OpenMV更好(我们用的是4不是4plus就更是如此)。但经过对赛题的深入分析,我判断本次任务的核心是几何图形的快速、稳定识别,而非复杂的场景理解或模式识别。OpenMV的优势在于:第一,开发效率高,它使用MicroPython,配合官方IDE,可以非常迅速地验证算法原型,这在时间紧张的电赛中至关重要。第二,针对性强,它的内置函数库,如find_blobsget_regression等,都是为这类传统的机器视觉任务高度优化的,足以满足赛题要求。第三,成本和功耗更低,系统集成更简单。综合考虑开发周期、任务匹配度和系统复杂度,我认为OpenMV是本次竞赛最高效、最合适的选择。

2. 问:在你的代码中,你提到了“边缘贴合检查”,能具体解释一下它的作用和实现原理吗?

答: 在调试过程中,我们发现当靶纸部分移出视野或在特定光照下,摄像头图像的边缘有时会被错误地识别成一个大的黑色区域(blob)。如果不对其进行处理,系统会错误地将这个“伪目标”作为靶纸,导致后续所有计算失效。“边缘贴合检查”就是为了解决这个问题。我的实现原理很简单:在find_target_boundary_rect函数中,当找到一个候选的矩形区域后,我会检查它的坐标 (x, y) 和尺寸 (w, h)。如果 xy 等于0,或者 x+w 大于等于屏幕宽度,或者 y+h 大于等于屏幕高度,就意味着这个矩形的一条边紧贴着图像的边界。在我们的应用场景下,完整的靶纸几乎不可能完美地贴合在图像边缘,所以一旦出现这种情况,我们就有理由认为这是一个误识别,并将其从候选目标中剔除。这个小技巧极大地提升了我们目标识别的鲁棒性。

3. 问:你是如何处理摄像头透视畸变,确保瞄准精度的?仅仅计算对角线交点就足够吗?

答: 处理透视畸变是我们保证精度的核心。我的方案分为两步:首先,通过find_blobs找到的黑色边框,我们能得到一个大致的矩形轮廓。然后调用find_rects或类似角点检测的算法来精确定位这个轮廓的四个顶点。得到这四个顶点坐标后,我采用了对角线交点法来计算靶纸的透视中心。理论上,在透视变换中,直线依然是直线,两条对-角线的交点在三维空间中是唯一的,并且非常接近靶面的物理中心。对于本赛题要求的2cm误差范围来说,这种方法的精度是完全足够的,并且计算量远小于完整的透视变换矩阵求解和图像校正(即image.warp_perspective),从而保证了处理速度。如果要求更高的精度,比如毫米级,那么可能就需要通过棋盘格标定计算出相机的内外参数,然后进行更精确的像素点反投影计算,但对于本次任务而言,对角线交点法是一个在精度和效率之间取得极佳平衡的方案。

4. 问:在画圆模式下,你是如何保证在物理靶面上画出的是一个标准圆,而不是一个椭圆?

答: 这是我们系统的一个创新点。由于透视效应,一个在物理世界中的标准圆,投影到倾斜的图像平面上会变成一个椭圆。反过来,如果我们想在物理靶面上得到一个标准圆,我们就必须在图像平面上绘制一个经过“预畸变”的椭圆。我的算法是这样做的:首先,我根据识别到的靶纸矩形在图像中的四个顶点,计算出它的两条边在图像中的长度比例,这个比例可以反映透视的强度。然后,我以靶纸短边的物理长度(21cm)和它在图像中的像素长度为基准,计算出6cm物理半径对应的像素半径r。最后,根据前面算出的透视强度,对这个基础半径r在长轴和短轴方向进行拉伸,生成椭圆的两个半轴ab以及旋转角度,从而得到一个补偿性的椭圆。当云台控制激光点按照这个补偿椭圆的轨迹运动时,它在物理靶纸上的投影就自然地被“还原”成了一个标准的圆形。

5. 问:你的视觉模块(OpenMV)和运动控制模块(STM32)是如何通信的?通信协议是怎样的?有没有考虑过通信延迟或数据丢失的问题?

答: 我们采用UART串口进行通信,波特率为115200。通信协议是我们自定义的一个简单数据帧格式:帧头(2字节) + 数据(2字节) + 帧尾(2字节)

  • 帧头是固定的0x3C, 0x3B,用于数据同步和起始判断。
  • 数据部分,我将目标点的X坐标和Y坐标(范围都是0-255)分别作为高8位和低8位,合并成一个16位的整数,这样一次就能把坐标信息传完。
  • 帧尾是固定的0x01, 0x01,用于校验数据包的完整性。
  • 控制指令则更简单,STM32直接发送单个字节(如0xA1, 0xA2)给OpenMV来切换模式。

为了应对延迟和丢包,我们做了几点处理:第一,STM32在接收数据时,会严格检查帧头和帧尾,只有当一个完整的数据包被接收后,才会更新目标坐标,这防止了数据的错乱。第二,我在OpenMV端设置了发送间隔(比如30ms),避免了数据发送过于频繁导致STM32处理不过来或串口堵塞。第三,如果STM32在一段时间内没有收到新的坐标数据,它会保持在最后一个有效坐标点的位置,而不是乱动,保证了系统的稳定性。

6. 问:小车在运动时,整个装置会有抖动,这对你的视觉识别会产生什么影响?你是如何克服的?

答: 抖动确实是一个挑战。它主要会造成图像模糊,影响边缘和角点的检测精度。我们主要通过软硬件结合的方式来缓解这个问题:

  • 硬件上,我们尽可能地加固了云台和摄像头的结构,减少物理上的松动。
  • 软件算法上,我做了几点优化:第一,提高摄像头的快门速度,在代码中我通过sensor.set_auto_exposure(False)关闭自动曝光,并手动设置一个较短的曝光时间,这相当于提高了快门速度,可以有效减轻运动模糊。第二,数据平滑,在画圆模式中,我使用了滑动平均滤波。新的目标点坐标并不会直接使用,而是与上一个目标点进行加权平均(target_x = int(target_x * (1-smooth_factor) + last_target_point[0] * smooth_factor)),这使得最终输出的轨迹更加平滑,滤除了高频的抖动。第三,算法容错性,我的目标识别算法本身对轻微的模糊不敏感,因为它依赖的是较大面积的色块(blob)而不是单个像素点。

7. 问:在画圆时,如何确保视觉画圆的周期与小车行驶的周期严格同步?

答: 我们采用的是一种开环同步结合闭环校正的策略。首先,我们通过多次测试,测得小车稳定行驶一圈的平均时间T(比如15.5秒)。这个时间T被预设在OpenMV的代码中(target_circle_time)。当画圆模式启动时,OpenMV会记录一个起始时间circle_start_time。在任意时刻t,理想的目标角度就是 (t / T) * 2 * PI。这就是我们的开环同步基础。但是,为了让轨迹更平滑,避免角度突变,我加入了一个闭环校正环节:当前帧计算出的理想角度并不会直接使用,而是与上一帧的实际角度进行比较,通过一个比例因子(代码中的0.3)来平滑地追赶理想角度。这种方式既保证了整体周期与小车匹配,又使得局部轨迹连续、顺滑,最终实现了小于1/4圈的同步误差要求。

8. 问:如果比赛现场的光照条件发生剧烈变化,比如突然变暗或出现强光反光,你的系统能适应吗?

答:我们充分考虑了光照变化对系统的影响。我的应对策略主要有两点:第一是二值化阈值的选择。我使用的黑色阈值BLACK_THRESHOLD = (0, 20)是一个比较严格的范围,专门针对我们使用的黑色胶带。这意味着只有非常暗的区域才会被识别,这在一定程度上可以抵抗环境光变亮的影响。第二,也是更重要的一点,是关闭自动曝光和自动白平衡。在初始化摄像头时,我会让摄像头先自动适应一下当前环境光,然后就调用sensor.set_auto_gain(False)sensor.set_auto_whitebal(False)sensor.set_auto_exposure(False)将其锁定。这样做的好处是,一旦参数固定,摄像头的成像特性就不会再随光线变化而变化,保证了后续图像处理算法输入的一致性。虽然这可能不是在所有光照下都是“最优”的画面,但它保证了“稳定”,而对于依赖固定阈值的算法来说,稳定比最优更重要。在实际赛场当中很多队伍也因为这个问题无法准确识别对象。

9. 问:你的代码中设置了两种发送延迟STANDARD_SEND_INTERVAL_MSCIRCLE_SEND_INTERVAL_MS,为什么画圆模式的延迟要比瞄准模式更长?

**答:**我将画圆模式的延迟(50ms)设置得比瞄准模式(30ms)更长,是基于对两种模式下系统负载和控制需求的权衡。

  • 瞄准模式下,系统的目标是“快”,需要尽快地将计算出的靶心坐标发送给主控,让云台快速响应。因此,我设置了较短的30ms延迟,以保证高刷新率。
  • 画圆模式下,系统的目标是“稳”和“平滑”。这个模式的计算量更大,因为它涉及到椭圆参数的动态计算和角度的平滑插值。设置更长的延迟(50ms)可以给OpenMV留出更充裕的计算时间,确保每一帧的计算都能完成。更重要的是,过于频繁地发送坐标点反而可能导致云台运动出现微小的“顿挫感”,适当降低刷新率,配合代码中的平滑算法,反而能让最终的画圆轨迹更加流畅和连贯。这是一种在保证实时性的前提下,优先确保轨迹质量的策略。

10. 问:我们注意到你自定义了串口通信协议。如果用《计算机网络:自顶向下方法》中的五层模型来审视,你的协议主要实现了哪几层的功能?

答: 用五层模型来分析,我们的自定义协议主要工作在两个层面:

  • 应用层:我们定义了0xA1为“瞄准模式”、0xA2为“画圆模式”等指令,以及将(x, y)坐标打包成16位整数的格式。这本质上就是定义了通信双方交换的报文(Message)的含义,是应用层的核心功能。
  • 链路层:我们使用了固定的帧头0x3C3B和帧尾0x0101来界定一个数据帧(Frame)的开始和结束。这实现了链路层的成帧功能。同时,通过检查帧头帧尾是否匹配,也提供了一种非常基础的差错检测能力。

我们没有实现网络层、运输层和会话层的功能,因为这是一个简单的点对点通信场景,不存在路由选择(网络层)、端口复用或可靠传输(运输层)等复杂需求。

11. 问:请再详细解释一下你的“透视自适应椭圆生成算法”。你是如何从矩形的边长比例,具体推导出补偿性椭圆的长短轴参数的?这背后的数学或几何假设是什么?

答: 这个算法的核心几何假设是:当一个平面(靶纸)相对相机倾斜时,其在图像上投影的主要形变是沿着深度方向的透视收缩(Foreshortening)。也就是说,离相机远的部分会比离相机近的部分在图像上显得更短。 我的算法实现步骤如下:

  1. 获取形变信息:在找到靶纸轮廓的四个顶点后,我计算出它在图像上四条边的像素长度:top_edge, bottom_edge, left_edge, right_edge
  2. 计算收缩率:我通过 h_ratio = min(top_edge, bottom_edge) / max(top_edge, bottom_edge)v_ratio = min(left_edge, right_edge) / max(left_edge, right_edge) 来量化水平和垂直方向的透视收缩程度。一个理想的正方形投影,这个比率会接近1.0,透视越严重,比率越小。
  3. 计算补偿系数:为了在物理上画出圆,我们需要在图像上“拉伸”那个被透视压缩了的轴。我设计了一个经验性的补偿公式,如 h_ellipse_ratio = 1 + (1 - max(0.1, h_ratio)) * k(其中k是一个调试出来的超参数,比如1.2)。这个公式的含义是,收缩率h_ratio越小(即形变越严重),补偿系数h_ellipse_ratio就越大。
  4. 生成椭圆参数:首先,我根据靶纸的物理尺寸和像素尺寸的比例,计算出目标6cm半径在图像中对应的基础像素半径r。然后,判断哪个方向的形变更严重(比如h_ratio < v_ratio),就在另一个方向上进行补偿。例如,如果水平方向压缩更厉害,那么椭圆的长轴a就等于基础半径r,而短轴b则等于 r / h_ellipse_ratio,并设置相应的旋转角度。

这个算法虽然不是基于严格的光学几何推导,但它抓住并量化了透视形变的核心特征,通过一个补偿模型,在实际测试中非常有效地将物理轨迹还原成了标准圆,满足了赛题的要求。