【CanMV K210】传感器实验 HC-SR04 超声波测距与状态判断

在智能硬件项目中,距离检测是一类非常常见的基础能力。自动避障小车需要判断前方是否存在障碍物,智能垃圾桶需要感知物体靠近,仓储设备需要检测物体位置,教学实验中也常用距离数据来演示"传感器如何把真实世界转换成程序变量"。这些场景背后的核心逻辑并不复杂:硬件负责采集环境变化,程序负责读取数据、计算结果并输出判断。

本实验使用 CanMV K210 开发板连接 HC-SR04 超声波测距模块,通过 Trig 引脚发送触发脉冲,通过 Echo 引脚接收回波信号,并根据 Echo 高电平持续时间换算距离。程序会持续输出当前距离,并按照距离范围显示"距离过近""正常检测""距离较远""超出常用检测范围"等状态。

学习目标 说明
理解超声波测距原理 认识 Trig 触发、Echo 回波和声波往返时间之间的关系
掌握 GPIO 输入输出 使用 GPIOHS 输出触发脉冲,并读取 Echo 输入电平
学会微秒级计时 使用 ticks_us() 记录 Echo 高电平持续时间
完成距离换算 将回波时间转换成厘米或毫米距离
建立传感器处理流程 把测距、异常处理、平均滤波和状态判断组合成完整实验

这段代码的价值不只是完成一次测距,而是把传感器驱动、GPIO 输入输出、脉冲计时、异常处理、数据平均和状态判断整合成一个完整实验。对于硬件编程入门而言,这类代码可以直观看到 Python 如何读取真实环境数据,并把物理距离转换成可判断、可输出、可扩展的程序状态。

文章目录

理论基础

HC-SR04 超声波测距模块的工作过程可以理解成一次"发出声音并等待回声"的过程。程序通过 Trig 引脚给模块一个短暂高电平脉冲,模块开始发射超声波;超声波遇到障碍物后反射回来,Echo 引脚会输出一个高电平脉冲。这个高电平持续时间越长,说明声波往返距离越远;持续时间越短,说明障碍物越近。

程序读取到的并不是距离本身,而是 Echo 高电平持续了多少微秒。由于超声波从模块发出后,需要到达障碍物再反射回来,所以 Echo 时间对应的是"往返时间"。计算单程距离时,需要把时间除以 2,再结合声速进行换算。代码中的 distance_cm() 使用 (pulse_time / 2) / 29.1 计算厘米距离,表示声波传播约 29.1 微秒对应 1 厘米。

