znlgis 博客

GIS开发与技术分享

第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 本章小结

本章介绍了超声波测距系统:

  1. 测距原理
    • 声波往返时间测量
    • 距离 = 声速 × 时间 / 2
  2. 代码实现
    • 触发脉冲发送
    • 回波时间测量
    • 超时保护
    • 滤波处理
  3. 应用场景
    • 倒车雷达
    • 液位检测
    • 智能设备

下一章将学习 LCD 显示模块。