本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:STM32超声波避障小车是一种基于ARM Cortex-M内核微控制器的智能移动机器人系统,利用HC-SR04等超声波传感器探测障碍物,并通过STM32进行数据处理和避障决策。该设计涵盖微控制器编程、传感器数据采集、电机控制、避障算法实现及电源管理等多个关键技术环节。适用于嵌入式系统学习与机器人开发实践,帮助学生掌握从硬件搭建到软件调试的完整开发流程。
stm32 超声波避障小车1

1. STM32微控制器基础与选型

1.1 STM32微控制器的基本架构

STM32系列微控制器基于ARM Cortex-M内核构建,常见的内核版本包括Cortex-M0、M3、M4和M7等,具有高性能、低功耗和低成本的特点。其基本架构包括:

  • 内核 :负责指令执行和中断处理,如Cortex-M4支持浮点运算,适用于实时控制和数字信号处理。
  • 存储器 :包括Flash和SRAM,用于程序存储和数据缓存。
  • 外设接口 :包含GPIO、UART、SPI、I2C、ADC、DAC、定时器等,便于连接各类传感器与执行器。

以STM32F407为例,其主频可达168MHz,支持外部存储器扩展,具备丰富的定时器资源,非常适合用于智能小车等嵌入式控制系统。

2. 超声波传感器工作原理与应用

2.1 超声波测距的基本原理

2.1.1 超声波的发射与接收机制

超声波测距技术是一种基于声波传播时间差的非接触式距离测量方法。其核心原理是通过发射超声波脉冲,并检测该脉冲在空气中传播后反射回来的时间差,从而计算出目标物体的距离。

超声波发射器通常由压电陶瓷构成,当施加高频电压信号(一般为40kHz)时,压电陶瓷会因机械振动而产生超声波。这些超声波在空气中以一定的速度(约340 m/s)传播,遇到障碍物后发生反射,被接收器捕获。接收器同样由压电材料构成,当接收到反射的超声波时,会产生微弱的电信号。

在智能小车系统中,这种发射-接收机制可以用于实时感知前方障碍物的距离,从而辅助避障决策。为了确保测量的准确性,发射与接收之间的时间间隔必须精确测量。

示例代码:超声波发射信号的生成

以下代码片段使用STM32的GPIO和定时器生成一个40kHz、持续时间为10μs的触发信号,适用于HC-SR04传感器:

void Trigger_UltraSonic(void) {
    HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);  // 拉高TRIG引脚
    HAL_Delay(1);                                                // 延时10μs
    HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); // 拉低TRIG引脚
}

代码解释:
- HAL_GPIO_WritePin() 用于控制TRIG引脚的电平状态。
- HAL_Delay(1) 实现1毫秒的延时,但由于系统时钟配置不同,可能需要使用更精确的定时器中断来实现微秒级控制。
- 触发信号持续时间为10μs是HC-SR04的标准要求。

2.1.2 时间差计算与距离换算公式

在超声波测距系统中,最核心的计算是通过声波传播的时间差来推导出距离。由于声波在空气中的传播速度是已知的(约为340 m/s),因此可以通过以下公式计算距离:

\text{Distance} = \frac{v \times t}{2}

其中:
- $ v $:声波传播速度(单位:m/s)
- $ t $:从发射到接收所经历的时间(单位:s)

除以2的原因是超声波经历了从发射到反射再返回的双程路径。

示例:使用STM32获取时间差并计算距离

在STM32平台上,通常使用定时器的输入捕获功能来获取精确的时间差值。以下为伪代码示例:

uint32_t start_time = 0;
uint32_t end_time = 0;
float distance = 0.0;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim == &htim2) {
        if (ultrasonic_state == ULTRASONIC_WAITING) {
            start_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 获取开始时间
            ultrasonic_state = ULTRASONIC_ECHOING;
        } else {
            end_time = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);   // 获取结束时间
            uint32_t duration = end_time - start_time;
            distance = (340.0 * duration * 0.000001) / 2;  // 转换为米
            ultrasonic_state = ULTRASONIC_DONE;
        }
    }
}

代码解释:
- 使用定时器2的输入捕获通道1来检测超声波回波信号的上升沿和下降沿。
- start_time 记录回波信号开始的时间戳。
- end_time 记录回波信号结束的时间戳。
- duration 表示回波持续时间(单位为定时器计数周期)。
- 最终将时间转换为距离,单位为米。

测距误差分析

超声波测距的精度受多种因素影响,如环境温度、湿度、风速等,这些都会改变声波在空气中的传播速度。因此,在高精度应用场景中,通常需要引入温度传感器进行补偿。

2.2 HC-SR04传感器的结构与接口

2.2.1 模块引脚功能与电气特性

HC-SR04 是一款常用的超声波测距模块,其结构简单、成本低廉,适用于嵌入式避障系统。该模块共有4个引脚,分别是VCC、GND、TRIG和ECHO。