超声波测距实验比普通按键实验多了计时过程。按键实验只需要判断某个 GPIO 是高电平还是低电平,而 HC-SR04 需要先输出触发脉冲,再等待 Echo 从低电平变成高电平,接着记录 Echo 高电平持续时间,最后把时间转换成距离。这个过程把 GPIO 输出、GPIO 输入、微秒计时和数学换算连接到了一起。
#mermaid-svg-QebVc3BAwnl3GuUP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QebVc3BAwnl3GuUP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QebVc3BAwnl3GuUP .error-icon{fill:#552222;}#mermaid-svg-QebVc3BAwnl3GuUP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QebVc3BAwnl3GuUP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QebVc3BAwnl3GuUP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QebVc3BAwnl3GuUP .marker.cross{stroke:#333333;}#mermaid-svg-QebVc3BAwnl3GuUP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QebVc3BAwnl3GuUP p{margin:0;}#mermaid-svg-QebVc3BAwnl3GuUP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QebVc3BAwnl3GuUP .cluster-label text{fill:#333;}#mermaid-svg-QebVc3BAwnl3GuUP .cluster-label span{color:#333;}#mermaid-svg-QebVc3BAwnl3GuUP .cluster-label span p{background-color:transparent;}#mermaid-svg-QebVc3BAwnl3GuUP .label text,#mermaid-svg-QebVc3BAwnl3GuUP span{fill:#333;color:#333;}#mermaid-svg-QebVc3BAwnl3GuUP .node rect,#mermaid-svg-QebVc3BAwnl3GuUP .node circle,#mermaid-svg-QebVc3BAwnl3GuUP .node ellipse,#mermaid-svg-QebVc3BAwnl3GuUP .node polygon,#mermaid-svg-QebVc3BAwnl3GuUP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QebVc3BAwnl3GuUP .rough-node .label text,#mermaid-svg-QebVc3BAwnl3GuUP .node .label text,#mermaid-svg-QebVc3BAwnl3GuUP .image-shape .label,#mermaid-svg-QebVc3BAwnl3GuUP .icon-shape .label{text-anchor:middle;}#mermaid-svg-QebVc3BAwnl3GuUP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QebVc3BAwnl3GuUP .rough-node .label,#mermaid-svg-QebVc3BAwnl3GuUP .node .label,#mermaid-svg-QebVc3BAwnl3GuUP .image-shape .label,#mermaid-svg-QebVc3BAwnl3GuUP .icon-shape .label{text-align:center;}#mermaid-svg-QebVc3BAwnl3GuUP .node.clickable{cursor:pointer;}#mermaid-svg-QebVc3BAwnl3GuUP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QebVc3BAwnl3GuUP .arrowheadPath{fill:#333333;}#mermaid-svg-QebVc3BAwnl3GuUP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QebVc3BAwnl3GuUP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QebVc3BAwnl3GuUP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QebVc3BAwnl3GuUP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QebVc3BAwnl3GuUP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QebVc3BAwnl3GuUP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QebVc3BAwnl3GuUP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QebVc3BAwnl3GuUP .cluster text{fill:#333;}#mermaid-svg-QebVc3BAwnl3GuUP .cluster span{color:#333;}#mermaid-svg-QebVc3BAwnl3GuUP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QebVc3BAwnl3GuUP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QebVc3BAwnl3GuUP rect.text{fill:none;stroke-width:0;}#mermaid-svg-QebVc3BAwnl3GuUP .icon-shape,#mermaid-svg-QebVc3BAwnl3GuUP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QebVc3BAwnl3GuUP .icon-shape p,#mermaid-svg-QebVc3BAwnl3GuUP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QebVc3BAwnl3GuUP .icon-shape .label rect,#mermaid-svg-QebVc3BAwnl3GuUP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QebVc3BAwnl3GuUP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QebVc3BAwnl3GuUP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QebVc3BAwnl3GuUP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-QebVc3BAwnl3GuUP .ctrl>*{fill:#E8F3FF!important;stroke:#5B9BFF!important;stroke-width:2px!important;color:#1F3B6D!important;}#mermaid-svg-QebVc3BAwnl3GuUP .ctrl span{fill:#E8F3FF!important;stroke:#5B9BFF!important;stroke-width:2px!important;color:#1F3B6D!important;}#mermaid-svg-QebVc3BAwnl3GuUP .ctrl tspan{fill:#1F3B6D!important;}#mermaid-svg-QebVc3BAwnl3GuUP .sensor>*{fill:#EAFBF1!important;stroke:#42B983!important;stroke-width:2px!important;color:#1F5C3A!important;}#mermaid-svg-QebVc3BAwnl3GuUP .sensor span{fill:#EAFBF1!important;stroke:#42B983!important;stroke-width:2px!important;color:#1F5C3A!important;}#mermaid-svg-QebVc3BAwnl3GuUP .sensor tspan{fill:#1F5C3A!important;}#mermaid-svg-QebVc3BAwnl3GuUP .calc>*{fill:#F4EEFF!important;stroke:#8E6AD8!important;stroke-width:2px!important;color:#3D2B68!important;}#mermaid-svg-QebVc3BAwnl3GuUP .calc span{fill:#F4EEFF!important;stroke:#8E6AD8!important;stroke-width:2px!important;color:#3D2B68!important;}#mermaid-svg-QebVc3BAwnl3GuUP .calc tspan{fill:#3D2B68!important;}#mermaid-svg-QebVc3BAwnl3GuUP .power>*{fill:#FCEEF4!important;stroke:#E76F51!important;stroke-width:2px!important;color:#7A2740!important;}#mermaid-svg-QebVc3BAwnl3GuUP .power span{fill:#FCEEF4!important;stroke:#E76F51!important;stroke-width:2px!important;color:#7A2740!important;}#mermaid-svg-QebVc3BAwnl3GuUP .power tspan{fill:#7A2740!important;}#mermaid-svg-QebVc3BAwnl3GuUP .warn>*{fill:#FFF4E8!important;stroke:#F4A261!important;stroke-width:2px!important;color:#7A4A00!important;}#mermaid-svg-QebVc3BAwnl3GuUP .warn span{fill:#FFF4E8!important;stroke:#F4A261!important;stroke-width:2px!important;color:#7A4A00!important;}#mermaid-svg-QebVc3BAwnl3GuUP .warn tspan{fill:#7A4A00!important;} Python 程序

