三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

国赛E题实战:光电传感与PID控制实现运动目标自动追踪

国赛E题实战:光电传感与PID控制实现运动目标自动追踪

1. 从零到一:国赛E题“运动目标控制与自动追踪系统”的实战复盘

去年夏天,我和我的团队一头扎进了全国大学生电子设计竞赛(国电赛)的E题——“运动目标控制与自动追踪系统”。这个题目,乍一看名字挺唬人,什么“运动目标”、“自动追踪”,感觉像是电影里的高科技。但说白了,核心就是让一个“小车”(或者云台)能自己动起来,去追一个会移动的“目标”。这个目标可能是一个发光的LED,一个特定颜色的色块,或者一个二维码。听起来是不是有点像我们小时候玩的“老鹰捉小鸡”的自动版?只不过,老鹰和小鸡都换成了电路板和单片机。

为什么这个题目每年都能吸引无数队伍前赴后继?因为它完美地融合了嵌入式系统、传感器技术、自动控制、图像处理(或光电检测)以及机械结构等多个学科的知识点。它不要求你某个单项技术做到极致,但要求你具备出色的系统集成能力、工程实践能力和临场应变能力。四天三夜,从拿到赛题到封箱上交作品,这不仅仅是一场智力的比拼,更是一场体力、意志力和团队协作的终极考验。今天,我就以一个过来人的身份,把我们当时做E题的完整思路、踩过的坑、以及最后让我们拿到国奖的关键决策,毫无保留地分享出来。无论你是即将参赛的学弟学妹,还是对嵌入式控制感兴趣的爱好者,相信这篇近万字的“脱水干货”都能给你带来实实在在的启发。

2. 破题与顶层设计:你的系统究竟要干什么?

国赛题目往往言简意赅,但字字珠玑。E题描述通常只有一页纸,里面却藏着所有得分点和“坑点”。我们的第一步,不是急着画电路图或写代码,而是像解数学题一样,把题目要求逐字逐句“翻译”成明确的技术指标和系统功能。

2.1 核心任务分解:从模糊描述到清晰指标

以我们那年的题目为例,核心要求通常包括:

  1. 基本要求:系统上电后,能自动搜寻并锁定初始静止的目标(比如一个特定频率闪烁的LED)。锁定后,追踪装置(如摄像头云台)的中心要对准目标中心。这考察的是静态目标捕获能力。
  2. 发挥部分:目标开始按照预定轨迹(如圆形、矩形)运动,追踪装置需持续跟踪,保持对准,并实时显示目标位置坐标。这考察的是动态目标跟踪控制系统响应速度
  3. 创新部分(加分项):目标运动轨迹变为随机或变速,系统需能适应并稳定跟踪。可能还会要求增加测距、无线数据传输、上位机显示等功能。这考察的是算法鲁棒性系统扩展能力

我们的做法是,制作一张“需求-技术-验证”对应表。用Excel或者直接画在草稿纸上,左边列是题目原话,中间列是我们理解的具体技术实现,右边列是计划如何测试验证。例如:

题目要求技术实现解读测试验证方法
“自动搜寻目标”云台需具备二维旋转能力(舵机/步进电机),配合传感器进行全域扫描。上电后,云台应能自动执行扫描程序,并在找到目标后停止。
“锁定目标”传感器(摄像头/光电管)输出信号需达到稳定阈值,并认为目标位于视场中心。用示波器或串口监视传感器信号,当信号稳定且云台停止运动时,视为锁定。
“跟踪运动目标”需实现闭环控制:传感器反馈目标位置偏差 -> 控制器计算 -> 驱动电机消除偏差。让目标沿固定轨迹运动,观察云台跟随是否平滑,用上位机记录轨迹曲线是否吻合。
“显示坐标”需有显示模块(OLED/LCD)或通过串口发送到电脑上位机。目视检查屏幕显示或上位机软件接收到的坐标值是否合理、连续。

这张表会成为我们后续所有工作的“宪法”,任何设计决策都要回头看看是否满足了表中的某项要求。很多队伍失败的第一个原因,就是没有吃透题目,想当然地去做,结果做出来的东西和评分标准格格不入。

2.2 技术路线选型:摄像头方案 vs 纯传感器方案

这是E题最核心的决策点,直接决定了整个系统的复杂度、成本和稳定性。