引脚名称 功能描述
VCC 电源正极(工作电压为5V)
GND 电源负极
TRIG 触发输入信号,高电平持续10μs启动一次测距
ECHO 回波输出信号,高电平持续时间与距离成正比
电气特性参数表
参数 数值范围 单位
工作电压 4.5 ~ 5.5 V
静态电流 < 2 mA
探测范围 2 ~ 400 cm
分辨率 0.3 cm
触发信号高电平 > 10 μs
输出信号高电平 与探测距离成正比

2.2.2 与STM32的通信方式与时序控制

HC-SR04模块与STM32的通信主要通过GPIO引脚实现。其中,TRIG引脚由STM32发出触发信号,ECHO引脚则通过定时器输入捕获功能检测回波信号的时间差。

HC-SR04与STM32连接示意图(使用Mermaid流程图)
graph TD
    A[STM32] -->|TRIG| B(HC-SR04)
    B -->|ECHO| A
    A -->|Power| C(+5V)
    B -->|Power| C
    C --> D(GND)
    A -->|GND| D
模块通信时序图(使用Mermaid绘制)
sequenceDiagram
    participant STM32
    participant HC-SR04

    STM32->>HC-SR04: TRIG=1 (持续10μs)
    HC-SR04-->>STM32: ECHO=1 (开始)
    HC-SR04-->>STM32: ECHO=0 (结束)
示例代码:配置HC-SR04与STM32的GPIO接口
// 定义TRIG和ECHO引脚
#define TRIG_PIN GPIO_PIN_0
#define TRIG_PORT GPIOA
#define ECHO_PIN GPIO_PIN_1
#define ECHO_PORT GPIOA

// 初始化函数
void UltraSonic_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();

    // TRIG配置为推挽输出
    GPIO_InitStruct.Pin = TRIG_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(TRIG_PORT, &GPIO_InitStruct);

    // ECHO配置为输入
    GPIO_InitStruct.Pin = ECHO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(ECHO_PORT, &GPIO_InitStruct);
}

代码解释:
- 使用 GPIO_MODE_OUTPUT_PP 将TRIG设置为推挽输出,用于触发传感器。
- 使用 GPIO_MODE_INPUT 将ECHO设置为输入模式,等待接收回波信号。
- 后续通过定时器捕获ECHO信号的高电平时间即可完成测距。

2.3 超声波传感器在避障系统中的部署

2.3.1 多传感器布局与覆盖范围设计

在智能小车的避障系统中,单一超声波传感器的探测角度有限(通常为15°~30°),无法覆盖整个前方空间。因此,通常采用多传感器布局策略,以扩大探测范围并提高避障的鲁棒性。

常见布局方式
布局方式 描述 优点 缺点
前方单传感器 安装于车体正前方 简单易实现 探测范围有限
前方双传感器 左右各一个,夹角15°~30° 增强侧向障碍检测能力 成本略高
前方三传感器 正前方+左右各一个,呈扇形分布 全方位探测 系统复杂度高
全向布局 前后左右各一个 全方位避障 引脚资源消耗大
传感器布局示意图(使用Mermaid绘制)
pie
    title 超声波传感器布局分布
    "前方中心" : 1
    "左侧前方" : 1
    "右侧前方" : 1
    "后方" : 1

2.3.2 环境因素对测距精度的影响

虽然超声波测距技术广泛应用于避障系统中,但其精度受多种环境因素影响:

影响因素分析表
影响因素 影响机制 影响程度 补偿方法
温度变化 改变声速 中等 加入温度传感器校准
障碍物材质 反射率不同 多传感器融合
风速与空气流动 改变声波传播方向 不易补偿
湿度变化 改变空气密度 可忽略
超声波干扰 多传感器串扰 启动时间错开
示例代码:多传感器数据采集与融合
typedef struct {
    float front;
    float left;
    float right;
} DistanceData;

DistanceData sensor_data;

void Read_Sensors(void) {
    sensor_data.front = Get_Distance(FRONT_SENSOR);
    sensor_data.left  = Get_Distance(LEFT_SENSOR);
    sensor_data.right = Get_Distance(RIGHT_SENSOR);
}

void Avoid_Obstacle(void) {
    if (sensor_data.front < 20) {
        if (sensor_data.left < sensor_data.right) {
            // 向左转
            Turn_Left();
        } else {
            // 向右转
            Turn_Right();
        }
    } else {
        Move_Forward();
    }
}

代码解释:
- Read_Sensors() 函数用于同时读取三个方向的超声波传感器数据。
- Avoid_Obstacle() 根据距离数据决定小车的运动方向。
- 如果前方距离小于20cm,优先选择障碍物较少的一侧转向。

实际部署建议

在实际部署中,建议将传感器安装在小车前方略高于地面的位置,避免地面反射造成的误判。同时,建议对传感器进行软件滤波处理,以提升测距稳定性。

3. STM32 GPIO与定时器配置实现超声波测距

