Skip to content

第9周个人作业:为 XIAO 扩展板添加超声波测距传感器

项目介绍

本周个人项目的主要目标是:向我设计的微控制器板添加传感器并读取数据。为了实现这一目标,我选择了 Grove - Ultrasonic Ranger 超声波测距传感器,这是一款非接触式距离测量模块,工作频率为 40KHz。

Grove - Ultrasonic Ranger 超声波测距传感器

本周作业我列出了几个具体目标包括:

  1. 理解超声波测距传感器的工作原理
  2. 设计传感器电路并与 XIAO ESP32C3 进行连接
  3. 编写程序测试距离测量功能
  4. 数据可视化展示测量结果
  5. 记录整个设计、制造和测试过程

理解超声波测距传感器的工作原理

Grove - Ultrasonic Ranger 简介

Grove - Ultrasonic Ranger 是一款由 Seeed Studio 生产的超声波测距模块,采用标准的 Grove 接口,便于与各种微控制器(如 XIAO 开发板)进行简单的集成。下图左侧是购得的传感器,右侧是我目前做好的开发板。

Grove - Ultrasonic Ranger 模块(左侧)与我的 XIAO 开发板对比

超声波测距原理

超声波测距是基于声波传播时间来测量距离的技术。其工作原理如下:

  1. 发射超声波:传感器发射 8 个 40KHz 的超声波脉冲
  2. 等待回波:超声波在空气中传播,遇到障碍物后反射回来
  3. 接收回波:传感器接收到反射回来的超声波
  4. 计算距离:根据超声波从发射到接收的时间差计算距离

距离计算公式:距离 = 回波信号高电平时间 × 声速(340m/s) ÷ 2

其中,除以 2 是因为超声波需要走完往返的路程。

Grove - Ultrasonic Ranger 技术规格

根据官方Wiki文档,Grove - Ultrasonic Ranger 具有以下技术特性:

参数规格
工作电压3.2V-5.2V
工作电流8mA
超声波频率40KHz
测量范围2-350cm
分辨率1cm
输出方式PWM
尺寸50mm × 25mm × 16mm
重量13g
测量角度15度
工作温度-10~60℃
触发信号10μS TTL
回波信号TTL

核心传感器 Ceramic Ultrasonic Sensor NU40C16T/R-1 的文档提供了测试电路,如下图所示。

Ceramic Ultrasonic Sensor NU40C16T/R-1 的文档提供了测试电路

这个测试电路图显示了超声波传感器(NU40C16T/R-1)的测试配置,分为接收器(Receiver)和发射器(Transmitter)两部分。我将逐一解释图中的各个元素和整体工作原理:

接收器(Receiver)电路

左侧部分展示了接收器的测试设置:

  1. A:振荡器(Oscillator) - 提供测试信号
  2. TW:高音扬声器(Tweeter) - 用于发出声波
  3. F.C.:频率计数器(Frequency Counter) - 监测信号频率
  4. 麦克风(MIC) - 标准参考麦克风,与接收器超声波传感器(R.U.S.)同时接收声波
  5. R.U.S.:接收超声波传感器(Receiver Ultrasonic Sensor) - 被测试的接收器
  6. R:3.9K电阻 - 为信号提供负载
  7. B:放大器(Amplifier) - 放大传感器输出信号
  8. C:电压表(Voltmeter) - 测量输出电压,参考值0dB=10V/Pa

发射器(Transmitter)电路

右侧部分显示了发射器的测试设置:

  1. A:振荡器(Oscillator) - 提供10Vrms的输入电压
  2. F.C.:频率计数器(Frequency Counter) - 监测信号频率
  3. T.U.S.:发射超声波传感器(Transmitter Ultrasonic Sensor) - 被测试的发射器
  4. 麦克风(MIC) - 用于接收发射器产生的超声波
  5. B:放大器(Amplifier) - 放大麦克风接收到的信号
  6. C:电压表(Voltmeter) - 测量放大后的信号,参考值0dB=0.02mPa

测试原理和流程

  1. 接收器测试
    • 振荡器产生40kHz频率的测试信号
    • 高音扬声器(TW)将电信号转换为超声波
    • 在无回声室(Anechoic Room)的30cm距离处,参考麦克风和被测接收器同时接收超声波
    • 接收器输出的信号通过3.9K电阻接地,然后被放大器放大
    • 电压表测量放大后的信号,与标准0dB=10V/Pa比较,确定接收器的灵敏度
  2. 发射器测试
    • 振荡器输出10Vrms的40kHz信号到被测发射器
    • 发射器产生超声波
    • 在无回声室的30cm距离处,标准麦克风接收超声波
    • 麦克风信号被放大器放大
    • 电压表测量放大后的信号,与标准0dB=0.02mPa比较,确定发射器的声压级

测试环境

