STM32F103ZET6智能小车开发实战从零搭建超声波避障与红外循迹系统1. 项目准备与环境搭建拿到STM32F103ZET6开发板的第一天我就被它丰富的GPIO资源和强大的性能所吸引。作为一款经典的Cortex-M3内核微控制器它完全能够胜任智能小车的控制任务。在开始编码之前我们需要做好以下准备工作硬件清单STM32F103ZET6最小系统板 ×1L298N电机驱动模块 ×1HC-SR04超声波模块 ×1TCRT5000红外循迹模块 ×3直流减速电机 ×2万向轮 ×118650电池及电池盒 ×1杜邦线若干提示初学者建议购买完整的智能小车底盘套件可以省去机械结构组装的时间专注于代码开发。开发环境我们选择Keil MDK这是STM32开发最常用的IDE之一。安装完成后需要配置以下内容// 系统时钟配置示例72MHz void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; // 配置HSE振荡器 RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9; HAL_RCC_OscConfig(RCC_OscInitStruct); // 配置系统时钟 RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2); }2. 超声波避障模块开发HC-SR04超声波模块是小车避障系统的核心。它的工作原理很简单发送一个10μs以上的高电平触发信号模块会发射8个40kHz的超声波脉冲然后检测回波。通过测量高电平持续时间可以计算出距离。硬件连接STM32引脚HC-SR04引脚PA1TrigPA2Echo5VVCCGNDGND在实际编码中我们需要特别注意以下几点触发信号必须持续10μs以上测量回波时间时要考虑超时情况多次测量取平均值提高稳定性// 超声波测距核心代码 float Get_Distance(void) { float distance; uint32_t timeout 0; // 发送10us高电平触发信号 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); delay_us(15); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 等待回波信号变高 while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) GPIO_PIN_RESET) { if(timeout 100000) return 0; // 超时处理 } // 测量高电平持续时间 uint32_t start HAL_GetTick(); while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) GPIO_PIN_SET) { if(HAL_GetTick() - start 30) break; // 超过30ms视为无效 } uint32_t duration HAL_GetTick() - start; // 计算距离声速340m/s distance duration * 0.034 / 2; return distance; }注意超声波模块在测量距离小于2cm或大于400cm时可能会出现误差实际应用中最好限制在这个范围内。3. 红外循迹系统实现TCRT5000红外循迹模块通过发射红外线并检测反射光强度来判断地面黑线位置。我们采用左中右三个模块的布局方案可以精确检测小车的偏移情况。模块安装位置左模块距离中心线左侧2cm中模块正对中心线右模块距离中心线右侧2cm循迹逻辑真值表左中右动作000停止或直行001左转010直行011轻微左转100右转101停止异常110轻微右转111停止终点// 循迹控制核心代码 void Track_Control(void) { uint8_t left HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); uint8_t middle HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); uint8_t right HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2); if(!left !middle right) { // 左转 Motor_Left(70); Motor_Right(30); } else if(!left middle !right) { // 直行 Motor_Left(60); Motor_Right(60); } else if(left !middle !right) { // 右转 Motor_Left(30); Motor_Right(70); } else if(!left middle right) { // 轻微左转 Motor_Left(50); Motor_Right(70); } else if(left middle !right) { // 轻微右转 Motor_Left(70); Motor_Right(50); } else if(!left !middle !right) { // 停止或直行根据应用场景决定 Motor_Left(60); Motor_Right(60); } else { // 其他情况停止 Motor_Stop(); } }4. 电机驱动与运动控制L298N是常用的双H桥电机驱动芯片可以同时驱动两个直流电机。我们需要通过PWM信号来控制电机转速通过方向控制引脚来控制转向。PWM配置关键代码// PWM初始化TIM3通道1和通道2 void PWM_Init(void) { TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; // 1MHz计数频率 htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // 1kHz PWM频率 htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比0 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); }电机控制函数// 左电机控制 void Motor_Left(int speed) { if(speed 0) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); // 方向控制 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed); } else if(speed 0) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, -speed); } else { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); } } // 右电机控制类似左电机使用TIM_CHANNEL_25. 系统整合与调试技巧将各个模块整合到一起时我们需要考虑任务调度和优先级问题。对于这种简单的智能小车可以采用轮询方式在主循环中依次处理各个传感器和控制器。主程序框架int main(void) { HAL_Init(); SystemClock_Config(); PWM_Init(); Ultrasonic_Init(); Track_Init(); while(1) { float distance Get_Distance(); if(distance 20.0) { // 避障模式 Avoid_Obstacle(distance); } else { // 循迹模式 Track_Control(); } // 其他任务如LED显示、按键检测等 HAL_Delay(50); // 控制循环周期 } }常见问题及解决方案超声波模块响应不稳定检查电源是否稳定增加软件滤波如多次测量取中值确保测量面没有障碍物干扰红外循迹误检测调整模块高度通常距离地面1-2cm最佳根据实际场地调整阈值电位器增加环境光补偿电机转动不顺畅检查PWM频率建议1-5kHz确保电源功率足够检查电机接线是否牢固系统死机或复位检查电源稳定性增加看门狗定时器检查堆栈大小是否足够在实际调试中我发现使用逻辑分析仪或示波器观察PWM信号和传感器信号非常有助于定位问题。特别是当电机转动时产生的电源干扰常常会导致传感器读数异常这时在电源端增加大容量电容如1000μF往往能解决问题。
新手也能搞定的STM32F103ZET6小车:从超声波避障到红外循迹,保姆级代码分享
STM32F103ZET6智能小车开发实战从零搭建超声波避障与红外循迹系统1. 项目准备与环境搭建拿到STM32F103ZET6开发板的第一天我就被它丰富的GPIO资源和强大的性能所吸引。作为一款经典的Cortex-M3内核微控制器它完全能够胜任智能小车的控制任务。在开始编码之前我们需要做好以下准备工作硬件清单STM32F103ZET6最小系统板 ×1L298N电机驱动模块 ×1HC-SR04超声波模块 ×1TCRT5000红外循迹模块 ×3直流减速电机 ×2万向轮 ×118650电池及电池盒 ×1杜邦线若干提示初学者建议购买完整的智能小车底盘套件可以省去机械结构组装的时间专注于代码开发。开发环境我们选择Keil MDK这是STM32开发最常用的IDE之一。安装完成后需要配置以下内容// 系统时钟配置示例72MHz void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; // 配置HSE振荡器 RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9; HAL_RCC_OscConfig(RCC_OscInitStruct); // 配置系统时钟 RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2); }2. 超声波避障模块开发HC-SR04超声波模块是小车避障系统的核心。它的工作原理很简单发送一个10μs以上的高电平触发信号模块会发射8个40kHz的超声波脉冲然后检测回波。通过测量高电平持续时间可以计算出距离。硬件连接STM32引脚HC-SR04引脚PA1TrigPA2Echo5VVCCGNDGND在实际编码中我们需要特别注意以下几点触发信号必须持续10μs以上测量回波时间时要考虑超时情况多次测量取平均值提高稳定性// 超声波测距核心代码 float Get_Distance(void) { float distance; uint32_t timeout 0; // 发送10us高电平触发信号 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); delay_us(15); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 等待回波信号变高 while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) GPIO_PIN_RESET) { if(timeout 100000) return 0; // 超时处理 } // 测量高电平持续时间 uint32_t start HAL_GetTick(); while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) GPIO_PIN_SET) { if(HAL_GetTick() - start 30) break; // 超过30ms视为无效 } uint32_t duration HAL_GetTick() - start; // 计算距离声速340m/s distance duration * 0.034 / 2; return distance; }注意超声波模块在测量距离小于2cm或大于400cm时可能会出现误差实际应用中最好限制在这个范围内。3. 红外循迹系统实现TCRT5000红外循迹模块通过发射红外线并检测反射光强度来判断地面黑线位置。我们采用左中右三个模块的布局方案可以精确检测小车的偏移情况。模块安装位置左模块距离中心线左侧2cm中模块正对中心线右模块距离中心线右侧2cm循迹逻辑真值表左中右动作000停止或直行001左转010直行011轻微左转100右转101停止异常110轻微右转111停止终点// 循迹控制核心代码 void Track_Control(void) { uint8_t left HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); uint8_t middle HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); uint8_t right HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2); if(!left !middle right) { // 左转 Motor_Left(70); Motor_Right(30); } else if(!left middle !right) { // 直行 Motor_Left(60); Motor_Right(60); } else if(left !middle !right) { // 右转 Motor_Left(30); Motor_Right(70); } else if(!left middle right) { // 轻微左转 Motor_Left(50); Motor_Right(70); } else if(left middle !right) { // 轻微右转 Motor_Left(70); Motor_Right(50); } else if(!left !middle !right) { // 停止或直行根据应用场景决定 Motor_Left(60); Motor_Right(60); } else { // 其他情况停止 Motor_Stop(); } }4. 电机驱动与运动控制L298N是常用的双H桥电机驱动芯片可以同时驱动两个直流电机。我们需要通过PWM信号来控制电机转速通过方向控制引脚来控制转向。PWM配置关键代码// PWM初始化TIM3通道1和通道2 void PWM_Init(void) { TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; // 1MHz计数频率 htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; // 1kHz PWM频率 htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比0 sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); }电机控制函数// 左电机控制 void Motor_Left(int speed) { if(speed 0) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); // 方向控制 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, speed); } else if(speed 0) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, -speed); } else { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); } } // 右电机控制类似左电机使用TIM_CHANNEL_25. 系统整合与调试技巧将各个模块整合到一起时我们需要考虑任务调度和优先级问题。对于这种简单的智能小车可以采用轮询方式在主循环中依次处理各个传感器和控制器。主程序框架int main(void) { HAL_Init(); SystemClock_Config(); PWM_Init(); Ultrasonic_Init(); Track_Init(); while(1) { float distance Get_Distance(); if(distance 20.0) { // 避障模式 Avoid_Obstacle(distance); } else { // 循迹模式 Track_Control(); } // 其他任务如LED显示、按键检测等 HAL_Delay(50); // 控制循环周期 } }常见问题及解决方案超声波模块响应不稳定检查电源是否稳定增加软件滤波如多次测量取中值确保测量面没有障碍物干扰红外循迹误检测调整模块高度通常距离地面1-2cm最佳根据实际场地调整阈值电位器增加环境光补偿电机转动不顺畅检查PWM频率建议1-5kHz确保电源功率足够检查电机接线是否牢固系统死机或复位检查电源稳定性增加看门狗定时器检查堆栈大小是否足够在实际调试中我发现使用逻辑分析仪或示波器观察PWM信号和传感器信号非常有助于定位问题。特别是当电机转动时产生的电源干扰常常会导致传感器读数异常这时在电源端增加大容量电容如1000μF往往能解决问题。