方案一:摄像头图像处理方案

  • 核心:使用OpenMV、K210、树莓派(配合摄像头)或STM32F4/F7系列(配合OV系列摄像头)作为主控,通过图像算法识别目标。
  • 优点
    • 信息丰富:能获取目标的二维坐标、形状、大小、颜色等多种信息,理论上识别更准确。
    • 灵活性高:通过修改代码可以适应不同特征的目标(颜色、形状、图案),甚至实现多目标跟踪。
    • “看起来”更高级:容易做出炫酷的上位机图像显示界面,视觉冲击力强。
  • 缺点与巨坑
    • 处理速度:图像处理非常消耗算力。在低端MCU上,帧率可能很低(如OpenMV处理复杂算法时可能只有10fps左右),导致系统响应迟钝,无法跟踪快速目标。
    • 光照影响:赛场光线不可控。窗户边的阳光、日光灯频闪、其他队伍的灯光干扰,都可能让颜色识别或阈值分割完全失效。这是我们亲眼所见最多队伍翻车的地方。
    • 算法复杂度:你需要编写或调试图像处理流程(找色块、轮廓检测、中心点计算),并处理好图像坐标到云台舵机角度的映射关系,调试周期长。
    • 稳定性:程序复杂,内存占用大,容易死机或出现偶发性识别错误。

方案二:光电传感器阵列方案

  • 核心:使用光敏电阻、光电二极管、激光接收管等模拟传感器,或者集成度更高的灰度传感器、红外循迹模块阵列,来感知目标光源的位置。
  • 优点
    • 响应速度极快:模拟电路或数字传感器的响应是微秒级的,几乎没有延迟。
    • 抗干扰能力强:可以针对特定频率(如果目标LED是调制的)进行选频放大,极大抑制环境光干扰。电路做好了,稳定性极高。
    • 控制逻辑简单:传感器输出直接就是电压或数字信号,反映目标偏离中心的方向和程度,可以直接用于PID控制计算,系统简洁可靠。
    • 成本低,功耗低
  • 缺点
    • 信息量少:通常只能得到一维或二维的偏差量,无法知道目标的绝对坐标或复杂特征。
    • 依赖目标特性:通常要求目标是主动发光的、特定频率的LED。如果题目目标是被动反射的普通物体,此方案可能不适用。

我们的关键决策与心得:经过激烈讨论和前期实验,我们毅然选择了方案二——光电传感器阵列。理由非常功利且直接:国赛是在体育馆里进行,现场光照条件复杂,稳定压倒一切。我们赌题目中的“运动目标”会是一个易于用光电方式检测的主动光源(后来证实果然如此)。我们用了4个光电二极管配合透镜,组成一个“十”字形探测阵列,后面接模拟电路进行放大、滤波和比较。整个检测电路的反应时间在毫秒级,为后续的高速跟踪打下了坚实基础。这个选择,让我们避开了最不可控的“光照”变量,把问题从复杂的图像处理拉回到了我们更擅长的电路设计和控制算法上。给后来者的忠告:除非你对图像处理有极深的功底和充分的抗干扰测试,否则在有限的比赛时间内,优先考虑最稳定、最直接的方案。

3. 硬件系统的骨架:稳定可靠的电路与机械设计

硬件是系统的身体,软件是灵魂。一个颤颤巍巍的云台或者噪声巨大的传感器信号,再优秀的算法也无力回天。

3.1 传感器模块:如何把“看见”变成“电信号”

我们采用四象限光电探测器方案。核心思想是:用一个遮光筒将视场限制在一个小圆锥内,四个光电二极管(A, B, C, D)呈“十”字排列在遮光筒底部。当目标光源出现在视野中时,它会照亮一个或几个二极管。

  • 电路设计要点
    1. 选型:光电二极管要选择响应速度快、暗电流小的型号。我们用的是BPW34。
    2. I-V转换:光电二极管输出的是微弱的电流信号(uA级)。第一级运放必须配置成跨阻放大器(Transimpedance Amplifier),将电流转换为电压。这里的反馈电阻和电容取值至关重要,决定了带宽和噪声。
    3. 放大与滤波:第二级可调增益放大,将信号放大到MCU ADC可采集的范围(如0-3.3V)。必须加入带通滤波!如果目标LED是1kHz方波调制,我们就设计一个中心频率在1kHz的带通滤波器。这能滤除几乎所有的环境光(直流和低频)以及其他队伍可能用的不同频率光源(高频)干扰。
    4. 峰值检波或有效值转换:经过滤波放大的交流信号,需要转换成直流电压才能被ADC采集。我们用了精密整流加低通滤波构成的峰值检波电路。也可以使用AD736这类真有效值芯片。
    5. 信号合成:得到四个通道的直流电压Va, Vb, Vc, Vd后,如何计算目标偏离中心的位置(ΔX, ΔY)?
      • 水平偏差 ΔX = (Va + Vd) - (Vb + Vc)。当目标在左边时,(Va+Vd) > (Vb+Vc),ΔX为正。
      • 垂直偏差 ΔY = (Va + Vb) - (Vc + Vd)。当目标在上边时,(Va+Vb) > (Vc+Vd),ΔY为正。
      • 这个公式的本质是差分测量,能有效抵消光源强度变化带来的影响。即使光源变暗,只要它偏离中心,差值依然存在。