启动一次测距
CanMV IO6

GPIOHS0 输出
HC-SR04 Trig

10us 高电平触发
超声波发射

遇到目标后反射
HC-SR04 Echo

输出高电平脉冲
CanMV IO7

GPIOHS1 输入计时
时间换算

pulse_time → distance_cm
状态判断

过近 / 正常 / 较远 / 超出范围
5V / GND

模块供电与公共地
Echo 电平保护

分压或电平转换

这张流程图展示的是超声波测距的完整电路和数据流程。CanMV K210 负责输出 Trig 脉冲和读取 Echo 电平,HC-SR04 负责发射和接收超声波,程序负责计算 Echo 高电平时间并换算距离。真正进入应用逻辑的不是 Echo 电平本身,而是经过换算后的厘米距离和状态文本。

需要特别注意的是,HC-SR04 常见模块使用 5V 供电,Echo 输出也可能是 5V 电平,而 CanMV K210 的 IO 通常是 3.3V 逻辑。程序层面可以读取 Echo 状态,但电气保护必须在硬件接线阶段完成。实际实验时,建议在 Echo 与 CanMV IO7 之间加入电阻分压或电平转换模块,降低 5V 信号直接进入开发板 IO 的风险。

硬件设施

本实验围绕 HC-SR04 超声波测距模块和 CanMV K210 的 GPIO 控制能力展开。代码中没有使用 LCD、蜂鸣器、摄像头、按键、电机等模块,因此这些内容不作为本节讲解重点。软件侧主要依赖 timemaix.GPIOfpioa_manager.fm,它们分别负责延时与计时、GPIO 输入输出控制、物理引脚功能映射。

接线关系可以先通过下面这张图片建立整体印象。当前实验真正用到 HC-SR04 的 VCC、GND、Trig 和 Echo 四个接口,其中 Trig 接 CanMV IO6,Echo 接 CanMV IO7,模块供电和开发板需要共地。

硬件 / 软件 作用 说明
CanMV K210 开发板 实验运行平台 负责执行 MicroPython 程序,并通过 GPIO 与超声波模块通信
HC-SR04 超声波模块 距离检测模块 通过 Trig 触发超声波发射,通过 Echo 返回回波脉冲宽度
5V 电源 模块供电 HC-SR04 通常使用 5V 供电,供电不足会影响测距稳定性
GND 公共地 开发板与传感器需要共地,保证信号电平参考一致
IO6 Trig 触发线 代码中作为 TRIGGER_PIN,用于输出 10us 高电平触发信号
IO7 Echo 回波线 代码中作为 ECHO_PIN,用于读取回波高电平持续时间
maix.GPIO GPIO 控制模块 用于创建 Trig 输出对象和 Echo 输入对象
fpioa_manager.fm 引脚映射模块 用于把物理引脚注册为 GPIOHS 功能
time 延时与计时模块 使用 sleepsleep_usticks_us 控制脉冲和计算时间

实验中使用的核心模块如下。HC-SR04 负责测距,CanMV K210 负责输出触发脉冲并读取回波时间。接线时除了关注 IO6 和 IO7,还需要重点检查供电、共地和 Echo 电平保护。

接线关系在代码注释和硬件配置区中已经明确给出。TRIGGER_PIN = 6 表示 HC-SR04 的 Trig 接到 CanMV IO6,ECHO_PIN = 7 表示 Echo 接到 CanMV IO7。类初始化时,程序又通过 fm.register() 将 IO6 注册为 GPIOHS0,将 IO7 注册为 GPIOHS1,进一步完成物理引脚到内部 GPIO 功能的绑定。

