4、超声波传感器实战:典型型号(如HC-SR04)的接口、驱动代码、数据读取与滤波处理
超声波测距是机器人避障系统中成本最低、应用最广泛的感知方案之一。HC-SR04 作为入门级典型型号,其工作原理基于声波在空气中的传播时间(Time of Flight, ToF)测量。本章将围绕 HC-SR04 的硬件接口、时序驱动、数据读取与滤波处理展开实战讲解,帮助学习者建立从传感器原始信号到可用距离数据的完整处理链路。
4.1 HC-SR04 硬件接口与电气特性
HC-SR04 是一款集成了超声波发射器、接收器与控制电路的测距模块,工作电压为 5V,静态电流小于 2mA。模块对外提供 4 个引脚:VCC(电源正极)、GND(电源地)、Trig(触发信号输入)和 Echo(回波信号输出)。其测距范围为 2cm~400cm,测量精度可达 3mm,波束角约为 15°。
在接口设计上,Trig 引脚为数字输入,接受来自 MCU 的 TTL 电平脉冲;Echo 引脚为数字输出,输出高电平持续时间与距离成正比。需要注意的是,Echo 输出高电平的电压为 5V,若 MCU 的 GPIO 耐压为 3.3V(如 ESP32、STM32 的部分引脚),必须使用电阻分压或电平转换电路,否则可能损坏 MCU 引脚。
| 引脚名称 | 类型 | 电气特性 | 连接目标 |
|---|---|---|---|
| VCC | 电源 | 5V DC,纹波 < 50mV | 5V 电源轨 |
| GND | 地 | 0V | 系统地 |
| Trig | 输入 | TTL 高电平触发,≥10µs 脉冲 | MCU GPIO(3.3V/5V 兼容) |
| Echo | 输出 | 高电平 5V,低电平 0V | MCU GPIO(需电平转换) |
4.2 驱动时序与距离计算原理
HC-SR04 的测距时序分为四个阶段:触发、发射、回波、输出。MCU 首先向 Trig 引脚发送一个 ≥10µs 的高电平脉冲,模块内部自动产生 8 个 40kHz 的超声波脉冲并发射。声波遇到障碍物后反射回来,被接收器捕获,模块随即在 Echo 引脚输出一个高电平脉冲,其持续时间即为声波往返的时间。
距离计算公式为:
距离 (cm) = Echo 高电平时间 (µs) / 58
或
距离 (cm) = Echo 高电平时间 (µs) × 0.017
其中 58 的推导过程为:声速在 20°C 空气中约为 343m/s,即 0.0343cm/µs。声波往返距离为 2 倍目标距离,因此单程距离 = 时间 × 声速 / 2 = 时间 × 0.0343 / 2 ≈ 时间 / 58.3。为便于计算,工程上常取 58 或 58.2 作为换算系数。
声速 = 331.4 + 0.6 × T(°C)。
4.3 驱动代码实现(以 STM32 和 Arduino 为例)
以下分别给出 Arduino 和 STM32 两种主流平台的驱动代码。Arduino 版本适合快速原型验证,STM32 版本适合嵌入式产品开发。
4.3.1 Arduino 驱动代码(阻塞式)
#define TRIG_PIN 9
#define ECHO_PIN 10
void setup() {
Serial.begin(9600);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
}
void loop() {
// 发送 10µs 触发脉冲
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// 读取 Echo 高电平持续时间
long duration = pulseIn(ECHO_PIN, HIGH);
// 计算距离
float distance = duration / 58.0;
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
delay(100); // 测量周期 100ms
}
4.3.2 STM32 驱动代码(中断+定时器输入捕获)
// 使用 TIM2_CH1 (PA0) 作为 Echo 输入捕获
// 使用 PA1 作为 Trig 输出
void HC_SR04_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
TIM_IC_InitTypeDef TIM_ICInitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_TIM2_CLK_ENABLE();
// Trig 引脚配置
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Echo 引脚配置(复用为 TIM2_CH1)
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 定时器配置:72MHz / 72 = 1MHz,即 1µs 计数
TIM_Handle.Instance = TIM2;
TIM_Handle.Init.Period = 0xFFFFFFFF;
TIM_Handle.Init.Prescaler = 72 - 1;
TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_IC_Init(&TIM_Handle);
// 输入捕获配置:上升沿触发
TIM_ICInitStruct.Channel = TIM_CHANNEL_1;
TIM_ICInitStruct.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
TIM_ICInitStruct.ICSelection = TIM_ICSELECTION_DIRECTTI;
TIM_ICInitStruct.ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&TIM_Handle, &TIM_ICInitStruct, TIM_CHANNEL_1);
// 启动捕获
HAL_TIM_IC_Start_IT(&TIM_Handle, TIM_CHANNEL_1);
// 使能中断
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
volatile uint32_t echo_start = 0;
volatile uint32_t echo_end = 0;
volatile uint8_t echo_flag = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
if (echo_flag == 0) {
echo_start = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
echo_flag = 1;
} else {
echo_end = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
echo_flag = 0;
echo_duration = echo_end - echo_start; // 单位 µs
}
}
}
pulseIn() 函数(其精度受中断延迟影响,通常有 5~10µs 误差)。
4.4 数据读取与滤波处理
超声波传感器在实际使用中常受到多种噪声干扰,包括:多径反射(声波经墙壁多次反射)、环境噪声(其他超声波源)、温度梯度(声速变化)、机械振动(传感器安装松动)等。因此,原始测量数据必须经过滤波处理才能用于避障决策。
4.4.1 常见滤波算法对比
| 滤波方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 限幅滤波 | 相邻两次差值超过阈值则丢弃 | 简单高效,实时性好 | 无法处理连续漂移 | 快速剔除突变值 |
| 中值滤波 | 取 N 次采样中间值 | 抗脉冲干扰强 | 延迟较大,需排序 | 静态或慢速场景 |
| 滑动均值 | N 次采样取平均 | 平滑效果好 | 对突变响应慢 | 移动机器人避障 |
| 加权滑动均值 | 近期数据权重更高 | 兼顾平滑与响应 | 参数需调优 | 动态避障 |
| 卡尔曼滤波 | 状态估计+噪声协方差 | 最优估计,自适应 | 计算量大,调参复杂 | 高精度定位 |
4.4.2 工程级滤波代码示例
// 滑动窗口中值+均值组合滤波
#define FILTER_SIZE 7
float distance_buffer[FILTER_SIZE];
uint8_t buffer_index = 0;
// 插入新数据
void filter_insert(float value) {
distance_buffer[buffer_index] = value;
buffer_index = (buffer_index + 1) % FILTER_SIZE;
}
// 中值滤波
float median_filter(void) {
float temp[FILTER_SIZE];
memcpy(temp, distance_buffer, sizeof(temp));
// 冒泡排序
for (int i = 0; i < FILTER_SIZE - 1; i++) {
for (int j = 0; j < FILTER_SIZE - 1 - i; j++) {
if (temp[j] > temp[j + 1]) {
float t = temp[j];
temp[j] = temp[j + 1];
temp[j + 1] = t;
}
}
}
return temp[FILTER_SIZE / 2];
}
// 限幅+均值组合滤波
float distance_filtered = 0;
float last_valid = 0;
float limit_average_filter(float new_value) {
// 限幅:最大变化量 20cm
if (fabs(new_value - last_valid) > 20.0f) {
return last_valid; // 丢弃突变值
}
// 滑动均值
filter_insert(new_value);
float sum = 0;
for (int i = 0; i < FILTER_SIZE; i++) {
sum += distance_buffer[i];
}
distance_filtered = sum / FILTER_SIZE;
last_valid = distance_filtered;
return distance_filtered;
}
4.5 实战案例:移动机器人前向避障系统
下面通过一个完整的案例,展示 HC-SR04 在移动机器人避障中的实际应用。
初始现象与问题:在初步测试中,机器人频繁出现误判——在无障碍物的空旷区域突然停车,或在距离障碍物 50cm 处才触发避障。通过串口日志分析发现,原始距离数据存在大量跳变:
距离(cm): 45.2, 44.8, 12.3, 45.5, 46.1, 120.4, 44.9, 45.0
其中 12.3cm 和 120.4cm 为明显异常值。进一步排查发现,12.3cm 的异常源于机器人前方地面的金属反光板造成的多径反射;120.4cm 的异常则是因为超声波波束边缘扫到了侧方墙壁。
分析与处理过程:
- 硬件检查:确认传感器安装牢固,无机械振动;检查电源纹波,发现 5V 电源在电机启动时跌落至 4.6V,导致测量不稳定。解决方案:在传感器电源引脚并联 100µF 电解电容和 0.1µF 陶瓷电容。
- 软件滤波:采用"限幅滤波 + 滑动中值滤波"组合方案。限幅阈值设为 30cm(相邻两次测量差值超过 30cm 则丢弃),滑动窗口大小设为 5。
- 测量周期优化:将测量周期从 50ms 调整为 100ms,避免与电机 PWM 频率产生谐振干扰。
- 多传感器融合:在机器人左右两侧各增加一个 HC-SR04,采用"三传感器投票"策略——当至少两个传感器同时检测到障碍物时才触发避障。
结果对比:
| 指标 | 滤波前 | 滤波后 | 提升幅度 |
|---|---|---|---|
| 误触发率(次/分钟) | 8~12 | 0~1 | 降低 90%+ |
| 距离测量标准差 (cm) | ±4.2 | ±0.8 | 提升 81% |
| 避障响应延迟 (ms) | 150 | 200 | 延迟增加 50ms(可接受) |
风险与代价:滤波处理引入了约 50ms 的额外延迟,对于高速移动的机器人(>1m/s)可能造成制动距离不足。解决方案是提高传感器采样频率(从 10Hz 提升至 20Hz),同时减小滑动窗口大小至 3,以平衡响应速度与滤波效果。
复盘经验:
- 超声波传感器的异常值往往具有"物理根源",单纯依靠软件滤波无法根治,需结合硬件改进(电源滤波、安装减震)。
- 滤波参数的选择应基于实际场景的噪声特性,而非盲目套用经验值。建议先采集 1000 组原始数据,统计分析噪声分布后再确定限幅阈值和窗口大小。
- 多传感器融合是提升系统鲁棒性的有效手段,但会增加成本和复杂度,需根据项目预算权衡。
4.6 常见误区与总结
在实际工程中,HC-SR04 的使用存在以下常见误区:
- 误区一:认为 HC-SR04 可以测量 4m 以上的距离。实际上,在 4m 处回波信号极弱,且易受环境噪声干扰,可靠测量范围通常不超过 3m。
- 误区二:忽略波束角的影响。HC-SR04 的波束角约 15°,在 1m 距离处波束覆盖直径约 26cm,可能检测到侧方障碍物导致误判。
- 误区三:将 Echo 引脚直接连接 3.3V MCU 的 GPIO。长期运行可能导致 MCU 引脚损坏,应使用分压电阻(如 1kΩ 串联 + 2kΩ 对地)进行电平转换。
- 误区四:在电机启动时进行测量。电机产生的电磁干扰和电源跌落会严重影响测量精度,应在电机停稳或使用独立电源供电时测量。
本章总结:HC-SR04 作为入门级超声波传感器,其接口简单、驱动容易,但要在实际避障系统中获得稳定可靠的测距数据,必须重视硬件设计(电源滤波、电平转换、安装方式)和软件滤波(限幅、中值、滑动均值)的配合。通过本章的学习,学习者应能够独立完成 HC-SR04 的硬件连接、驱动编写、数据采集与滤波处理,并具备排查常见故障的能力。