在嵌入式系统开发中,微控制器的通用输入输出(GPIO)和定时器是实现外设控制与精确计时的关键模块。本章将围绕STM32平台,结合HC-SR04超声波传感器的测距需求,深入讲解GPIO配置与定时器使用方法。通过本章内容,读者将掌握如何配置STM32的GPIO引脚模式、如何使用定时器进行高精度时间测量,并最终实现完整的超声波测距驱动代码。

3.1 GPIO端口的基本配置

在STM32中,GPIO引脚支持多种工作模式,包括输入、输出、复用功能和中断配置。在与HC-SR04传感器交互时,我们需要将部分引脚配置为推挽输出用于发送触发信号,另一些引脚配置为上拉输入用于接收回波信号。

3.1.1 推挽输出与上拉输入模式设置

推挽输出(Push-Pull Output)具有较强的驱动能力,适合用于输出高电平和低电平信号;而上拉输入(Input with Pull-Up)则适用于读取外部信号,并具备上拉电阻以防止浮空。

以下为配置GPIO的基本步骤(以STM32F4系列为例):

// 初始化GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct;

// 使能GPIO端口时钟(假设使用GPIOB)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

// 配置触发引脚(PB0)为推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStruct);

// 配置回波引脚(PB1)为上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStruct);

逐行分析:

  • RCC_AHB1PeriphClockCmd() :使能GPIOB的时钟,否则无法访问该端口。
  • GPIO_InitStruct :定义一个结构体变量用于配置引脚属性。
  • GPIO_Mode_OUT :设置为输出模式。
  • GPIO_OType_PP :选择推挽输出类型。
  • GPIO_Speed_50MHz :设置输出速度为50MHz,适用于中高速场景。
  • GPIO_PuPd_NOPULL :不启用上下拉电阻。
  • 对于回波引脚PB1,设置为输入模式,并启用上拉电阻以确保高电平稳定。

3.1.2 引脚复用与中断配置

当需要对回波信号进行高精度捕获时,可将GPIO配置为复用功能,并与定时器的输入捕获通道关联。此外,也可配置外部中断以实现回波信号的实时响应。

配置回波引脚为复用输入并关联定时器通道:
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStruct);

// 设置复用功能为定时器输入捕获通道
GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM2);
配置外部中断:
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;

// 配置EXTI
EXTI_InitStruct.EXTI_Line = EXTI_Line1;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);

// 配置NVIC
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

参数说明:

  • GPIO_Mode_AF :将引脚配置为复用功能。
  • GPIO_AF_TIM2 :将该引脚连接到TIM2的输入捕获通道。
  • EXTI_Line1 :对应PB1引脚的外部中断线。
  • EXTI_Trigger_Rising :设置为上升沿触发中断。

3.2 定时器的使用与精确计时

STM32的通用定时器(如TIM2、TIM3)提供了输入捕获、输出比较、PWM生成等多种功能,非常适合用于高精度时间测量。

3.2.1 定时器时钟配置与计数器初始化

定时器的计数频率由系统时钟分频而来,需根据所需精度合理设置预分频值。

TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;

// 使能定时器2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

// 配置定时器基本参数
TIM_TimeBaseStruct.TIM_Prescaler = 84 - 1;       // 预分频值,系统时钟为168MHz,分频后为2MHz
TIM_TimeBaseStruct.TIM_Period = 0xFFFFFFFF;       // 自动重装载值设为最大
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);

// 启动定时器
TIM_Cmd(TIM2, ENABLE);

逐行分析:

  • TIM_Prescaler = 84 - 1 :将168MHz系统时钟分频为2MHz,即每个计数周期为0.5μs。
  • TIM_Period = 0xFFFFFFFF :设置为最大值,避免频繁溢出。
  • TIM_Cmd(TIM2, ENABLE) :启动定时器。

3.2.2 输入捕获模式实现高精度时间测量

输入捕获(Input Capture)可用于记录外部信号的上升沿或下降沿到来时刻,从而计算两个事件之间的时间差。

配置输入捕获通道:
TIM_ICInitTypeDef TIM_ICInitStruct;

// 初始化输入捕获结构体
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICFilter = 0x00;
TIM_ICInit(TIM2, &TIM_ICInitStruct);

// 启用捕获比较中断
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);

参数说明:

  • TIM_Channel_2 :使用定时器通道2。
  • TIM_ICPolarity_Rising :设置为上升沿触发捕获。
  • TIM_ICSelection_DirectTI :选择直接输入模式。
  • TIM_ICPrescaler = TIM_ICPSC_DIV1 :不进行分频,每次边沿都触发捕获。
  • TIM_ICFilter = 0x00 :滤波器关闭,适用于快速信号。
示例中断处理函数:
void TIM2_IRQHandler(void) {
    static uint32_t rising_time = 0, falling_time = 0;
    if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) {
        if (TIM_GetCapture2(TIM2) == 0) {
            rising_time = TIM_GetCounter(TIM2);
        } else {
            falling_time = TIM_GetCounter(TIM2);
            uint32_t time_diff = falling_time - rising_time;
            // 根据时间差计算距离
            float distance = time_diff * 0.034 / 2;  // 单位:cm
        }
        TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    }
}

