1. 五路灰度循迹模块的实战配置
五路灰度传感器是双车协同系统中最重要的环境感知部件,它的工作原理就像人的手指触摸不同材质表面。当红外发射管发出的光线照射到黑色赛道时,大部分光线被吸收,接收管输出高电平;遇到白色区域则反射强烈,输出低电平。我们团队实测发现,淘宝常见的TCRT5000模块在10mm检测高度时,对3cm宽黑线的识别成功率能达到98%。
硬件连接上需要注意两个关键点:一是供电电压必须稳定在3.3V-5V之间,我们遇到过因电压波动导致的误检测问题;二是安装高度建议控制在8-15mm,太高会降低灵敏度,太低容易刮擦赛道。具体初始化代码中,GPIO_Mode_IPU(上拉输入)模式比浮空输入更稳定:
c复制void GRAY_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 五路传感器分别连接PA4-PA8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
实际巡线时推荐采用加权算法处理五路数据。我们将传感器从左到右编号为0-4,定义权重数组int weights[] = {-20, -10, 0, 10, 20}。当中间传感器(2号)检测到黑线时,直接前进;当0号或4号触发时,进行急转弯修正。这种方案在1m/s速度下仍能保持稳定循迹。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 蓝牙主从机通信架构设计
双车协同的核心在于可靠的无线通信。经过对比测试,HC-05蓝牙模块在10米距离内传输延迟可以控制在50ms以内,完全满足比赛要求。主从机配置有个坑要注意:必须先用AT命令将波特率统一设为9600,我们最初用的115200导致数据包丢失严重。
通信协议设计采用3字节帧结构:
- 字节1:固定帧头0xAA
- 字节2:指令类型(0x01速度指令/0x02状态查询)
- 字节3:数据内容
- 字节4:校验和(前三个字节异或值)
发送端关键代码示例:
c复制void Bluetooth_Send(u8 cmd, u8 data) {
u8 buffer[4];
buffer[0] = 0xAA; // 帧头
buffer[1] = cmd;
buffer[2] = data;
buffer[3] = buffer[0]^buffer[1]^buffer[2]; // 校验
USART_SendData(USART2, buffer, 4);
}
接收端建议使用环形缓冲区+状态机解析。我们遇到过数据粘包问题,后来加入50ms的接收超时机制:如果超过时限未收到完整帧,就清空缓冲区。实测表明这种方式能有效处理20%以内的数据丢包。
3. 特殊路况识别算法
起始线识别采用"三中灰度全白"的判定条件。具体实现时要注意防抖处理——连续5次检测到中间三个传感器都输出0才确认。我们在调试中发现,小车高速通过时可能漏检,所以加入了编码器辅助判断:当行驶距离超过30cm仍未检测到起始线时强制触发。
分叉路口处理更复杂些。推荐两种方案:
- 外圈方案:检测到路口后维持原速前进300ms
- 内圈方案:检测最外侧两个灰度传感器状态变化
我们最终采用混合策略:
c复制if(Gray_Number >= 3) { // 至少三个传感器触发
static u8 enter_count = 0;
enter_count++;
if(enter_count > 5) {
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) &&
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)) {
// 最两侧传感器都触发,执行外圈策略
Maintain_Speed(3000, 300);
} else {
// 执行内圈转向
Sharp_Turn(LEFT, 90);
}
enter_count = 0;
}
}
4. 双车协同状态机实现
整个系统需要管理6个主要状态:
- INIT:初始化状态
- TRACE:单车循迹
- SYNC:双车同步
- CROSS:通过路口
- FINISH:完成任务
- ERROR:异常处理
状态转换示意图如下:
code复制[INIT] --初始化完成--> [TRACE] --收到同步信号--> [SYNC]
[SYNC] --路径协调--> [CROSS] --完成通过--> [TRACE]
[任何状态] --异常--> [ERROR]
关键实现技巧是使用枚举定义状态变量,并通过定时器每100ms更新一次状态:
c复制typedef enum {
STATE_INIT,
STATE_TRACE,
STATE_SYNC,
STATE_CROSS,
STATE_FINISH,
STATE_ERROR
} SystemState;
SystemState current_state = STATE_INIT;
void TIM3_IRQHandler(void) { // 100ms定时中断
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
Update_System_State();
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
在SYNC状态时,主车会通过蓝牙发送速度指令,从车需要根据指令调整PWM占空比。我们实测发现,两车保持30cm间距时,速度差应控制在±15%以内才能稳定编队。
5. 调试技巧与性能优化
示波器是调试利器。建议重点监测三个信号:
- 灰度传感器输出波形(检查抗干扰能力)
- 电机PWM波形(观察控制响应)
- 蓝牙模块RXD/TXD信号(确认通信质量)
PID参数整定有个小窍门:先用Ziegler-Nichols方法初步确定参数,然后在实际赛道上微调。我们最终采用的参数是:
- 比例系数Kp=0.8
- 积分时间Ti=0.05
- 微分时间Td=0.02
电源管理容易被忽视。建议给数字电路和电机驱动使用独立电源,或者在中间加π型滤波电路。我们在比赛现场遇到过因电机启动导致单片机复位的状况,后来在电源端并联了4700μF电容解决问题。
最后分享一个代码优化技巧:将频繁调用的函数放在RAM中执行。STM32可以通过__attribute__((section(".ramfunc")))实现,这样能让关键控制循环的执行时间缩短20%以上。