3.2 执行机构:云台驱动与选型

云台需要两个自由度:水平(Pan)和垂直(Tilt)。常用方案是舵机。

  • 舵机选型不要用廉价的9g塑料舵机!它们扭矩小、齿轮易打滑、存在死区、精度差。我们选用的是数字金属齿轮舵机,如MG996R。虽然贵一点,但扭矩大(10kg/cm以上),回中精度高,响应快。
  • 机械结构:自己用亚克力或铝型材搭建云台时,重心和刚度是生命线。摄像头或传感器模块必须牢固安装,整体重心尽量靠近两个舵机的旋转轴交汇处。否则,舵机负载不均,会产生抖动甚至无法带动。我们用了3D打印了一个轻量化且结构坚固的支架,并用螺丝严格固定。
  • 供电隔离:舵机启动瞬间电流很大(可达2A),会在电源线上产生毛刺,严重干扰敏感的模拟电路和单片机。必须将舵机电源和控制系统电源隔离!我们的做法是:一块大容量锂电池(如2S/3S锂电)单独给舵机供电。单片机、传感器、运放等由另一块稳压后的电池或通过DC-DC模块从总电源隔离出来。两地之间仅共用信号线(PWM)。

3.3 主控与电路板:把一切连起来

  • 主控MCU:STM32F103C8T6(蓝桥杯板)或STM32F407。性能完全足够。F103的ADC和定时器做PID控制绰绰有余。关键是要熟悉其外设,特别是定时器产生PWM驱动舵机,ADC多通道扫描采集传感器信号,以及串口与上位机通信。
  • PCB还是洞洞板强烈建议绘制PCB!四天时间看似紧张,但用嘉立创等平台打样,通常24-48小时就能拿到。洞洞板焊接模拟电路,特别是多路运放,极易引入噪声、产生虚焊,调试起来是噩梦。我们在第一天就确定了原理图,当晚就发出去打样,第三天早上拿到焊好,整个系统的稳定性有了质的飞跃。这是时间投资,回报极高。
  • 电源管理:模拟部分(运放)最好使用±5V或±12V供电以获得更大动态范围,数字部分3.3V。使用LDO(如AMS1117-3.3)或低噪声DC-DC模块。每个芯片的电源引脚附近都必须放置一个0.1uF和一个10uF的电容进行去耦。

4. 软件算法的灵魂:从数据到精准控制

硬件提供了干净、快速的偏差信号(ΔX, ΔY),软件的任务就是让云台动起来,让这个偏差趋于零。

4.1 核心控制算法:PID的深入理解与整定