逻辑分析:

  • 每次捕获中断触发时,判断当前是上升沿还是下降沿。
  • time_diff 表示高电平持续时间,单位为0.5μs。
  • 使用公式 distance = time_diff * 0.034 / 2 计算距离,其中0.034表示声速(cm/μs),除以2是因为来回路程。

3.3 超声波测距模块的驱动实现

结合GPIO与定时器配置,本节将展示完整的超声波测距模块驱动代码,并介绍调试技巧。

3.3.1 触发信号生成与回波捕获流程

HC-SR04的工作流程如下:

  1. 向TRIG引脚发送至少10μs的高电平脉冲,触发测距。
  2. 模块自动发送8个40kHz的超声波脉冲。
  3. ECHO引脚输出高电平,持续时间等于超声波往返时间。

流程图如下:

graph TD
A[开始测距] --> B[发送10μs高电平触发信号]
B --> C[等待ECHO上升沿]
C --> D[记录上升沿时间]
D --> E[等待ECHO下降沿]
E --> F[记录下降沿时间]
F --> G[计算时间差]
G --> H[换算为距离]

3.3.2 STM32代码实现与调试技巧

完整测距函数实现:
float get_distance(void) {
    uint32_t time_diff;
    float distance;

    // 发送10μs高电平触发
    GPIO_SetBits(GPIOB, GPIO_Pin_0);
    Delay_us(10);
    GPIO_ResetBits(GPIOB, GPIO_Pin_0);

    // 等待ECHO上升沿
    while (!GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1));

    // 启动定时器捕获
    TIM_Cmd(TIM2, ENABLE);
    TIM_SetCounter(TIM2, 0);

    // 等待ECHO下降沿
    while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1));

    // 获取计数值
    time_diff = TIM_GetCounter(TIM2);
    distance = time_diff * 0.034 / 2;

    return distance;
}

逐行分析:

  • GPIO_SetBits() GPIO_ResetBits() :控制TRIG引脚发送触发信号。
  • Delay_us(10) :使用微秒级延时函数生成10μs高电平。
  • while (!GPIO_ReadInputDataBit()) :等待ECHO上升沿。
  • TIM_SetCounter(TIM2, 0) :重置计数器。
  • while (GPIO_ReadInputDataBit()) :等待ECHO下降沿。
  • 最后根据时间差计算距离。
调试技巧:
  1. 示波器检测信号: 使用示波器观察TRIG和ECHO引脚波形,确认触发信号和回波是否正常。
  2. 串口打印距离值: 将测得的距离值通过串口输出,便于验证数据准确性。
  3. 延时函数校准: 使用定时器或SysTick实现精确的微秒级延时函数。
  4. 滤波处理: 多次测量取平均值,减少偶然误差。
示例串口输出代码:
char buffer[50];
sprintf(buffer, "Distance: %.2f cm\r\n", get_distance());
USART_SendString(USART2, buffer);

效果:

在串口调试助手中可看到类似如下输出:

Distance: 25.32 cm
Distance: 24.87 cm
Distance: 25.11 cm

总结

本章详细讲解了如何在STM32平台上配置GPIO与定时器,实现对HC-SR04超声波传感器的驱动。从GPIO模式配置、中断设置,到定时器的输入捕获与高精度时间测量,再到完整的测距函数编写与调试技巧,内容层层递进,适合有一定嵌入式开发经验的读者深入学习。通过本章内容,读者应能够独立完成基于STM32的超声波测距模块开发,并为后续数据处理与避障控制打下坚实基础。

4. 超声波数据滤波处理

在智能小车的避障系统中,超声波传感器虽然能够实现基础的距离测量,但由于其工作原理依赖于声波的发射与接收,极易受到环境噪声、多路径反射、信号干扰等因素的影响,导致测得的距离数据出现波动。为了提升测距的稳定性和可靠性,对超声波传感器返回的数据进行滤波处理是至关重要的。本章将从滤波的必要性出发,深入讲解常用的滤波算法及其在STM32平台上的实现方法。

4.1 数据滤波的必要性分析

4.1.1 环境噪声与信号干扰问题

在实际应用中,超声波传感器的工作环境往往不是理想的。例如,当小车运行在存在其他超声波设备或存在强烈反射面的环境中时,可能会导致传感器接收回波的时间出现偏差。此外,机械振动、电源波动等因素也可能引起测量数据的抖动。

干扰因素 影响描述 对策
多路径反射 声波经过多个表面反射后被接收,造成测量距离偏大 优化传感器布局、采用中值滤波
温度变化 声速随温度变化而变化,影响测距精度 温度补偿算法
电源波动 导致信号强度不稳定,影响回波检测准确性 稳压电路、软件滤波

4.1.2 提高测距稳定性的意义