物理引脚 / 接口 GPIO 功能 代码变量 对应硬件 说明
5V 电源 HC-SR04 VCC 为超声波模块供电,通常使用 5V
GND 地线 HC-SR04 GND 与 CanMV 开发板共地
IO6 GPIOHS0 self.trigger / TRIGGER_PIN HC-SR04 Trig 负责输出触发脉冲
IO7 GPIOHS1 self.echo / ECHO_PIN HC-SR04 Echo 负责读取回波脉冲
Echo 保护电路 分压或电平转换 Echo 到 IO7 之间 建议把 Echo 电平转换到 3.3V 逻辑范围

物理引脚是开发板上真实连接杜邦线的位置,GPIOHS 是 K210 中用于高速 GPIO 控制的功能编号,代码变量则是程序中操作这些引脚的对象名称。当前程序把 IO6 映射为 GPIOHS0,并封装成 self.trigger,用于输出脉冲信号;把 IO7 映射为 GPIOHS1,并封装成 self.echo,用于读取输入电平。这样写可以把底层引脚细节放在驱动类内部,主循环只需要调用测距函数即可获得距离结果。

完成接线后的整体状态如下。检查时重点关注 HC-SR04 的四根线是否连接正确,尤其是 Trig 和 Echo 不要接反;Echo 接入 CanMV IO7 前建议加入分压或电平转换;被测物体应放在模块正前方,距离不要太近,也不要超过常用检测范围。

实验现象 正常表现 异常提示
程序启动 串口打印实验启动信息和 Trig / Echo 引脚信息 如果无输出,检查程序运行和串口连接
放置近距离物体 串口显示较小厘米数,状态可能为"距离过近" 如果一直无回波,检查 Trig、Echo 和供电
物体在正常范围内 串口显示稳定距离,状态为"正常检测" 如果数值跳动较大,检查目标表面和测量角度
物体较远 串口显示较大距离,状态为"距离较远" 如果反复丢失回波,可能已接近检测极限
超出范围 串口提示未检测到有效回波或超出范围 检查目标是否过远、反射面是否太小或角度是否偏斜
靠近模块 2cm 以内 读数可能异常或被过滤 HC-SR04 存在最小测距盲区,测试时保持合理距离

软件代码

本实验代码围绕 HC-SR04 的测距流程展开。程序把超声波模块封装成 HCSR04 类,类内部负责引脚注册、Trig 脉冲发送、Echo 电平等待、回波时间计算和距离换算。主程序负责周期性读取距离,并根据距离范围输出对应状态。这样的结构把传感器驱动和应用逻辑拆开,后续扩展显示屏、报警提示或自动避障判断时,代码会更容易维护。

软件环境 作用 检查重点
CanMV IDE 编辑、运行和调试 K210 程序 能识别开发板串口,并能正常运行基础 print() 测试
CanMV 固件 提供 maix.GPIOfpioa_managertime 等模块 固件环境需要支持 GPIOHS 输入输出和微秒计时
USB 串口驱动 让电脑识别开发板串口 串口工具中能看到对应端口
串口终端 查看测距结果 能看到距离数值和状态文本持续输出
Echo 电平保护 保护开发板 IO 确认 Echo 到 IO7 之间已经做分压或电平转换
python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
CanMV K210 HC-SR04 超声波测距实验 - 单文件优化版

接线说明:
HC-SR04 VCC  -> 5V
HC-SR04 GND  -> GND
HC-SR04 Trig -> CanMV IO6
HC-SR04 Echo -> CanMV IO7

注意:
HC-SR04 供电通常使用 5V。
Echo 输出可能为 5V,CanMV IO 为 3.3V 逻辑,建议给 Echo 做分压或电平转换保护。