无回声室(Anechoic Room)是测试的关键环境,它能够:

  • 消除环境反射和干扰
  • 提供稳定、一致的测试条件
  • 确保测量结果的准确性和可重复性

这种测试方法允许精确测量传感器的关键性能参数,包括中心频率(40kHz±1kHz)、发射器的声压级(≥114dB)和接收器的灵敏度(≥-68dB)。

工作流程

Grove - Ultrasonic Ranger 的工作流程如下:

  1. 初始化:微控制器初始化传感器
  2. 触发测量:微控制器向传感器发送至少 10μs 的高电平触发信号
  3. 发射超声波:传感器接收到触发信号后,发射 8 个 40KHz 的超声波脉冲
  4. 等待回波:传感器等待超声波反射回来
  5. 接收回波:传感器接收到反射回来的超声波,并输出与距离成正比的高电平信号
  6. 计算距离:微控制器测量高电平信号的持续时间,并根据公式计算距离

与微控制器的接口方式

Grove - Ultrasonic Ranger 采用标准的 Grove 接口与微控制器通信:

  • VCC:连接到微控制器的 3.3V 或 5V 电源
  • GND:连接到微控制器的地线
  • SIG:连接到微控制器的数字引脚,用于发送触发信号和接收回波信号
  • NC:不连接

Grove - Ultrasonic Ranger 的特点是触发信号和回波信号共用一个 SIG 引脚,这简化了接线,但需要在软件中进行特殊处理。

设计传感器电路并与 XIAO ESP32C3 开发板进行连接

硬件连接方案

我的 XIAO 开发板的 8 针引脚如下图所示。

我设计的 XIAO 开发板的引脚编号标注

XIAO ESP32C3扩展板上8针引脚连接器(J1)的功能说明:

  1. GND - 接地连接
  2. 3.3V - 提供3.3V电源输出
  3. RX/D7 - 串口接收引脚(GPIO20)
  4. TX/D6 - 串口发送引脚(GPIO21)
  5. SCK/D8 - SPI时钟信号(GPIO8),带*标记的Strapping引脚
  6. MISO/D9 - SPI主机输入从机输出(GPIO9),带*标记的Strapping引脚
  7. MOSI/D10 - SPI主机输出从机输入(GPIO10)
  8. RST/其他可用引脚 - 可作为复位信号或其他功能扩展用途

Grove - Ultrasonic Ranger 超声波测距传感器与 XIAO ESP32C3 的开发板连接非常简单直接。该传感器只需要三个引脚连接,包括电源、地线和信号线。

连接方案详情

Grove - Ultrasonic Ranger 引脚XIAO ESP32C3 引脚开发板 8针引脚连接器(J1)对应编号功能
VCC(红色线)3.3V2电源正极
GND(黑色线)GND1电源地线
SIG(黄色线)D6 / GPIO214信号线(触发和回波共用)
NC(白色线)不连接未使用

接线图

XIAO ESP32C3 与 Grove - Ultrasonic Ranger 模块的接线图

硬件连接注意事项

  1. 电源选择:Grove - Ultrasonic Ranger 可在 3.2V-5.2V 电压范围内工作,XIAO ESP32C3 的 3.3V 输出正好满足要求。
  2. 信号线连接:传感器的 SIG 引脚连接到 XIAO ESP32C3 的数字引脚 D6,用于发送触发信号和接收回波信号。
  3. 传感器放置:超声波传感器的放置位置非常重要,应确保:
    • 传感器前方没有遮挡物
    • 测量区域不小于 0.5 平方米且表面平滑
    • 固定牢固,避免晃动导致测量误差
  4. 连接线长度:使用尽可能短的连接线,以减少信号干扰。推荐连接线长度不超过 20cm。

实际连接实现

由于项目处于原型阶段,我使用杜邦线进行了临时连接,将 Grove - Ultrasonic Ranger 模块与之前设计的 XIAO ESP32C3 扩展板相连。完成连接后的实物如下图所示:

自制 XIAO ESP32C3 开发板与 Grove - Ultrasonic Ranger 模块的连接效果图

编程测试距离测量功能

开发环境搭建

开发环境基于 Arduino IDE 进行搭建,主要包括以下步骤:

  1. 安装 XIAO ESP32C3 开发板支持
    • 在 Arduino IDE 中添加 Seeed Studio XIAO 开发板支持。
    • 选择 "XIAO_ESP32C3" 作为目标开发板。
  2. 安装 Ultrasonic Ranger 库
    • 从 Arduino IDE 的库管理界面搜索 grove Ultrasonic
    • 安装 Grove Ultrasonic Ranger by Seeed Studio ,如下图所示。

需要在库管理器搜索安装 Grove Ultrasonic Ranger by Seeed Studio 库

  1. 测试环境准备
    • 设置串口监视器波特率为 115200。
    • 准备测试环境,确保有稳定的测量条件。