测距数据的稳定性直接影响避障系统的决策准确性。例如,在避障逻辑中,如果因传感器数据波动而导致误判前方存在障碍物,可能会导致小车不必要的转向或停止,影响整体运行效率。因此,通过滤波技术平滑数据,提升系统的鲁棒性,是实现高效避障的关键。

4.2 常用滤波算法对比与实现

4.2.1 算术平均值滤波与滑动平均法

算术平均值滤波是一种最基础的滤波方法,其原理是对一定数量的连续测量值进行求和并取平均。这种方法适用于周期性噪声的消除,但响应速度较慢,对突变信号不敏感。

#define FILTER_SIZE 10

float buffer[FILTER_SIZE];
int index = 0;

float arithmetic_average_filter(float new_value) {
    buffer[index++] = new_value;
    if (index >= FILTER_SIZE) index = 0;

    float sum = 0;
    for (int i = 0; i < FILTER_SIZE; i++) {
        sum += buffer[i];
    }
    return sum / FILTER_SIZE;
}

代码解析:

  • FILTER_SIZE 定义滤波窗口大小。
  • buffer[] 存储最近的测量值。
  • index 用于循环写入缓冲区。
  • 每次调用函数时,更新缓冲区并计算平均值作为输出。

滑动平均法是算术平均的一种改进,它在每次添加新值时移除最旧的值,从而保持窗口滑动,提升对数据变化的响应速度。

4.2.2 中值滤波与加权平均滤波

中值滤波

中值滤波是一种非线性滤波方法,其原理是将一组数据排序后取中间值作为输出。特别适用于去除脉冲型噪声。

float median_filter(float new_value) {
    static float buffer[5];
    static int index = 0;

    buffer[index++] = new_value;
    if (index >= 5) index = 0;

    // 对buffer排序
    float temp[5];
    memcpy(temp, buffer, sizeof(temp));
    for (int i = 0; i < 5; i++) {
        for (int j = i + 1; j < 5; j++) {
            if (temp[i] > temp[j]) {
                float t = temp[i];
                temp[i] = temp[j];
                temp[j] = t;
            }
        }
    }

    return temp[2];  // 取中间值
}

代码解析:

  • 每次将新值插入缓冲区后,对数组进行排序。
  • 返回中间值,有效滤除异常值。
加权平均滤波

加权平均滤波根据数据的时间远近赋予不同的权重,较新的数据赋予更高的权重,适合处理具有趋势变化的信号。

float weighted_average_filter(float new_value) {
    static float history[3] = {0, 0, 0};

    history[2] = history[1];
    history[1] = history[0];
    history[0] = new_value;

    return (history[0] * 0.5 + history[1] * 0.3 + history[2] * 0.2);
}

代码解析:

  • 使用历史数据,对最近的数据赋予更高权重。
  • 适用于数据具有趋势变化的情况,如小车缓慢靠近障碍物。

算法对比表

滤波方法 优点 缺点 适用场景
算术平均 实现简单,滤除周期性噪声 延迟大,对突变响应差 数据波动较小
滑动平均 响应更快 实现复杂度略高 动态数据过滤
中值滤波 抑制脉冲噪声效果好 增加计算量 异常值较多的场合
加权平均 适应趋势变化 参数敏感 有方向性变化的数据

4.3 滤波算法在STM32上的应用

4.3.1 数据采集与缓存机制

在STM32平台上,为了实现高效滤波,通常需要结合定时器中断进行周期性数据采集,并使用环形缓冲区进行数据缓存。

graph TD
    A[定时器中断] --> B{采集新数据}
    B --> C[写入缓冲区]
    C --> D[触发滤波处理]
    D --> E[更新显示或避障决策]

流程说明:

  1. 定时器中断 :设置定时器每10ms触发一次中断。
  2. 采集新数据 :在中断服务函数中调用超声波测距函数获取当前距离值。
  3. 写入缓冲区 :将新值存入环形缓冲区,覆盖旧值。
  4. 触发滤波处理 :调用滤波函数处理当前缓冲区数据。
  5. 更新系统状态 :将滤波后的结果用于显示或避障逻辑判断。

4.3.2 滤波函数编写与性能优化

在STM32中,由于资源有限,需要在滤波算法的选择和实现上进行性能优化。

优化策略:
  1. 固定窗口大小 :避免动态内存分配,使用固定大小的数组。
  2. 使用整数运算 :浮点运算占用较多CPU资源,可将距离值乘以100转换为整数处理。
  3. 预排序机制 :对于中值滤波,可在插入新值时维护排序,避免每次排序。

示例:使用整数实现的滑动平均滤波

#define BUF_SIZE 5
int16_t buffer[BUF_SIZE];
uint8_t index = 0;

int16_t moving_average_filter(int16_t new_value) {
    buffer[index++] = new_value;
    if (index >= BUF_SIZE) index = 0;

    int32_t sum = 0;
    for (int i = 0; i < BUF_SIZE; i++) {
        sum += buffer[i];
    }
    return sum / BUF_SIZE;
}