实验效果:
程序会持续读取距离,并根据距离范围输出状态:
距离过近、正常检测、距离较远、超出范围。
"""

from time import sleep, sleep_us, ticks_us
from maix import GPIO
from fpioa_manager import fm


# =========================
# 超声波驱动类
# =========================

class HCSR04:
    """
    HC-SR04 超声波测距模块驱动

    工作流程:
    1. Trig 引脚输出一个 10us 高电平脉冲
    2. Echo 引脚接收返回高电平
    3. 计算 Echo 高电平持续时间
    4. 根据声波往返时间换算距离
    """

    def __init__(self, trigger_pin, echo_pin, echo_timeout_us=30000):
        """
        trigger_pin     : Trig 触发引脚
        echo_pin        : Echo 回响引脚
        echo_timeout_us : 超时时间,单位微秒
        """
        self.echo_timeout_us = echo_timeout_us

        fm.register(trigger_pin, fm.fpioa.GPIOHS0, force=True)
        fm.register(echo_pin, fm.fpioa.GPIOHS1, force=True)

        self.trigger = GPIO(GPIO.GPIOHS0, GPIO.OUT, GPIO.PULL_NONE)
        self.echo = GPIO(GPIO.GPIOHS1, GPIO.IN, GPIO.PULL_NONE)

        self.trigger.value(0)
        sleep_us(5)

    def _wait_for_value(self, target_value, timeout_us):
        """
        等待 Echo 引脚变成指定电平

        返回:
        True  : 在超时时间内检测到目标电平
        False : 超时
        """
        start_time = ticks_us()

        while self.echo.value() != target_value:
            if ticks_us() - start_time > timeout_us:
                return False

        return True

    def _send_pulse_and_wait(self):
        """
        发送 Trig 脉冲,并计算 Echo 高电平持续时间
        """
        self.trigger.value(0)
        sleep_us(5)

        self.trigger.value(1)
        sleep_us(10)
        self.trigger.value(0)

        # 等待 Echo 从低电平变成高电平
        if not self._wait_for_value(1, self.echo_timeout_us):
            raise OSError("Out of range")

        start_time = ticks_us()

        # 等待 Echo 从高电平回到低电平
        if not self._wait_for_value(0, self.echo_timeout_us):
            raise OSError("Out of range")

        end_time = ticks_us()
        pulse_time = end_time - start_time

        return pulse_time

    def distance_mm(self):
        """
        获取距离,单位:毫米
        """
        pulse_time = self._send_pulse_and_wait()

        # 声波需要往返,所以时间要除以 2
        # 约等于 pulse_time * 100 // 582
        distance = pulse_time * 100 // 582

        return distance

    def distance_cm(self):
        """
        获取距离,单位:厘米
        """
        pulse_time = self._send_pulse_and_wait()

        # 声速约 343m/s,约等于 29.1us 传播 1cm
        # 距离 = 时间 / 2 / 29.1
        distance = (pulse_time / 2) / 29.1

        return distance


# =========================
# 硬件配置区
# =========================

TRIGGER_PIN = 6
ECHO_PIN = 7

MEASURE_INTERVAL = 0.3

TOO_CLOSE_CM = 10
NORMAL_MAX_CM = 80
FAR_MAX_CM = 200


# =========================
# 创建超声波对象
# =========================

sensor = HCSR04(
    trigger_pin=TRIGGER_PIN,
    echo_pin=ECHO_PIN,
    echo_timeout_us=30000
)


# =========================
# 距离状态判断
# =========================

def get_distance_state(distance_cm):
    """
    根据距离返回状态文本
    """
    if distance_cm < TOO_CLOSE_CM:
        return "距离过近"

    if distance_cm <= NORMAL_MAX_CM:
        return "正常检测"

    if distance_cm <= FAR_MAX_CM:
        return "距离较远"

    return "超出常用检测范围"


def read_distance_average(times=3):
    """
    多次读取距离并取平均值

    这样可以减少单次测量抖动,让数据显示更稳定。
    """
    values = []

    for _ in range(times):
        try:
            distance = sensor.distance_cm()

            if 2 <= distance <= 400:
                values.append(distance)

        except OSError:
            pass

        sleep(0.05)

    if len(values) == 0:
        return None

    return sum(values) / len(values)


# =========================
# 主循环
# =========================

def loop():
    """
    循环读取超声波距离
    """
    print("HC-SR04 超声波测距实验启动")
    print("Trig: IO%d, Echo: IO%d" % (TRIGGER_PIN, ECHO_PIN))
    print("-" * 35)

    while True:
        distance = read_distance_average(times=3)

        if distance is None:
            print("未检测到有效回波,可能超出范围或接线异常")
        else:
            state = get_distance_state(distance)
            print("距离:%.2f cm,状态:%s" % (distance, state))

        sleep(MEASURE_INTERVAL)


# =========================
# 程序入口
# =========================

if __name__ == "__main__":
    try:
        loop()
    except KeyboardInterrupt:
        print("程序已停止")

这段程序可以分成三个层级。底层是 HCSR04 类,负责 GPIOHS 引脚映射、Trig 脉冲输出、Echo 电平等待和回波时间计算;中间层是 distance_cm()distance_mm()read_distance_average(),负责把原始脉冲时间转换成距离,并通过多次采样降低抖动;上层是 get_distance_state()loop(),负责把距离转换成状态文本并持续输出。

_send_pulse_and_wait() 是测距动作的核心。程序先让 Trig 保持低电平,再输出 10 微秒高电平脉冲,触发 HC-SR04 发射超声波。Echo 引脚变成高电平后,程序记录开始时间;Echo 回到低电平后,程序记录结束时间。两个时间点之间的差值就是回波持续时间。由于超声波从模块发出后需要到达障碍物再反射回来,所以距离换算时需要考虑往返时间。

函数名 功能 对应现象
__init__() 初始化超声波模块 完成 IO6、IO7 的功能映射,并创建 Trig 输出对象和 Echo 输入对象
_wait_for_value() 等待 Echo 达到目标电平 判断回波信号是否在超时时间内出现或结束
_send_pulse_and_wait() 发送触发脉冲并计算回波时间 触发模块测距,得到 Echo 高电平持续时间
distance_mm() 获取毫米距离 将回波时间换算成毫米数值
distance_cm() 获取厘米距离 将回波时间换算成厘米数值,主循环使用该函数
get_distance_state() 判断距离状态 根据距离范围输出距离过近、正常检测、距离较远等文本
read_distance_average() 多次测距并取平均值 减少单次测量抖动,提高距离显示稳定性
loop() 循环读取并打印结果 终端持续显示距离和检测状态

主程序中的 MEASURE_INTERVAL = 0.3 表示每轮测距之间间隔 0.3 秒。这个间隔可以避免测距频率过高导致回波干扰,也让串口终端输出更容易观察。TOO_CLOSE_CMNORMAL_MAX_CMFAR_MAX_CM 三个常量负责划分距离状态,后续如果实验场景发生变化,只需要调整这些阈值,不需要修改底层测距代码。

扩展应用

超声波测距实验常见问题主要集中在供电、Echo 电平保护、Trig / Echo 接线、检测范围、回波干扰和测量抖动。排查时可以先确认模块是否正常供电,再检查 IO 映射和终端输出信息。

问题现象 可能原因 处理思路
终端一直提示未检测到有效回波 Trig 或 Echo 接线错误、模块供电异常、目标物超出检测范围 核对 IO6 接 Trig、IO7 接 Echo,确认 HC-SR04 使用 5V 供电,并把障碍物放在合理距离内
距离数值明显不稳定 被测物表面不平整、角度偏斜、测量间隔过短、环境回波复杂 调整模块正对目标物,保持被测表面平整,适当增大 timesMEASURE_INTERVAL
测距结果一直偏大 Echo 回波结束检测异常、目标物反射弱、环境中存在远处反射面 缩短测试距离,使用较大的平整物体测试,排除周围墙面或杂物干扰
距离过近时读数异常 HC-SR04 存在最小测距盲区 保持目标物距离模块 2 厘米以上,代码中已经过滤小于 2 厘米的结果
开发板 IO 存在损坏风险 Echo 可能输出 5V,而 CanMV IO 为 3.3V 逻辑 在 Echo 信号线加入分压电路或电平转换模块,避免 5V 直接进入 IO7
程序启动后无任何输出 脚本没有运行、串口终端未连接、程序入口未执行 检查运行环境和终端连接,确认脚本以主程序方式执行
距离偶尔显示 None 单次采样未收到有效回波 保持目标物正对模块,增加平均采样次数,确认 Echo 信号稳定
近距离检测正常,远距离不稳定 目标物反射面积小或角度不合适 使用平整硬质物体测试,避免布料、斜面或过小目标造成回波弱

超声波测距实验的扩展价值在于把"距离"变成程序可以判断的条件。程序读取到的距离不只是一个数字,还可以进一步驱动状态提示、避障逻辑、开关控制、报警反馈和数据记录。当前代码已经具备传感器封装、异常处理、平均滤波和状态判断能力,后续接入其他模块时,只需要把距离结果作为输入条件即可。

应用场景 实现思路 可扩展能力
智能避障小车 根据前方距离判断是否需要减速、停止或转向 后续可接入电机控制,实现自动避障
智能垃圾桶感应 检测手部或物体靠近,距离小于阈值时触发开盖动作 后续可接入舵机,实现自动开合结构
仓储物体检测 定时读取距离变化,判断货物是否存在或位置是否偏移 可增加状态记录和异常提醒
液位高度估算 将传感器固定在容器顶部,通过距离变化估算液面高度 需要结合容器高度做换算,并增加防水隔离设计
教学演示实验 用距离变化观察传感器数据、条件判断和异常处理 可作为 GPIO 输入输出、计时函数和数据过滤的综合案例
安全距离提醒 小于安全距离时输出"距离过近"状态 后续可扩展蜂鸣器或 LED,实现声光提示
数据采集记录 周期性读取距离并保存到本地或上传 后续可扩展串口通信、网络通信或数据可视化

从工程结构看,当前程序已经把测距驱动和业务判断分开。sensor.distance_cm() 只负责获取距离,get_distance_state() 只负责状态分类,loop() 只负责循环调度和输出显示。这种拆分方式适合继续扩展项目功能,例如把距离显示到 LCD 屏幕、通过蜂鸣器提示过近距离、结合摄像头做目标识别,或者把距离数据上传到 Web 后台做实时监控。当前代码没有实现这些模块,因此它们更适合作为后续课程方向。

总结

本实验通过 CanMV K210 开发板完成了 HC-SR04 超声波测距,核心能力包括 FPIOA 引脚映射、GPIOHS 输入输出配置、Trig 脉冲触发、Echo 回波检测、微秒级计时、距离换算、异常处理、平均值滤波和状态判断。代码从硬件接线出发,把超声波往返时间转换成厘米距离,再把距离结果转换成更容易理解的状态文本,完整展示了传感器实验中"真实环境数据进入程序"的过程。

这类实验非常适合作为 AI 硬件课程中的传感器入门案例。距离数据可以成为自动控制系统的判断依据,也可以作为后续智能交互项目的输入来源。后续课程可以在此基础上继续加入 LED 状态灯、蜂鸣器报警、LCD 显示、舵机控制、电机避障、摄像头识别和数据上传等内容,让单一测距实验逐步扩展成完整的智能硬件应用。

相关推荐
beyond阿亮12 小时前
PicoClaw皮皮虾: 端侧设备能跑AI智能体 超轻量AI智能体 极低成本硬件跑AI Agent,内存小于10MB
人工智能·ai·openclaw·picoclaw
kennyS_Titan12 小时前
PCB多层板升级,支撑智能硬件加速落地
人工智能·智能硬件
新加坡内哥谈技术12 小时前
Apple 和 Google 正在如何改造你的 push notification
人工智能
我材不敲代码12 小时前
【OpenCV零基础实战】键盘交互、像素位运算、通道离合、色彩转换与智能抠像
人工智能·opencv·计算机外设
yubo050912 小时前
计算机视觉第二课:3 个核心操作(灰度图 + 模糊 + 边缘检测)
人工智能·opencv·计算机视觉
Apache RocketMQ12 小时前
全新 AI 消息模型:Apache RocketMQ 如何让 AI 应用拥抱事件驱动架构?
人工智能·apache·rocketmq
TMT星球12 小时前
比亚迪发布中国首款4nm制程智驾芯片,布局高等级自动驾驶
人工智能·机器学习·自动驾驶
金融Tech趋势派12 小时前
企业微信营销获客实战指南:如何用企业微信AI SCRM工具实现低成本高转化
大数据·人工智能·企业微信
风落无尘12 小时前
《智能重生:从垃圾堆到AI工程师》——第十一章 对齐与安全
人工智能·安全