测试程序代码

根据 Wiki 文档提供的测试程序:

cpp
#include "Ultrasonic.h"

Ultrasonic ultrasonic(7);
void setup()
{
 Serial.begin(9600);
}
void loop()
{
 long RangeInInches;
 long RangeInCentimeters;

 Serial.println("The distance to obstacles in front is: ");
 RangeInInches = ultrasonic.MeasureInInches();
 Serial.print(RangeInInches);//0~157 inches
 Serial.println(" inch");
 delay(250);

 RangeInCentimeters = ultrasonic.MeasureInCentimeters(); // two measurements should keep an interval
 Serial.print(RangeInCentimeters);//0~400cm
 Serial.println(" cm");
 delay(250);
}

我稍作了点修改,原文档 Ultrasonic ultrasonic(7);修改为 Ultrasonic ultrasonic(21);(以和 XIAO ESP32C3 的引脚 D6/ GPIO21 编号一致,注意这里要填 GPIO 编号,否则会收不到信号),另外,我简化了输出,变成只输出数值,以方便串口绘图仪进行图形化展示,修改后的程序如下所示:

cpp
#include "Ultrasonic.h"

// 使用正确的GPIO编号
Ultrasonic ultrasonic(21);  // 对应XIAO ESP32C3的 D6/GPIO21

void setup() {
  Serial.begin(115200);  // XIAO ESP32C3通常使用115200波特率
  delay(1000);  // 等待串口连接
  
  // 在开始时发送一条单位信息
  // 大多数绘图仪会忽略这一行,但对于人类用户来说是有用的信息
  Serial.println("Distance(cm)");
}

void loop() {
  // 测量距离
  long distance = ultrasonic.MeasureInCentimeters();
  
  // 只输出数值,不包含文本,适合串口绘图仪
  Serial.println(distance);
  
  delay(500);  // 每500毫秒测量一次
}

运行程序后能通过串口监视器看到我伸手的测量距离,如下图所示。

串口监视器开始输出测试到的距离

数据可视化

在 Arduino IDE 右上角点击串口绘图仪,会打开一个图形化窗口,通过图形方式展示测试到的距离变化,如下图所示。

串口绘图仪用可视化方式动态展示测试到的距离变化

测试方法和结果

我用尺子搭建了一个简单的距离测试环境,在相对较近的距离,还是比较准确的。

距离测试视频,屏幕上串口监视器会反馈传感器输出的测量距离(cm)

我设计了系统化的测试流程,对距离测量功能进行了全面评估:

  1. 基本功能测试
    • 在不同距离放置测试物体
    • 记录测量结果和实际距离
    • 计算测量误差
  2. 不同材质测试
    • 测试不同材质物体的反射效果
    • 硬质平面(如木板):测量准确度高,误差约 ±1cm
    • 软质材料(如布料):反射较弱,测量距离偏大
    • 不规则表面:测量结果波动较大
  3. 不同角度测试
    • 测试物体与传感器不同角度下的测量效果
    • 垂直放置:测量最准确
    • 倾斜放置(15°以内):测量误差增加但仍可接受
    • 倾斜放置(>15°):测量误差显著增加
  4. 长时间稳定性测试
    • 连续运行 30 分钟,记录测量结果的稳定性
    • 分析测量数据的漂移情况

测试结果表明,该超声波测距系统在正常使用环境下具有良好的稳定性和可靠性,测量误差在 ±2cm 以内,完全满足一般应用需求。

添加测距值与开发板 LED 的距离反馈

既然我们现在已经可以通过传感器获得距离输出,那就可以玩的有趣的应用,我想可以把开发板上的 6 个 LED 灯利用起来,提供测距预估距离反馈。距离如果在 10cm 以内,点亮 D1;然后距离每增加 10cm,就按顺序依次点亮直到 D6。

准备用自己 DIY 的开发板的 6 个 LED,提供距离反馈指示

以下是我开发的超声波测距测试程序,它能够测量物体与传感器之间的距离,并通过 LED 和串口输出提供反馈。

cpp
/**
 * XIAO ESP32C3 超声波测距程序 + LED距离显示
 * 使用Grove Ultrasonic Ranger传感器
 * 
 * 作者:冯磊
 * 日期:2025-03-25
 */

#include "Ultrasonic.h"

// 传感器引脚定义
#define ULTRASONIC_PIN 21  // 对应D7/GPIO21

// LED引脚定义 - 使用扩展板上的6个LED
const int LED_PINS[] = {D0, D1, D2, D3, D4, D5};
const int LED_COUNT = 6;

// 创建超声波传感器对象
Ultrasonic ultrasonic(ULTRASONIC_PIN);

// 定义变量
long distance = 0;
unsigned long lastMeasureTime = 0;
const unsigned long MEASURE_INTERVAL = 500; // 测量间隔(毫秒)