性能分析:

  • 使用整型变量减少浮点运算开销。
  • 窗口大小较小,适合资源受限的MCU。
  • 适用于对精度要求不高的避障场景。
编译优化建议:
  • 启用编译器优化(如 -O2 )。
  • 将滤波函数定义为 inline 以减少函数调用开销。
  • 避免频繁使用 printf 或调试输出,影响实时性。

综上所述,滤波处理是提升超声波测距精度和系统稳定性的重要手段。在STM32平台上,开发者可以根据具体应用场景选择合适的滤波算法,并结合硬件资源进行合理优化,从而为后续的避障逻辑提供可靠的数据支撑。

5. 避障策略设计与实现

在智能小车的实际运行中,避障策略是实现自主导航的核心环节。本章将围绕避障系统的设计逻辑、距离阈值的设定方法、路径规划机制以及行为控制实现展开详细分析。我们将基于STM32平台,结合超声波传感器的数据输入,设计一套具备逻辑判断、状态切换和路径决策能力的避障系统。

5.1 避障系统的基本逻辑框架

5.1.1 障碍物识别与距离判断

避障系统的第一步是通过传感器获取环境信息,并判断是否检测到障碍物。在本系统中,我们采用多个HC-SR04超声波模块进行多方向探测,每个模块可提供当前方向上的距离数据。

以下为障碍物识别的基本判断逻辑:

#define OBSTACLE_THRESHOLD 30  // 障碍物判定阈值,单位cm

// 假设left_distance、front_distance、right_distance为三个方向的测距结果
if (front_distance < OBSTACLE_THRESHOLD) {
    // 前方有障碍物
    obstacle_detected = FRONT;
} else if (left_distance < OBSTACLE_THRESHOLD) {
    // 左侧有障碍物
    obstacle_detected = LEFT;
} else if (right_distance < OBSTACLE_THRESHOLD) {
    // 右侧有障碍物
    obstacle_detected = RIGHT;
} else {
    // 无障碍物
    obstacle_detected = NONE;
}
代码逻辑分析与参数说明:
  • OBSTACLE_THRESHOLD 表示设定的障碍物距离阈值,单位为厘米。该值可以根据实际应用场景调整。
  • 变量 obstacle_detected 用于记录障碍物出现的方向,其值为枚举类型,如 {NONE, FRONT, LEFT, RIGHT}
  • 该段代码通过简单的比较逻辑判断前方、左侧或右侧是否出现障碍物,并将结果保存,供后续决策使用。

5.1.2 决策逻辑与行为输出

在完成障碍物识别后,需要根据识别结果执行相应的避障行为。常见的行为包括:停止、后退、左转、右转等。

以下为一个简单的决策逻辑实现:

switch(obstacle_detected) {
    case FRONT:
        stop();          // 停止前进
        delay_ms(500);   // 延时500ms
        reverse();       // 后退
        delay_ms(300);   // 延时300ms
        turn_right();    // 右转
        break;

    case LEFT:
        turn_right();    // 向右避障
        break;

    case RIGHT:
        turn_left();     // 向左避障
        break;

    case NONE:
        move_forward();  // 继续前进
        break;

    default:
        move_forward();  // 默认前进
        break;
}
代码逻辑分析与参数说明:
  • switch 结构根据 obstacle_detected 的值决定执行哪一种避障动作。
  • stop() reverse() turn_left() 等函数为封装好的电机控制函数,将在后续章节中详细介绍。
  • 通过 delay_ms() 控制避障动作的持续时间,避免因动作过快导致系统不稳定。

5.2 距离阈值设定与路径规划

5.2.1 静态阈值与动态阈值的比较

在避障系统中,距离阈值的设定直接影响避障行为的灵敏度和稳定性。常见的设定方法包括静态阈值和动态阈值。

表:静态阈值与动态阈值对比分析
特性 静态阈值 动态阈值
定义 固定的距离判断值 根据环境或速度动态调整的判断值
优点 实现简单,逻辑清晰 更加灵活,适应不同环境和速度
缺点 环境适应性差,容易误判或漏判 实现复杂,需考虑多种变量影响
适用场景 简单避障、固定环境 多变环境、高速移动、复杂路径规划
示例代码:动态阈值设定
// 动态阈值计算函数,根据当前速度调整阈值
uint16_t calculate_threshold(uint8_t speed) {
    float base_threshold = 30.0;  // 基础阈值30cm
    float dynamic_factor = 0.5;   // 速度影响因子
    uint16_t dynamic_threshold = base_threshold - (speed * dynamic_factor);
    return (dynamic_threshold > 10) ? dynamic_threshold : 10;  // 最小阈值10cm
}
代码逻辑分析与参数说明:
  • speed 为当前小车的运行速度,单位为任意比例值。
  • dynamic_factor 控制速度对阈值的影响程度,数值越大,速度越高,避障越灵敏。
  • dynamic_threshold 为动态计算出的避障阈值,避免因速度过高而无法及时避障。
  • 最终返回的阈值不会低于10cm,防止误触发。