我们采用了最经典也最有效的位置式PID算法。公式大家都知道:Output = Kp * error + Ki * integral + Kd * derivative。但真正用好不容易。

  • 误差(Error)的获取:这就是我们传感器计算出的ΔX和ΔY。注意,这里存在一个映射关系:ΔX的电压值对应目标在水平方向偏离了多少角度。我们需要通过实验标定。例如,让目标在云台正前方左右缓慢移动,记录下ΔX从正最大到负最大变化时,目标实际移动的物理角度。从而建立一个(电压差 -> 角度偏差)的近似线性关系。这个关系系数会直接乘到Kp等参数上。
  • 积分项(Integral)的陷阱与抗饱和:积分项能消除静态误差。但如果目标快速运动或云台暂时丢失目标(误差一直很大),积分项会累积到一个巨大的值(积分饱和),当目标再次出现时,系统会产生严重的超调甚至震荡。必须实现积分抗饱和。我们采用的方法:当输出值达到硬件限幅(如PWM占空比上限)时,就停止积分项的累积。或者更精细一点,只累积那些与输出方向相反的误差。
  • 微分项(Derivative)与滤波:微分项能预测变化趋势,提高响应速度。但它对噪声极度敏感!传感器信号哪怕有一点毛刺,经过微分也会被放大成尖峰。必须在软件中对误差进行低通滤波后再计算微分,或者使用“不完全微分”的PID变种。
  • 参数整定:不是玄学,是科学实验
    1. 先Kp, 再Kd, 最后Ki。这是黄金法则。
    2. 将Ki和Kd设为0。逐渐增大Kp,直到云台开始出现等幅振荡(临界振荡)。记下此时的Kp为Ku,振荡周期为Tu。
    3. 根据齐格勒-尼科尔斯(Ziegler-Nichols)经验公式设置初始参数:
      • 经典PID: Kp = 0.6 * Ku, Ki = 2 * Kp / Tu, Kd = Kp * Tu / 8。
      • 保守型(我们采用的): Kp = 0.33 * Ku, Ki = 2 * Kp / Tu, Kd = Kp * Tu / 3。
    4. 在这个初始参数附近微调。观察云台跟踪一个匀速运动目标时的响应曲线(可以通过串口发送数据到电脑用Python matplotlib画图)。理想状态是:快速响应,小幅超调,迅速稳定在误差带内。

4.2 程序架构与多任务调度

