尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

循迹小车从检测到控制:红外对管、差速PWM与PID调参全解析

发布时间:2026/9/19 20:11:17

资讯中心
01
ARTICLE

循迹小车从检测到控制:红外对管、差速PWM与PID调参全解析

循迹小车从检测到控制:红外对管、差速PWM与PID调参全解析
简介围绕P89V51RB2单片机的循迹小车实验报告是一份面向电气工程与自动化学院学生的课程设计实践资料完整展示了从系统总体设计、硬件电路搭建到软件驱动编写与整机调试的全过程。报告以三轮小车为平台讲解红外探测法识别黑线、PWM控制电机调速、光电编码器测速以及超声波测距与LCD1602显示等关键模块尤其适合单片机入门者参考电路设计与代码实现思路。压缩包内为1个PDF文件整体大小约1.53MB内容包含绪论、系统总体设计、硬件设计、软件设计、系统调试与结束语等章节目录结构清晰。已有219人学习下载可作为嵌入式技术实践、智能车竞赛备赛或相关课程设计的参考资料。1. 拿到“循迹小车实验报告.pdf”这个标题时先别急着补文档很多问题出在车上实验报告一般最后才写但循迹小车这个项目真正卡的从来不是排版而是车为什么跑不稳。红外对管装好了、电机能转、代码烧进去了结果小车在直道上画龙过一个锐角弯直接冲出去。这份报告的核心是把“循迹”这件事讲清楚传感器怎么判断黑线、单片机怎么决定左右轮转速差、参数为什么这么设。本文面向正在做单片机课程设计、蓝桥杯备赛或刚接触 STC/STM32 小车控制的人用可复现的代码和参数把循迹的检测、转向、调速三个环节走一遍最后给出整车验证顺序和最容易翻车的边界情况。报告内容可以之后再填但车要先能跑。2. 循迹小车怎么“看见”黑线红外对管与检测判据2.1 两种常见循迹方案的读数差异循迹小车的“眼睛”通常有两大类。第一类是红外对管由红外发射管和接收管组成发射管持续发出红外光接收管接收反射回来的光强。地面是白色时反射强接收管导通输出低电平地面是黑色时吸收红外光反射弱输出高电平。这种方案输出的是数字量单片机只要读 I/O 口的电平即可成本低、代码简单是 51 单片机循迹小车最常见的配置。第二类是灰度传感器原理类似但输出模拟量能区分不同灰度的颜色。它带来的好处是可以做“阈值自适应”在光线变化的环境里通过算法自动调整判定标准但需要单片机有 ADC。STC89C52 这类基础 51 芯片不带 ADC要用 ADC 得外挂芯片或换 STC12 系列、STM32。做课设的话我一般建议先上红外对管把逻辑跑通再考虑灰度方案。对比项红外对管数字量灰度传感器模拟量输出信号高/低电平0~5V 模拟电压接线复杂度低只需 I/O 口需要 ADC 引脚环境光影响大需调电位器可通过软件校准适合芯片STC89C52、STM32STM32、STC12 系列单路成本约 1~2 元约 3~8 元判定逻辑电平判断电压阈值比较2.2 STC89C52 读取两路红外对管的代码与电平逻辑下面是一段用 STC89C52属于 STC 单片机家族驱动两路红外对管的最小代码。硬件接线约定左传感器接 P1.0右传感器接 P1.1传感器模块上的 VCC 接 5VGND 接地OUT 接 I/O 口。绝大多数市售红外对管模块自带比较器如 LM393输出已经是数字电平不需要再加 ADC。#include REGX52.H sbit LeftSensor P1^0; // 左传感器输出引脚 sbit RightSensor P1^1; // 右传感器输出引脚 // 电机控制引脚 sbit LeftMotorDir P2^0; sbit RightMotorDir P2^1; void Motor_Stop(void) { LeftMotorDir 0; RightMotorDir 0; } unsigned char Read_Sensor_State(void) { unsigned char state 0; // 状态位定义bit1 left, bit0 right // 1 表示检测到黑线输出高电平0 表示白地 if (LeftSensor 1) state | 0x02; if (RightSensor 1) state | 0x01; return state; } void main(void) { unsigned char sensor; while (1) { sensor Read_Sensor_State(); switch (sensor) { case 0x00: Motor_Stop(); break; // 两路都在白地停车或直行 case 0x02: // 只有左边压线 - 右转 LeftMotorDir 1; RightMotorDir 0; break; case 0x01: // 只有右边压线 - 左转 LeftMotorDir 0; RightMotorDir 1; break; case 0x03: // 两路都压线 - 直行 LeftMotorDir 1; RightMotorDir 1; break; } } }这里先解释状态位的判定逻辑。红外对管模块在检测到黑线时输出高电平所以if (LeftSensor 1)意味着左传感器正压在黑线上。0x02表示左黑右白车应该往右打方向0x01反之0x03两路同时压线说明车头正对黑线直行即可。注意 I/O 口若没有内部上拉需要在模块 OUT 和 VCC 之间接一个 10k 上拉电阻否则电平会漂浮导致误判。这段代码的价值在于把“循迹”抽象成了状态查表之后无论加几路传感器都只是扩展状态位而已。缺点是左右轮转速一样转向幅度不可调弯道稍急就会冲出赛道所以实际项目中要引入占空比控制。2.3 传感器布局间距与高度对循迹的影响布局是实验报告里最容易被忽略的一节。两个传感器的间距如果只有 2~3cm小车在直道上轻微晃动就会导致两路同时脱离黑线进入未知状态如果间距拉到 8cm 以上遇到小角度弯道时可能两边都没压到线车就“瞎”了。我一般把间距设为黑线宽度的 1.2~1.5 倍标准 PVC 胶带宽度约 2cm传感器间距取 3~4cm 比较稳妥。传感器离地高度同样关键。模块底部的红外发射管和接收管是斜对射设计离地太近小于 0.5cm时接收管只能收到很少的反射光白地和黑线的电平差变小离地太远大于 3cm时红外光扩散黑线也反射部分红外光同样难区分。2cm 左右是最佳工作距离这个可以直接写进实验报告的“实验条件”部分。调传感器上的蓝色电位器可以改变灵敏度阈值顺时针旋转让判定更敏感但过度敏感会把细微污渍也当成黑线此消彼长需要实测。3. 直道加速弯道减速差速控制的 PWM 调参3.1 两轮差速的基本公式与转向策略调整只靠电平切换转向是“开关量控制”车在直道上左右摇摆过弯时内侧轮和外侧轮转速相同转弯半径完全由摩擦力决定很难跑出稳定的轨迹。要让循迹小车跑得平稳需要用 PWM 独立控制左右电机的占空比实现差速。差速的核心公式很简单直线时左右占空比相等转弯时一侧减速、另一侧保持或略微升速转速差越大转弯半径越小。转向策略分两步设计。第一步是“主从轮”以弯道内侧轮为减速对象外侧轮保持直行速度。第二步是“速度补偿”如果发现外侧轮在急弯时速度下降电池电压跌落导致就把它稍微提高。开环阶段不需要编码器直接给固定占空比就能跑。下面用 STM32F103C8T6 的定时器输出 PWM 来演示因为 STM32 循迹小车普及率很高且可以用定时器同时输出两路频率相同的 PWM。3.2 用 STM32 输出 PWM 实现差速的最小配置// 使用 TIM2 的 CH1(PA0) 和 CH2(PA1) 输出 PWM频率 1kHz #include stm32f10x.h void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler 72 - 1; // 72MHz/72 1MHz TIM_TimeBaseStructure.TIM_Period 1000 - 1; // 1MHz/1000 1kHz TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // 两路 PWM 模式 1初始占空比 0 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM2, ENABLE); TIM_Cmd(TIM2, ENABLE); } // 左轮占空比 left_duty右轮占空比 right_duty范围 0~999 void Motor_SetDuty(uint16_t left_duty, uint16_t right_duty) { TIM_SetCompare1(TIM2, left_duty); TIM_SetCompare2(TIM2, right_duty); }这段配置的逻辑是先将 72MHz 系统时钟分频到 1MHz再设置自动重装载值为 1000最终得到 1kHz 的 PWM 频率。TIM_SetCompare1设置的是占空比数值999 对应 99.9% 占空比0 对应停机。PWM 频率选择 1kHz 是折中方案——电机驱动芯片如 L298N、TB6612响应这个频率没问题也不容易产生人耳可闻的啸叫。参数设置上有一个关键点占空比不能直接等于目标速度。以 TB6612 为例2.5V 的电机驱动电压大约对应 50% 占空比但电机存在死区电压占空比低于某个值约 20%时电机根本不会转。所以实际控制时要给一个基础占空比比如直线巡航是 55%转弯时内侧降到 30%外侧提高到 65%这样内外轮的转速差就能形成明显的转向力。3.3 差速转向参数表与实测调整顺序道路情况左轮占空比右轮占空比说明直道550550两轮等速微调消除跑偏左急弯380600左轮减速右轮略加速右急弯600380右轮减速左轮略加速S 弯连续470530动态切换幅度不宜过大十字路口550550冲过去不做转向动作调整顺序有讲究。先把直道占空比两边调到跑得直此时如果发现车总往左偏说明左轮摩擦力大或电机负载高将左轮占空比加 20~30 即可。然后再调急弯参数观察入弯时车头是否冲出白线冲出就是内侧减速不够狠或打晚了。这里我建议用“先慢后快”的调法把直线占空比降到 400等急弯能稳定过再逐步升回 550。3.4 编码器测速在这个阶段可以先不做热词里频繁出现“单片机小车测速”“编码器测速”但对循迹小车来说编码器不是启动阶段必须的。开环 PWM 控制的问题是电池电压随放电下降同样占空比下电机会越跑越慢弯道参数会逐渐失效。如果实验报告需要有“速度闭环”这一节可以用霍尔编码器加定时器输入捕获来测轮速然后用 PID 把速度稳住。但这是第二个迭代版本的内容先把开环跑通、拿到一组稳定的占空比参数再谈闭环代码复杂度会低很多。4. 跑得稳不等于跑得快阈值自适应与转向收敛4.1 环境光变化下的动态阈值算法红外对管最怕的不是黑线不明显而是环境光变化。早上十点的阳光和傍晚五点的夕阳会让同一块白地的反射电平差 0.5V 以上。固定电压阈值只在一段时间内有效拉上窗帘和拉开窗帘完全是两种表现。常见的做法是用滑动窗口动态求阈值单片机定期采样红外对管的模拟量如果是 STM32 可以直接用 ADC维护最近 N 个采样值的数组取最大值和最小值的中间值作为当前判定阈值。#define SAMPLE_SIZE 10 uint16_t samples[SAMPLE_SIZE]; uint8_t sample_index 0; // 更新滑动窗口并返回当前阈值 uint16_t update_threshold(uint16_t current_value) { uint16_t max_val 0, min_val 0xFFFF; uint8_t i; samples[sample_index] current_value; sample_index (sample_index 1) % SAMPLE_SIZE; for (i 0; i SAMPLE_SIZE; i) { if (samples[i] max_val) max_val samples[i]; if (samples[i] min_val) min_val samples[i]; } // 阈值取中间值也可用 (maxmin)/2 return (max_val min_val) / 2; }这段代码的计算开销很小10 个元素的扫描在 72MHz 主频下不超过几微秒不会影响控制周期。这里需要说明的是窗口大小的选择窗口太小比如 3阈值会跟着瞬时噪声乱跳窗口太大比如 50光线变化时阈值响应太慢车已经跑偏了还没更新过来。10 到 20 是常用区间。滑窗法不要求传感器输出模拟量数字量也可以做——把多次采样的 0/1 结果累加超过一半算黑线相当于一个软件滤波器不过对光线变化的适应能力不如模拟量阈值方案。4.2 积分分离 PID 用于转向修正量开关量转向的局限是“要么全转、要么不转”在直道上容易出现小幅度摇摆。引入 PID 后传感器的状态不再只是“压线/没压线”而是一个误差量。比如五路传感器把每个传感器的位置加权成线形误差值左侧传感器权重 -2左侧中路 -1中间为 0右侧中路 1右侧 2误差越大说明偏离黑线越远。控制目标是让误差收敛到 0输出量是左右轮的占空比修正量。循迹小车建议用增量式 PID 或积分分离 PID因为普通 PID 的积分项容易让车在过弯时产生严重的滞后超调出弯后还在继续加大转向。积分分离的意思很简单偏差大于某个阈值时关闭积分项防止积分饱和偏差小的时候再恢复积分用来消除稳差。float kp 0.8f, ki 0.08f, kd 0.6f; float integral, last_error, output; float pid_calc(float error, float threshold) { float p_term, i_term, d_term; p_term kp * error; if (error threshold || error -threshold) { // 偏差过大关闭积分防止过冲 integral 0; i_term 0; } else { integral error; if (integral 50) integral 50; // 积分限幅 if (integral -50) integral -50; i_term ki * integral; } d_term kd * (error - last_error); last_error error; output p_term i_term d_term; if (output 300) output 300; // 输出限幅 if (output -300) output -300; return output; } // 假设基础占空比 base_duty 550误差左偏为负右偏为正 // left_duty base_duty - output; // right_duty base_duty output;上面的代码没有使用固定的 sin 或查表函数所有计算都是浮点运算在 STM32F103 上没有任何压力。但如果用的是 STC89C52浮点 PID 计算会拖慢主循环此时需要改成定点数或者把 kp、ki、kd 放大 100 倍用整数算。参数说明kp决定转向力度直道上太小会让车“找不到线”太大又会在两个传感器之间来回弹跳kd提供阻尼抑制过冲ki在循迹中通常只起微调作用取kp的十分之一到二十分之一。4.3 参数调整顺序先 P 再 D 后 IPID 调参有个直接的顺序我一般按这个流程做能节省不少时间。先把ki和kd设为 0只保留kp从小值慢慢加大观察小车过弯时能不能跟上黑线。当车在线两侧出现规律性振荡时说明kp已到边界此时加入kd消除振荡。最后再加ki并且只在小车直道上存在固定偏移时使用。现象调整方向直道上左右连续摆动增大 kd微调 kp过弯转不过去冲出线增大 kp过弯之后仍偏向一侧检查机械结构增大 ki过弯猛烈抖动减小输出限幅动态阈值下仍误判检查采样窗口长度和传感器高度一个容易被忽略的细节是 PID 的控制周期。PWM 输出是 1kHz但 PID 计算不需要那么频繁10ms 到 20ms 计算一次就够了。如果每毫秒都算微小的误差变化会被微分项放大产生高频抖动。用定时器产生 100Hz 中断在中断里做 PID 计算主循环只负责显示或通信结构会清爽很多。5. 整车验证顺序与最有用的一个小技巧5.1 从上电到连续过弯的测试顺序拿到一套新组装的循迹小车不要急着上赛道。按下面的顺序逐级验证每一级过了再进下一级能在十分钟内定位到具体问题。传感器方向检测用白纸和黑胶带分别放在传感器下方确认输出电平与预期一致若不正确交换模块接线或改代码中的极性判断。电机方向检测左右轮分别短时给 30% 占空比确认两个轮子的旋转方向都向前否则交换电机线之后不要动它。直道测试在 1 米长的黑线直道上运行记录偏移方向和摆动幅度只调直道基础占空比。单弯测试先跑 30 度缓弯再跑 60 度中弯最后加 90 度直角弯每个弯道类型单独记录过弯成功率。连续 S 弯测试把 S 弯间距设置为小车长度的 1.5 倍检验转向收敛性这步是对 PID 参数的最终验收。5.2 一个提高过弯成功率的实用技巧弯前减速我发现很多小车不是转向能力不够而是速度太快导致传感器还没看到线就已经冲过去了。简单的办法是给传感器加“前瞻”在车头前方 3~5cm 处再加一排传感器提前感知弯道方向单片机在处理时优先响应前瞻传感器的数据相当于提前一个车身距离决定转向策略。这样做的机械成本只是一两个传感器模块代码上只需在读传感器时多读几个 I/O 口再用一个简单的优先级判断即可。有一个在蓝桥杯单片机和 STC 单片机项目中很实用的做法在循迹主循环里加一个“弯道检测”逻辑当两路辅助传感器中的任何一路压线时就把基础占空比从 550 降到 400同时加大转向修正量。这本质上是给转向控制加了一个前馈路径比单纯调 PID 更直接也让整车的速度上限提升不少。5.3 十字路口、起跑线和电池电压这三个边界项目收尾前还需要处理几个容易翻车的边界场景。十字路口的特征是底部四路传感器同时压线此时如果执行“全部压线直行”的逻辑会直接冲过路口但在需要计数转弯的场景下会出问题。常见做法是检测到四路同时为 1 时启动计数器根据后续状态变化决定是直行还是转固定角度。起跑线检测则简单一些只需加一个独立的光电开关或霍尔传感器在车轮压到起跑线时触发停车信号即可。电池电压的影响用经验值处理4 节 5 号电池供电时新电池电压约 6.2V占空比 500 对应的转速在电压降到 5.4V 时会下降约 15%如果实验中换过电池参数要重新校验。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。