5.2.2 多方向探测与路径选择策略

在实际避障过程中,智能小车通常需要同时探测多个方向的距离,从而选择最优路径。以下为一个基于方向探测的路径选择策略示意图:

graph TD
    A[前方无障碍] --> B[继续前进]
    A --> C[前方有障碍]
    C --> D{是否有左侧路径}
    D -->|有| E[左转]
    D -->|无| F{是否有右侧路径}
    F -->|有| G[右转]
    F -->|无| H[后退并转向]
流程图说明:
  • 小车优先判断前方是否有障碍,若有则进入路径选择流程。
  • 若左侧路径畅通,则优先左转避障。
  • 若左侧被堵,则判断右侧路径是否畅通,若畅通则右转。
  • 若左右都被堵,则执行后退并转向动作,寻找新的路径。

5.3 避障行为的控制实现

5.3.1 行为模式切换与优先级控制

在复杂环境中,小车可能同时面临多个障碍物的威胁,因此需要引入行为优先级机制。例如:当小车前方和左侧同时检测到障碍物时,应优先处理前方障碍。

行为优先级表
行为类型 优先级
停止 5
后退 4
左转 3
右转 2
前进 1
示例代码:行为优先级判断
typedef enum {
    NONE = 0,
    MOVE_FORWARD = 1,
    TURN_RIGHT = 2,
    TURN_LEFT = 3,
    REVERSE = 4,
    STOP = 5
} Behavior;

Behavior decide_behavior(uint8_t front, uint8_t left, uint8_t right) {
    if (front < OBSTACLE_THRESHOLD) return STOP;
    if (left < OBSTACLE_THRESHOLD) return TURN_RIGHT;
    if (right < OBSTACLE_THRESHOLD) return TURN_LEFT;
    return MOVE_FORWARD;
}
代码逻辑分析与参数说明:
  • Behavior 枚举定义了五种行为类型,数字越小优先级越低。
  • decide_behavior 函数接收三个方向的距离值,按优先级顺序判断并返回应执行的行为。
  • 在逻辑中,首先判断前方是否被堵,若被堵则立即停止,否则再判断左右方向。

5.3.2 基于状态机的避障逻辑设计

状态机是一种非常有效的控制逻辑设计方式,适用于多状态切换的系统。在避障系统中,我们可以将小车的状态分为以下几种:

  • 前进状态(FORWARD) :正常移动
  • 避障状态(AVOIDING) :检测到障碍,执行避障动作
  • 后退状态(REVERSING) :避障失败后倒退
  • 转向状态(TURNING) :执行左转或右转
stateDiagram
    [*] --> FORWARD
    FORWARD --> AVOIDING : 检测到障碍
    AVOIDING --> REVERSING : 避障失败
    AVOIDING --> FORWARD : 避障成功
    REVERSING --> TURNING : 倒退完成
    TURNING --> FORWARD : 转向完成
状态机说明:
  • 系统初始状态为前进。
  • 若检测到障碍,进入避障状态。
  • 若避障失败(如左右都被堵),则进入后退状态。
  • 后退完成后进入转向状态,转向后重新进入前进状态。
示例代码:状态机逻辑实现
typedef enum {
    FORWARD,
    AVOIDING,
    REVERSING,
    TURNING
} State;

State current_state = FORWARD;

void update_state(Behavior behavior) {
    switch(current_state) {
        case FORWARD:
            if (behavior == STOP) {
                current_state = AVOIDING;
            }
            break;
        case AVOIDING:
            if (behavior == REVERSE) {
                current_state = REVERSING;
            } else if (behavior == MOVE_FORWARD) {
                current_state = FORWARD;
            }
            break;
        case REVERSING:
            current_state = TURNING;
            break;
        case TURNING:
            current_state = FORWARD;
            break;
    }
}
代码逻辑分析与参数说明:
  • update_state 函数根据当前行为决定状态切换。
  • FORWARD 状态下,若检测到障碍( behavior == STOP ),切换为 AVOIDING
  • AVOIDING 状态下,根据避障结果切换到 REVERSING 或返回 FORWARD
  • 状态切换逻辑清晰,便于扩展和维护。

本章系统地讲解了避障策略的设计与实现,包括障碍物识别、行为决策、阈值设定、路径规划以及状态机控制等内容。通过结合STM32平台和超声波传感器,我们构建了一个具备多方向探测能力和智能决策能力的避障系统。下一章将介绍电机驱动与PWM控制,实现小车的运动执行部分。

6. 电机驱动与PWM控制

本章将围绕智能小车中电机驱动与控制的核心技术展开,重点讲解如何利用STM32微控制器通过PWM(脉宽调制)信号对直流电机进行精确的速度和方向控制。同时,将介绍主流的电机驱动芯片选型原则,并结合避障系统实现电机控制策略的联动设计。

6.1 直流电机驱动芯片选型与特性

