1. 项目概述
这个超声波测距实验是我在智能硬件开发过程中经常用到的基础模块。通过定时器和超声波传感器的组合,我们可以实现非接触式的距离测量,精度能达到厘米级。这种方案成本低廉(整套硬件不到50元)、响应速度快(测量周期仅需几十毫秒),非常适合机器人避障、液位检测、停车辅助等场景。
我最早接触这个方案是在做一个自动跟随行李箱项目时,需要实时检测人与箱体之间的距离。当时试过红外、激光等多种传感器,最终发现HC-SR04超声波模块是最经济实惠的选择。经过多次迭代优化,现在这套测量系统在2cm-400cm范围内误差可以控制在±1cm以内。
2. 硬件准备与电路连接
2.1 核心器件选型
HC-SR04超声波模块是这个实验的核心部件,市场价格约8-15元。它包含超声波发射器、接收器和控制电路,工作电压5V,静态电流小于2mA。相比更昂贵的US-100等模块,HC-SR04虽然缺少温度补偿功能,但在室内环境下完全够用。
定时器我推荐使用STM32的通用定时器(如TIM2),因为:
- 输入捕获功能可以直接测量高电平持续时间
- 72MHz主频能提供微秒级计时精度
- 硬件计时不占用CPU资源
如果使用Arduino平台,可以用micros()函数实现软件定时,但要注意其精度会受中断影响。
2.2 电路连接示意图
code复制VCC ---- 5V
Trig -- PA1 (任意GPIO)
Echo -- PA0 (需支持输入捕获)
GND ---- GND
重要提示:Echo引脚输出电压也是5V,如果连接3.3V单片机必须分压!我常用1kΩ+2kΩ电阻分压,将5V降至约3.3V。
3. 测距原理深度解析
3.1 超声波飞行时间法
模块工作时序分为三个阶段:
- 触发阶段:给Trig引脚10μs以上高电平
- 发射阶段:模块自动发送8个40kHz超声波脉冲
- 回波阶段:Echo引脚输出高电平,其持续时间与距离成正比
距离计算公式:
code复制距离(cm) = 高电平时间(μs) × 声速(340m/s) / 2 / 10000
除以2是因为声波需要往返,10000是单位转换系数。
3.2 温度补偿改进
标准声速340m/s是在15℃下的值,实际声速会随温度变化:
code复制V = 331.4 + 0.607×T (T为摄氏温度)
我在室外项目中使用DS18B20温度传感器进行补偿,将误差从±3cm降低到±1cm。
4. STM32硬件定时器实现
4.1 定时器配置
以STM32F103的TIM2为例:
c复制// 定时器初始化
void TIM2_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; // 最大计数值
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // 1MHz计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 输入捕获配置
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
4.2 距离测量流程
- 触发测量:
c复制HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
delay_us(20);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
- 捕获回波:
c复制// 等待上升沿
while(!TIM_GetFlagStatus(TIM2, TIM_FLAG_CC1));
TIM_SetCounter(TIM2, 0);
// 等待下降沿
while(TIM_GetFlagStatus(TIM2, TIM_FLAG_CC1));
// 计算时间
uint32_t pulse_width = TIM_GetCapture1(TIM2);
float distance = pulse_width * 0.034 / 2;
5. Arduino软件实现方案
对于没有硬件定时器的平台,可以用micros()实现:
arduino复制long getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH);
return duration * 0.034 / 2;
}
实测发现:Arduino的pulseIn()分辨率约4μs,在50cm内误差±1cm,远距离时误差会增大到±3cm。
6. 精度优化技巧
6.1 数字滤波算法
我常用的三重滤波方案:
- 连续采样5次,去掉最大最小值
- 剩余3次取平均
- 与上次有效值做加权平均(权重0.3:0.7)
c复制#define SAMPLE_NUM 5
float filtered_distance(float new_dist) {
static float last_valid = 0;
float buffer[SAMPLE_NUM];
// 采样
for(int i=0; i<SAMPLE_NUM; i++){
buffer[i] = getDistance();
delay(30); // 防止余震干扰
}
// 排序去极值
sort(buffer, SAMPLE_NUM);
float sum = 0;
for(int i=1; i<SAMPLE_NUM-1; i++){
sum += buffer[i];
}
float current = sum / (SAMPLE_NUM-2);
// 加权滤波
last_valid = last_valid*0.7 + current*0.3;
return last_valid;
}
6.2 安装注意事项
- 传感器与被测面夹角最好≤15°
- 避免测量绒毛、海绵等吸声材料
- 最小检测距离2cm,实际建议5cm以上
- 多个模块同时工作时需错开触发时间
7. 典型问题排查
7.1 常见故障现象
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 始终返回0 | Echo引脚接触不良 | 检查接线,确保分压电路正常 |
| 数值波动大 | 电源干扰 | 增加10μF电容稳压 |
| 测量值偏小 | 温度影响 | 添加温度补偿算法 |
| 无响应 | Trig信号不足10μs | 用示波器检查触发脉冲 |
7.2 调试技巧
- 用示波器同时观察Trig和Echo信号
- 标准测试:30cm处放置障碍物,Echo高电平应≈1.76ms
- 在代码中添加原始脉冲宽度打印,便于分析
8. 进阶应用案例
8.1 三维空间定位
布置三个超声波模块,通过三角测量法实现空间定位。关键点:
- 模块间距建议50-100cm
- 采用时分复用避免干扰
- 加入RSSI判断信号强度
8.2 液位监测系统
我在化工项目中的实施经验:
- 使用防水型超声波传感器(如JSN-SR04T)
- 安装时加装导波管减少液面波动影响
- 设置死区屏蔽容器壁反射
这个超声波测距方案我已经在十多个项目中验证过稳定性。最近一次是在智能仓储机器人上,20个传感器连续工作半年无故障。关键是要做好电源滤波和机械防护,避免振动导致接线松动。