第21章:超声波测距系统
本章讲解 HC-SR04 超声波传感器的工作原理、测距算法与应用案例。
21.1 超声波测距原理
21.1.1 工作原理
超声波传感器通过测量声波往返时间计算距离:
发射器 ─────→ 超声波 ─────→ 障碍物
↑ │
│ │
└──── 反射 ←───┘
距离 = 声速 × 时间 / 2
21.1.2 HC-SR04 参数
| 参数 | 数值 |
|---|---|
| 工作电压 | 5V (3.3V 版可用) |
| 工作频率 | 40kHz |
| 测量范围 | 2cm - 400cm |
| 测量精度 | 约 3mm |
| 测量角度 | 约 15° |
21.1.3 引脚定义
HC-SR04:
┌─────────────┐
│ VCC TRIG ECHO GND │
└─────────────┘
│ │ │ │
5V 触发 回波 地
21.2 测距时序
21.2.1 工作流程
1. 主机发送 10μs 以上高电平触发信号
2. 模块发出 8 个 40kHz 超声波脉冲
3. 模块等待回波
4. ECHO 引脚输出高电平,持续时间=声波往返时间
5. 主机测量 ECHO 高电平时间,计算距离
21.2.2 时序图
TRIG: ──┐ ┌──────────────────────
│ │
└──┘ 10μs+
ECHO: ──────────┐ ┌──
│ 高电平时间 │
└──────────────┘
= 往返时间
21.3 测距代码
21.3.1 基础测距
'''
实验:超声波测距
接线:TRIG→GP14, ECHO→GP13
'''
from machine import Pin
import utime
trigger = Pin(14, Pin.OUT)
echo = Pin(13, Pin.IN)
def get_distance():
"""测量距离(厘米)"""
# 发送触发脉冲
trigger.low()
utime.sleep_us(2)
trigger.high()
utime.sleep_us(10)
trigger.low()
# 等待回波开始
while echo.value() == 0:
start = utime.ticks_us()
# 等待回波结束
while echo.value() == 1:
end = utime.ticks_us()
# 计算距离
duration = utime.ticks_diff(end, start)
# 声速 343.2 m/s = 0.0343 cm/μs
distance = duration * 0.0343 / 2
return distance
while True:
dist = get_distance()
print(f"距离: {dist:.2f} cm")
utime.sleep(0.5)
21.3.2 带超时保护
'''
实验:带超时保护的测距
'''
def get_distance_safe(timeout_us=30000):
"""带超时的测距"""
trigger.low()
utime.sleep_us(2)
trigger.high()
utime.sleep_us(10)
trigger.low()
# 等待回波开始(带超时)
start_wait = utime.ticks_us()
while echo.value() == 0:
if utime.ticks_diff(utime.ticks_us(), start_wait) > timeout_us:
return -1 # 超时
start = utime.ticks_us()
# 等待回波结束(带超时)
while echo.value() == 1:
if utime.ticks_diff(utime.ticks_us(), start) > timeout_us:
return -2 # 超出量程
end = utime.ticks_us()
duration = utime.ticks_diff(end, start)
distance = duration * 0.0343 / 2
return distance
while True:
dist = get_distance_safe()
if dist == -1:
print("超时:无回波")
elif dist == -2:
print("超出量程")
else:
print(f"距离: {dist:.2f} cm")
utime.sleep(0.5)
21.3.3 滤波处理
'''
实验:中值滤波测距
'''
def get_distance_filtered(samples=5):
"""中值滤波"""
readings = []
for _ in range(samples):
d = get_distance_safe()
if d > 0:
readings.append(d)
utime.sleep_ms(60) # 采样间隔
if not readings:
return -1
readings.sort()
return readings[len(readings) // 2]
while True:
dist = get_distance_filtered(5)
if dist > 0:
print(f"距离: {dist:.2f} cm")
else:
print("测量失败")
utime.sleep(0.5)
21.4 距离可视化
21.4.1 LED 条显示
'''
实验:距离 LED 条显示
'''
from machine import Pin
import neopixel
np = neopixel.NeoPixel(Pin(16), 8)
def distance_to_leds(dist):
"""距离映射到 LED"""
max_dist = 100 # 最大显示距离 (cm)
if dist < 0:
# 错误状态 - 全红闪烁
np.fill((255, 0, 0))
else:
# 距离越近,亮的 LED 越多
lit = int((1 - min(dist, max_dist) / max_dist) * 8)
for i in range(8):
if i < lit:
if lit > 6:
np[i] = (255, 0, 0) # 危险 - 红
elif lit > 4:
np[i] = (255, 255, 0) # 警告 - 黄
else:
np[i] = (0, 255, 0) # 安全 - 绿
else:
np[i] = (0, 0, 0)
np.write()
while True:
dist = get_distance_filtered()
distance_to_leds(dist)
print(f"距离: {dist:.1f} cm")
utime.sleep(0.1)
21.4.2 声音反馈
'''
实验:倒车雷达(蜂鸣器频率变化)
'''
from machine import Pin, PWM
buzzer = PWM(Pin(21))
def distance_to_beep(dist):
"""距离映射到蜂鸣频率"""
if dist < 0 or dist > 200:
buzzer.duty_u16(0)
return
if dist < 10:
# 很近 - 持续响
buzzer.freq(2000)
buzzer.duty_u16(5000)
elif dist < 30:
# 近 - 快速间歇
buzzer.freq(1500)
buzzer.duty_u16(3000)
utime.sleep_ms(50)
buzzer.duty_u16(0)
utime.sleep_ms(50)
elif dist < 60:
# 中等 - 慢速间歇
buzzer.freq(1000)
buzzer.duty_u16(2000)
utime.sleep_ms(100)
buzzer.duty_u16(0)
utime.sleep_ms(200)
else:
# 远 - 不响
buzzer.duty_u16(0)
while True:
dist = get_distance_safe()
distance_to_beep(dist)
21.5 应用案例
21.5.1 液位检测
'''
实验:水箱液位监测
'''
TANK_HEIGHT = 100 # 水箱高度 (cm)
SENSOR_OFFSET = 5 # 传感器距顶部距离 (cm)
def get_water_level():
"""获取水位百分比"""
dist = get_distance_filtered()
if dist < 0:
return -1
# 水位 = 总高度 - 空气层高度
water_height = TANK_HEIGHT - (dist - SENSOR_OFFSET)
water_percent = max(0, min(100, water_height / TANK_HEIGHT * 100))
return water_percent
while True:
level = get_water_level()
if level >= 0:
bar = '█' * int(level / 5)
print(f"水位: {level:.1f}% |{bar}|")
else:
print("测量错误")
utime.sleep(1)
21.5.2 智能垃圾桶
'''
实验:智能垃圾桶(自动开盖)
'''
from machine import PWM
servo = PWM(Pin(16))
servo.freq(50)
def set_lid(open_state):
"""控制垃圾桶盖"""
if open_state:
duty = 8192 # 开盖 (约 180°)
else:
duty = 1638 # 关盖 (约 0°)
servo.duty_u16(duty)
lid_open = False
close_timer = 0
while True:
dist = get_distance_safe()
if 5 < dist < 30: # 检测到靠近
if not lid_open:
set_lid(True)
lid_open = True
print("开盖")
close_timer = utime.ticks_ms()
else:
# 3 秒无人后关盖
if lid_open and utime.ticks_diff(utime.ticks_ms(), close_timer) > 3000:
set_lid(False)
lid_open = False
print("关盖")
utime.sleep_ms(100)
21.6 温度补偿
21.6.1 声速与温度关系
声速随温度变化:
v = 331.3 + 0.606 × T
T: 温度 (°C)
v: 声速 (m/s)
21.6.2 温度补偿代码
'''
实验:温度补偿测距
'''
def get_distance_compensated(temperature=25):
"""温度补偿的距离测量"""
# 计算声速 (cm/μs)
speed_of_sound = (331.3 + 0.606 * temperature) / 10000
trigger.low()
utime.sleep_us(2)
trigger.high()
utime.sleep_us(10)
trigger.low()
while echo.value() == 0:
start = utime.ticks_us()
while echo.value() == 1:
end = utime.ticks_us()
duration = utime.ticks_diff(end, start)
distance = duration * speed_of_sound / 2
return distance
# 假设温度 30°C
dist_25 = get_distance_safe()
dist_30 = get_distance_compensated(30)
print(f"25°C 默认: {dist_25:.2f} cm")
print(f"30°C 补偿: {dist_30:.2f} cm")
21.7 本章小结
本章介绍了超声波测距系统:
- 测距原理:
- 声波往返时间测量
- 距离 = 声速 × 时间 / 2
- 代码实现:
- 触发脉冲发送
- 回波时间测量
- 超时保护
- 滤波处理
- 应用场景:
- 倒车雷达
- 液位检测
- 智能设备
下一章将学习 LCD 显示模块。