在智能小车系统中,电机作为执行部件,其驱动方式直接影响整车的性能表现。常见的直流电机驱动芯片包括L298N和TB6612FNG,它们各自具备不同的性能特点与适用场景。

6.1.1 L298N与TB6612FNG的对比分析

参数 L298N TB6612FNG
工作电压 最高46V 最高13.5V
最大输出电流 2A/通道(带散热) 1.2A/通道(持续)
集成H桥
封装形式 Multiwatt15 SSOP24
散热需求
控制方式 PWM + 方向引脚 PWM + 方向引脚
内置保护功能 过热/过流自动断电

从上表可以看出,L298N适合高电压、大功率应用,但效率较低且需要额外散热;而TB6612FNG则更适合低电压、中功率场景,具有更优的集成度和保护机制,适用于智能小车等嵌入式平台。

6.1.2 驱动电路设计与保护措施

以TB6612FNG为例,其典型应用电路如下:

graph TD
    A[STM32] -->|PWM1| B[TB6612FNG]
    A -->|DIR1| B
    A -->|STBY| B
    B -->|OUT1| C[Motor A]
    B -->|OUT2| C
    D[电源Vcc] --> B
    E[GND] --> B

关键引脚说明:

  • AIN1 / AIN2 :方向控制引脚
  • PWMA :PWM输入引脚,用于控制速度
  • STBY :待机控制引脚,高电平使能驱动
  • AO1 / AO2 :电机输出端口

保护措施建议:

  • 加装反向电动势吸收二极管
  • 电源端加装滤波电容(100uF + 0.1uF并联)
  • 使用散热片或强制风冷(视电流需求)

6.2 PWM信号生成与速度调节

STM32微控制器内置多个通用定时器(TIMx),可用于生成高精度的PWM信号,实现对电机转速的精准控制。

6.2.1 STM32定时器PWM模式配置

以下为基于STM32 HAL库配置TIM3生成PWM信号的示例代码:

// 定义定时器句柄
TIM_HandleTypeDef htim3;

// 初始化PWM通道
void MX_TIM3_Init(void)
{
    __HAL_RCC_TIM3_CLK_ENABLE();

    htim3.Instance = TIM3;
    htim3.Init.Prescaler = 83;         // 分频系数,84MHz / (83+1) = 1MHz
    htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim3.Init.Period = 999;           // 自动重载值,1MHz / (999+1) = 1kHz
    htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

// 设置占空比(0~100)
void set_motor_speed(uint8_t duty)
{
    uint32_t pulse = (htim3.Instance->ARR * duty) / 100;
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pulse);
}

参数说明:

  • Prescaler :定时器时钟预分频器,用于降低计数频率
  • Period :计数周期,决定PWM频率
  • pulse :比较寄存器值,决定占空比

执行逻辑说明:

  1. 系统时钟为84MHz,通过预分频得到1MHz的定时器时钟
  2. 设置ARR(自动重载寄存器)为999,则PWM周期为1ms(频率1kHz)
  3. 改变CCR(捕获比较寄存器)的值即可调节占空比,实现速度控制

6.2.2 占空比调节与电机响应特性

电机的转速与PWM占空比呈近似线性关系,但在实际应用中需考虑以下因素:

  • 死区时间 :电机启动时存在惯性,低占空比下可能无法转动
  • 非线性响应 :不同电机的机械特性会导致响应曲线略有差异
  • 负载影响 :负载变化会影响实际转速,需配合反馈机制进行补偿

6.3 电机控制策略与避障联动

在智能小车系统中,电机控制不仅需要完成前进、后退、转向等基本动作,还需与避障系统联动,实现自主导航与路径调整。

6.3.1 差速控制与转向实现

差速控制是通过调节左右电机的不同转速来实现转向。例如:

左电机占空比 右电机占空比 转向行为
100% 100% 直行
80% 100% 向左偏转
100% 80% 向右偏转
100% -100% 原地左转
-100% 100% 原地右转

代码示例(简化):

void turn_left_smoothly() {
    set_left_motor_speed(80);
    set_right_motor_speed(100);
}

6.3.2 实时速度反馈与闭环调节

为提高小车的运动稳定性,可引入编码器或霍尔传感器实现速度反馈,构建闭环控制系统。例如使用PID控制器:

float pid_control(float target_speed, float current_speed) {
    float error = target_speed - current_speed;
    integral += error;
    float derivative = error - last_error;
    last_error = error;

    return Kp * error + Ki * integral + Kd * derivative;
}

通过实时调节PWM占空比,使电机速度快速响应目标值,提升小车在避障过程中的动态响应能力。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:STM32超声波避障小车是一种基于ARM Cortex-M内核微控制器的智能移动机器人系统,利用HC-SR04等超声波传感器探测障碍物,并通过STM32进行数据处理和避障决策。该设计涵盖微控制器编程、传感器数据采集、电机控制、避障算法实现及电源管理等多个关键技术环节。适用于嵌入式系统学习与机器人开发实践,帮助学生掌握从硬件搭建到软件调试的完整开发流程。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