四天三夜写出的代码,不能是“面条代码”。一个清晰的架构能让你在最后关头修改功能时,不至于牵一发而动全身。

  • 时间片轮询:我们没有用复杂的RTOS,而是采用了简单可靠的时间片轮询。在SysTick中断(1ms触发一次)中设置多个软件定时器标志位。
    // 示例代码结构 volatile uint8_t flag_10ms = 0, flag_20ms = 0, flag_100ms = 0; void SysTick_Handler(void) { static uint16_t tick = 0; tick++; if(tick % 10 == 0) flag_10ms = 1; // 10ms任务 if(tick % 20 == 0) flag_20ms = 1; // 20ms任务 if(tick % 100 == 0){ // 100ms任务 flag_100ms = 1; tick = 0; } } int main(void) { // 初始化... while(1) { if(flag_10ms) { flag_10ms = 0; Sensor_Read(); // 读取传感器数据 (10ms) PID_Calculate(); // 计算PID输出 (10ms) } if(flag_20ms) { flag_20ms = 0; Servo_Update(); // 更新舵机PWM (20ms) } if(flag_100ms) { flag_100ms = 0; UART_SendData(); // 向上位机发送数据 (100ms) OLED_Display(); // 刷新显示 (100ms) } // 其他非实时任务... } }
    • 10ms任务:读取ADC(传感器信号),执行PID计算。这是控制环的核心,周期最短,决定了系统带宽。
    • 20ms任务:更新舵机PWM信号。舵机的控制周期通常在20ms左右,太快了没必要。
    • 100ms任务:刷新OLED显示、向上位机发送调试数据。这些任务实时性要求不高。
  • 状态机设计:系统工作流程用状态机来管理,非常清晰。
    typedef enum { SYS_INIT, SCANNING, // 扫描目标 LOCKED, // 锁定目标 TRACKING, // 跟踪目标 LOST // 目标丢失 } SysState_t;
    while(1)主循环中,根据当前状态执行不同的函数,并在满足条件时进行状态转移。例如,在SCANNING状态,云台匀速旋转,一旦传感器信号超过阈值,就转入LOCKED状态。

5. 调试、测试与赛场实战:细节决定成败

系统搭起来能跑只是第一步,如何让它跑得稳、跑得准,才是拿高分的关键。

5.1 分模块调试:隔离问题,逐个击破

  1. 传感器单独测试:用信号发生器产生一个1kHz的正弦波,模拟光电二极管放大后的信号,输入到我们的滤波和检波电路,用示波器看输出是否平滑的直流,并且幅值随输入信号幅值线性变化。然后用手电筒(最好也用1kHz调制)在传感器前移动,看输出变化是否灵敏、连续。
  2. 舵机单独测试:写一个简单的程序,让舵机在0°和180°之间往复运动,观察其运动是否平滑,有无异响,回中是否准确。用手机慢动作拍摄,检查是否有抖动。
  3. PID开环测试:先将PID输出断开,手动给一个阶跃的“误差”信号(比如通过按键模拟),用串口打印出此时PID计算出的输出值,观察比例、积分、微分三项的变化是否符合理论预期。
  4. 闭环联调:这是最激动人心也最痛苦的阶段。先让目标静止,调整Kp让云台能快速对准并稳定住,没有明显抖动。然后让人手持目标缓慢移动,观察云台跟随是否平滑。这里一定要用上位机软件(如SerialPlot、自己用Python PyQt5写的)实时绘制误差曲线和输出曲线,眼睛看云台动作是感性的,看曲线才是理性的分析依据。

5.2 抗干扰与鲁棒性提升

  • 软件滤波:除了硬件带通滤波,ADC采集后还要进行软件滤波。我们用了一阶互补滤波来处理ΔX和ΔY信号,平滑掉偶尔的尖峰。
    float filtered_value = 0.9 * filtered_value + 0.1 * raw_adc_value; // 一阶低通
  • 目标丢失处理:在跟踪过程中,可能会因为目标移动太快、暂时被遮挡等原因导致传感器信号消失(四个通道电压都很低)。程序必须能检测到这种情况(LOST状态),并触发一个重新搜索策略。我们的策略是:记录丢失前一刻的目标运动方向,让云台向那个方向加速旋转一小段,同时缩小搜索范围。如果几秒内还没找到,再扩大为全局扫描。这个策略让我们在测试中成功应对了目标的突然加速和短时遮挡。
  • 参数自适应:进阶玩法。我们尝试了一个简单的方案:根据误差的大小动态调整PID参数。当误差很大时(目标刚出现或丢失后重新找到),使用一组更激进的参数(Kp大,Kd小)快速接近目标;当误差很小时,使用一组更保守的参数(Kp小,Ki, Kd适当)精细调整,避免震荡。这有点像“模糊控制”的思想,效果不错。

5.3 赛场上的最后48小时

  • 封装与测试清单:作品封箱前,列一个详细的测试清单,每项功能都让队友操作,自己记录结果。包括:上电自检、扫描锁定时间、静态对准精度、匀速圆周跟踪误差、矩形轨迹拐角跟踪性能、目标丢失恢复时间等等。确保万无一失。
  • 备用方案:我们准备了两套完全相同的传感器模块和一套备用舵机。主控程序烧录在两块不同的核心板上。永远要有Plan B
  • 心态调整:最后一天,核心代码和参数就不要再做大改了,只做微调。把时间花在整理报告、练习答辩、检查机械结构螺丝是否紧固、电池电量是否充足这些“琐事”上。保持冷静,相信你们之前上百个小时的调试。

6. 总结与延伸:E题带来的远不止一个奖项

回过头看,国赛E题就像是一个微缩的工程项目。它强迫你在极短时间内,完成从需求分析、方案论证、硬件设计、软件编程、调试测试到最终交付的全流程。我们选择的“光电阵列+经典PID”路线,可能不是最炫酷的,但却是最扎实、最可靠的。它让我们深刻理解了**“简单即是美”“稳定高于一切”**的工程哲学。

比赛结束后,我们在这个系统上做了很多延伸。比如,将传感器换成了分辨率更高的PSD(位置敏感探测器),实现了亚毫米级的定位精度;尝试用MPU6050做云台自身的姿态补偿;甚至把算法移植到了带浮点单元的STM32F4上,实现了更复杂的自适应PID。这些都是在比赛核心框架上生长出来的新技能。

对于准备参赛的朋友,我的建议是:尽早组队,明确分工(硬件、软件、机械、报告),找一个往年的经典赛题(如2017年的“滚球控制系统”),从头到尾做一遍。在这个过程中,你们会遇到本文提到的所有问题,并学会如何解决它们。当你们真正比赛时,就会发现,题目虽然新颖,但底层技术和解决问题的方法论是相通的。

最后,分享一个让我们在测试阶段省下大量时间的小工具:一个用Python和PyQt5写的简易上位机。它通过串口接收单片机发来的数据(实时误差、PID输出、舵机角度等),并实时绘制成曲线图。同时,还能发送一些简单的指令(如修改PID参数)给下位机。这个工具让我们摆脱了反复修改代码、编译、下载、观察的循环,调试效率提升了不止十倍。它的核心代码并不复杂,网上有很多教程,强烈建议每一支队伍都花点时间准备一个。

国赛之旅,痛并快乐着。那些在实验室通宵调试、争吵又和解、看到云台终于平稳跟踪目标时的欢呼,都成为了我们大学生活中最宝贵的记忆和技术生涯最坚实的起点。希望这篇长文,能为你点亮一点前行的光。祝你成功!

← 返回列表