从零构建智能避障小车STM32与TOF10120的实战指南当第一次看到自己组装的小车在房间里自主穿梭、灵活避开障碍物时那种成就感是难以言喻的。TOFTime of Flight激光测距技术为创客们提供了一种高精度、低成本的避障解决方案。本文将带你从硬件选型到代码实现完整构建一个基于STM32和TOF10120的智能避障原型系统。1. 硬件架构设计1.1 核心组件选型一个完整的智能小车避障系统需要几个关键组件协同工作主控单元STM32F103C8T6蓝色药丸开发板72MHz Cortex-M3内核64KB Flash, 20KB RAM丰富的外设接口测距模块TOF10120激光测距传感器测量范围10cm-180cm最佳精度范围接口I2C默认地址0x52工作电压3.3V-5V电机驱动L298N双H桥驱动模块驱动电压5V-35V最大电流2A每通道支持PWM调速电源系统18650锂电池组7.4VAMS1117-3.3V稳压模块1.2 系统连接示意图---------------- ---------------- ---------------- | TOF10120 | | STM32F103 | | L298N | | (I2C) |---| (PB6/PB7) |---| (IN1-IN4) | ---------------- ---------------- ---------------- | | v v ---------- ----------- | USB | | DC电机 | | 串口调试 | ----------- ----------提示实际接线时注意给每个模块添加0.1uF的去耦电容特别是在电机驱动附近。2. TOF10120传感器驱动开发2.1 I2C接口初始化STM32的硬件I2C有时会遇到稳定性问题我们可以采用软件模拟I2C// 软件I2C引脚定义 #define TOF_I2C_SCL_PIN GPIO_Pin_6 #define TOF_I2C_SDA_PIN GPIO_Pin_7 #define TOF_I2C_PORT GPIOB void I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin TOF_I2C_SCL_PIN | TOF_I2C_SDA_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(TOF_I2C_PORT, GPIO_InitStructure); GPIO_SetBits(TOF_I2C_PORT, TOF_I2C_SCL_PIN | TOF_I2C_SDA_PIN); }2.2 距离数据读取TOF10120提供两种距离数据模式实时模式和滤波模式。对于移动中的小车滤波模式更为稳定#define TOF_ADDRESS 0x52 uint16_t TOF_ReadDistance(void) { uint8_t buffer[2]; uint16_t distance 0; // 启动I2C通信 I2C_Start(); I2C_WriteByte(TOF_ADDRESS 1); I2C_WaitAck(); // 请求滤波后的距离数据寄存器0x04 I2C_WriteByte(0x04); I2C_WaitAck(); I2C_Stop(); // 读取2字节数据 I2C_Start(); I2C_WriteByte((TOF_ADDRESS 1) | 0x01); I2C_WaitAck(); buffer[1] I2C_ReadByte(); I2C_Ack(); buffer[0] I2C_ReadByte(); I2C_NAck(); I2C_Stop(); distance (buffer[1] 8) | buffer[0]; return distance; }2.3 数据滤波处理即使使用传感器的滤波模式实际应用中仍需添加软件滤波#define FILTER_SIZE 5 uint16_t distance_filter[FILTER_SIZE] {0}; uint8_t filter_index 0; uint16_t ApplyMedianFilter(uint16_t new_value) { // 更新滤波数组 distance_filter[filter_index] new_value; filter_index (filter_index 1) % FILTER_SIZE; // 简单中值滤波 uint16_t temp[FILTER_SIZE]; memcpy(temp, distance_filter, sizeof(distance_filter)); // 冒泡排序 for(int i0; iFILTER_SIZE-1; i) { for(int j0; jFILTER_SIZE-i-1; j) { if(temp[j] temp[j1]) { uint16_t t temp[j]; temp[j] temp[j1]; temp[j1] t; } } } return temp[FILTER_SIZE/2]; }3. 电机控制与运动逻辑3.1 L298N电机驱动实现L298N模块支持PWM调速我们可以使用STM32的定时器产生PWM信号void Motor_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 配置GPIO 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_Period 999; // 1kHz PWM TIM_TimeBaseStructure.TIM_Prescaler 71; // 72MHz/(711) 1MHz TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比0 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_Cmd(TIM2, ENABLE); } void SetMotorSpeed(int left, int right) { // 限制PWM值在0-999范围内 left left 999 ? 999 : (left 0 ? 0 : left); right right 999 ? 999 : (right 0 ? 0 : right); // 设置PWM占空比 TIM_SetCompare1(TIM2, left); TIM_SetCompare2(TIM2, right); }3.2 避障算法实现基于距离数据的简单避障算法可以采用状态机实现#define SAFE_DISTANCE 30 // 30cm安全距离 #define TURN_DISTANCE 20 // 20cm紧急避障距离 typedef enum { STATE_FORWARD, STATE_TURN_LEFT, STATE_TURN_RIGHT, STATE_BACKWARD } ObstacleAvoidanceState; ObstacleAvoidanceState current_state STATE_FORWARD; void ObstacleAvoidance(uint16_t distance) { static uint32_t turn_timer 0; switch(current_state) { case STATE_FORWARD: if(distance TURN_DISTANCE) { // 紧急避障 current_state STATE_BACKWARD; SetMotorSpeed(-500, -500); // 后退 turn_timer 0; } else if(distance SAFE_DISTANCE) { // 转向避障 if(rand() % 2) { current_state STATE_TURN_LEFT; SetMotorSpeed(-300, 300); // 左转 } else { current_state STATE_TURN_RIGHT; SetMotorSpeed(300, -300); // 右转 } turn_timer 0; } else { // 前进 SetMotorSpeed(600, 600); } break; case STATE_TURN_LEFT: case STATE_TURN_RIGHT: turn_timer; if(turn_timer 500 || distance SAFE_DISTANCE 10) { current_state STATE_FORWARD; } break; case STATE_BACKWARD: turn_timer; if(turn_timer 300) { current_state STATE_FORWARD; } break; } }4. 系统优化与调试技巧4.1 响应速度优化智能小车的实时性至关重要以下是几种优化策略I2C通信优化将I2C时钟频率提高到400kHz快速模式减少不必要的寄存器读取使用DMA传输如果支持控制周期优化设置合理的控制周期建议50-100ms使用定时器中断触发控制循环// 定时器中断配置示例 void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); uint16_t distance TOF_ReadDistance(); distance ApplyMedianFilter(distance); ObstacleAvoidance(distance); } }4.2 多传感器融合思路单一TOF传感器存在盲区可以考虑增加传感器数量前、左、右各安装一个TOF10120互补传感器添加红外或超声波传感器作为冗余传感器数据融合算法typedef struct { uint16_t front; uint16_t left; uint16_t right; uint32_t timestamp; } SensorData; SensorData FuseSensors(SensorData raw) { SensorData fused; // 简单加权融合 fused.front raw.front * 0.6 raw.left * 0.2 raw.right * 0.2; // 时间补偿 if(raw.timestamp 100) { // 如果数据延迟超过100ms fused.front * 1.1; // 增加预测权重 } return fused; }4.3 常见问题排查问题现象可能原因解决方案读取距离为0I2C通信失败检查接线确认上拉电阻数据跳动大电源干扰添加滤波电容检查接地小车反应迟钝控制周期过长优化代码结构减少延迟电机不转电源不足检查电池电压确保L298N使能注意调试时建议先用串口输出距离数据确认传感器工作正常后再进行避障测试。