// 距离显示配置
const int DISTANCE_PER_LED = 10; // 每个LED代表10cm
const int MAX_DISPLAY_DISTANCE = LED_COUNT * DISTANCE_PER_LED; // 最大显示距离60cm

/**
 * 初始化函数
 */
void setup() {
  // 初始化串口通信
  Serial.begin(115200);
  
  // 等待串口连接
  delay(1000);
  Serial.println("\n\nXIAO ESP32C3 - 超声波测距系统初始化");
  
  // 初始化LED引脚为输出
  for (int i = 0; i < LED_COUNT; i++) {
    pinMode(LED_PINS[i], OUTPUT);
  }
  
  // LED测试 - 依次点亮所有LED
  for (int i = 0; i < LED_COUNT; i++) {
    digitalWrite(LED_PINS[i], HIGH);
    delay(100);
  }
  
  // 关闭所有LED
  for (int i = 0; i < LED_COUNT; i++) {
    digitalWrite(LED_PINS[i], LOW);
  }
  
  Serial.println("系统准备就绪!开始测量距离...");
  Serial.println("距离显示: 每个LED代表10cm的距离");
  
  // 打印CSV表头,用于数据记录
  Serial.println("时间(ms),距离(cm),LED数量");
}

/**
 * 主循环函数
 */
void loop() {
  // 定时测量距离
  unsigned long currentTime = millis();
  if (currentTime - lastMeasureTime >= MEASURE_INTERVAL) {
    lastMeasureTime = currentTime;
    
    // 测量距离
    distance = ultrasonic.MeasureInCentimeters();
    
    // 检测传感器是否工作正常
    if (distance == 0) {
      Serial.println("警告:接收到距离为0,请检查传感器连接!");
      
      // 传感器错误时LED闪烁提示
      blinkAllLEDs(3, 200);
    } else {
      // 输出CSV格式数据
      Serial.print(currentTime);
      Serial.print(",");
      Serial.print(distance);
      Serial.print(",");
      
      // 计算需要点亮的LED数量
      int ledsToLight = 0;
      if (distance <= MAX_DISPLAY_DISTANCE) {
        ledsToLight = (distance + DISTANCE_PER_LED - 1) / DISTANCE_PER_LED;
      } else {
        ledsToLight = LED_COUNT; // 超出最大显示距离,点亮所有LED
      }
      
      Serial.println(ledsToLight);
      
      // 根据距离点亮对应数量的LED
      updateLEDDisplay(ledsToLight);
      
      // 显示详细测量信息
      Serial.print("距离: ");
      Serial.print(distance);
      Serial.print(" cm | LED: ");
      Serial.print(ledsToLight);
      Serial.println(" 个");
    }
  }
  
  // 短暂延时,避免CPU占用过高
  delay(50);
}

/**
 * 更新LED显示
 * 
 * @param count 要点亮的LED数量
 */
void updateLEDDisplay(int count) {
  // 确保count在有效范围内
  if (count > LED_COUNT) {
    count = LED_COUNT;
  }
  
  // 更新LED状态
  for (int i = 0; i < LED_COUNT; i++) {
    if (i < count) {
      digitalWrite(LED_PINS[i], HIGH); // 点亮
    } else {
      digitalWrite(LED_PINS[i], LOW);  // 熄灭
    }
  }
}

/**
 * 所有LED同时闪烁
 * 
 * @param times 闪烁次数
 * @param delayTime 闪烁间隔时间(毫秒)
 */
void blinkAllLEDs(int times, int delayTime) {
  for (int i = 0; i < times; i++) {
    // 点亮所有LED
    for (int j = 0; j < LED_COUNT; j++) {
      digitalWrite(LED_PINS[j], HIGH);
    }
    delay(delayTime);
    
    // 熄灭所有LED
    for (int j = 0; j < LED_COUNT; j++) {
      digitalWrite(LED_PINS[j], LOW);
    }
    delay(delayTime);
  }
}

运行程序后,可以看到串口监视器会显示测量距离和 LED 灯亮的数量,如下图所示。

串口监视器显示测量距离和 LED 灯亮的数量

下面的视频展示了装置随测量距离变化 LED 数量变化互动的效果。

测量遇到的主要问题

在测量过程中,我遇到了以下几个主要挑战:

  1. 测量不稳定问题
    • 在某些情况下,测量结果会出现跳变或异常值。
    • 特别是在测量不规则表面或倾斜物体时,测量结果不稳定。
  2. 盲区问题
    • 传感器在 2cm 以内的距离无法准确测量(盲区)。
    • 需要避免将物体放置在盲区内。
  3. 角度限制
    • 传感器的测量角度有限(约 15 度),超出此范围的物体可能无法被准确检测。
    • 需要确保被测物体在传感器的检测范围内。