第9周个人作业:为 XIAO 扩展板添加超声波测距传感器
项目介绍
本周个人项目的主要目标是:向我设计的微控制器板添加传感器并读取数据。为了实现这一目标,我选择了 Grove - Ultrasonic Ranger 超声波测距传感器,这是一款非接触式距离测量模块,工作频率为 40KHz。
Grove - Ultrasonic Ranger 超声波测距传感器
本周作业我列出了几个具体目标包括:
- 理解超声波测距传感器的工作原理
- 设计传感器电路并与 XIAO ESP32C3 进行连接
- 编写程序测试距离测量功能
- 数据可视化展示测量结果
- 记录整个设计、制造和测试过程
理解超声波测距传感器的工作原理
Grove - Ultrasonic Ranger 简介
Grove - Ultrasonic Ranger 是一款由 Seeed Studio 生产的超声波测距模块,采用标准的 Grove 接口,便于与各种微控制器(如 XIAO 开发板)进行简单的集成。下图左侧是购得的传感器,右侧是我目前做好的开发板。
Grove - Ultrasonic Ranger 模块(左侧)与我的 XIAO 开发板对比
超声波测距原理
超声波测距是基于声波传播时间来测量距离的技术。其工作原理如下:
- 发射超声波:传感器发射 8 个 40KHz 的超声波脉冲
- 等待回波:超声波在空气中传播,遇到障碍物后反射回来
- 接收回波:传感器接收到反射回来的超声波
- 计算距离:根据超声波从发射到接收的时间差计算距离
距离计算公式:距离 = 回波信号高电平时间 × 声速(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)电路
左侧部分展示了接收器的测试设置:
- A:振荡器(Oscillator) - 提供测试信号
- TW:高音扬声器(Tweeter) - 用于发出声波
- F.C.:频率计数器(Frequency Counter) - 监测信号频率
- 麦克风(MIC) - 标准参考麦克风,与接收器超声波传感器(R.U.S.)同时接收声波
- R.U.S.:接收超声波传感器(Receiver Ultrasonic Sensor) - 被测试的接收器
- R:3.9K电阻 - 为信号提供负载
- B:放大器(Amplifier) - 放大传感器输出信号
- C:电压表(Voltmeter) - 测量输出电压,参考值0dB=10V/Pa
发射器(Transmitter)电路
右侧部分显示了发射器的测试设置:
- A:振荡器(Oscillator) - 提供10Vrms的输入电压
- F.C.:频率计数器(Frequency Counter) - 监测信号频率
- T.U.S.:发射超声波传感器(Transmitter Ultrasonic Sensor) - 被测试的发射器
- 麦克风(MIC) - 用于接收发射器产生的超声波
- B:放大器(Amplifier) - 放大麦克风接收到的信号
- C:电压表(Voltmeter) - 测量放大后的信号,参考值0dB=0.02mPa
测试原理和流程
- 接收器测试:
- 振荡器产生40kHz频率的测试信号
- 高音扬声器(TW)将电信号转换为超声波
- 在无回声室(Anechoic Room)的30cm距离处,参考麦克风和被测接收器同时接收超声波
- 接收器输出的信号通过3.9K电阻接地,然后被放大器放大
- 电压表测量放大后的信号,与标准0dB=10V/Pa比较,确定接收器的灵敏度
- 发射器测试:
- 振荡器输出10Vrms的40kHz信号到被测发射器
- 发射器产生超声波
- 在无回声室的30cm距离处,标准麦克风接收超声波
- 麦克风信号被放大器放大
- 电压表测量放大后的信号,与标准0dB=0.02mPa比较,确定发射器的声压级
测试环境
无回声室(Anechoic Room)是测试的关键环境,它能够:
- 消除环境反射和干扰
- 提供稳定、一致的测试条件
- 确保测量结果的准确性和可重复性
这种测试方法允许精确测量传感器的关键性能参数,包括中心频率(40kHz±1kHz)、发射器的声压级(≥114dB)和接收器的灵敏度(≥-68dB)。
工作流程
Grove - Ultrasonic Ranger 的工作流程如下:
- 初始化:微控制器初始化传感器
- 触发测量:微控制器向传感器发送至少 10μs 的高电平触发信号
- 发射超声波:传感器接收到触发信号后,发射 8 个 40KHz 的超声波脉冲
- 等待回波:传感器等待超声波反射回来
- 接收回波:传感器接收到反射回来的超声波,并输出与距离成正比的高电平信号
- 计算距离:微控制器测量高电平信号的持续时间,并根据公式计算距离
与微控制器的接口方式
Grove - Ultrasonic Ranger 采用标准的 Grove 接口与微控制器通信:
- VCC:连接到微控制器的 3.3V 或 5V 电源
- GND:连接到微控制器的地线
- SIG:连接到微控制器的数字引脚,用于发送触发信号和接收回波信号
- NC:不连接
Grove - Ultrasonic Ranger 的特点是触发信号和回波信号共用一个 SIG 引脚,这简化了接线,但需要在软件中进行特殊处理。
设计传感器电路并与 XIAO ESP32C3 开发板进行连接
硬件连接方案
我的 XIAO 开发板的 8 针引脚如下图所示。
我设计的 XIAO 开发板的引脚编号标注
XIAO ESP32C3扩展板上8针引脚连接器(J1)的功能说明:
- GND - 接地连接
- 3.3V - 提供3.3V电源输出
- RX/D7 - 串口接收引脚(GPIO20)
- TX/D6 - 串口发送引脚(GPIO21)
- SCK/D8 - SPI时钟信号(GPIO8),带*标记的Strapping引脚
- MISO/D9 - SPI主机输入从机输出(GPIO9),带*标记的Strapping引脚
- MOSI/D10 - SPI主机输出从机输入(GPIO10)
- RST/其他可用引脚 - 可作为复位信号或其他功能扩展用途
Grove - Ultrasonic Ranger 超声波测距传感器与 XIAO ESP32C3 的开发板连接非常简单直接。该传感器只需要三个引脚连接,包括电源、地线和信号线。
连接方案详情
Grove - Ultrasonic Ranger 引脚 | XIAO ESP32C3 引脚 | 开发板 8针引脚连接器(J1)对应编号 | 功能 |
---|---|---|---|
VCC(红色线) | 3.3V | 2 | 电源正极 |
GND(黑色线) | GND | 1 | 电源地线 |
SIG(黄色线) | D6 / GPIO21 | 4 | 信号线(触发和回波共用) |
NC(白色线) | 不连接 | 未使用 |
接线图
XIAO ESP32C3 与 Grove - Ultrasonic Ranger 模块的接线图
硬件连接注意事项
- 电源选择:Grove - Ultrasonic Ranger 可在 3.2V-5.2V 电压范围内工作,XIAO ESP32C3 的 3.3V 输出正好满足要求。
- 信号线连接:传感器的 SIG 引脚连接到 XIAO ESP32C3 的数字引脚 D6,用于发送触发信号和接收回波信号。
- 传感器放置:超声波传感器的放置位置非常重要,应确保:
- 传感器前方没有遮挡物
- 测量区域不小于 0.5 平方米且表面平滑
- 固定牢固,避免晃动导致测量误差
- 连接线长度:使用尽可能短的连接线,以减少信号干扰。推荐连接线长度不超过 20cm。
实际连接实现
由于项目处于原型阶段,我使用杜邦线进行了临时连接,将 Grove - Ultrasonic Ranger 模块与之前设计的 XIAO ESP32C3 扩展板相连。完成连接后的实物如下图所示:
自制 XIAO ESP32C3 开发板与 Grove - Ultrasonic Ranger 模块的连接效果图
编程测试距离测量功能
开发环境搭建
开发环境基于 Arduino IDE 进行搭建,主要包括以下步骤:
- 安装 XIAO ESP32C3 开发板支持:
- 在 Arduino IDE 中添加 Seeed Studio XIAO 开发板支持。
- 选择 "
XIAO_ESP32C3
" 作为目标开发板。
- 安装 Ultrasonic Ranger 库:
- 从 Arduino IDE 的库管理界面搜索
grove Ultrasonic
。 - 安装
Grove Ultrasonic Ranger by Seeed Studio
,如下图所示。
- 从 Arduino IDE 的库管理界面搜索
需要在库管理器搜索安装 Grove Ultrasonic Ranger by Seeed Studio 库
- 测试环境准备:
- 设置串口监视器波特率为 115200。
- 准备测试环境,确保有稳定的测量条件。
测试程序代码
根据 Wiki 文档提供的测试程序:
#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 编号,否则会收不到信号),另外,我简化了输出,变成只输出数值,以方便串口绘图仪进行图形化展示,修改后的程序如下所示:
#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)
我设计了系统化的测试流程,对距离测量功能进行了全面评估:
- 基本功能测试:
- 在不同距离放置测试物体
- 记录测量结果和实际距离
- 计算测量误差
- 不同材质测试:
- 测试不同材质物体的反射效果
- 硬质平面(如木板):测量准确度高,误差约 ±1cm
- 软质材料(如布料):反射较弱,测量距离偏大
- 不规则表面:测量结果波动较大
- 不同角度测试:
- 测试物体与传感器不同角度下的测量效果
- 垂直放置:测量最准确
- 倾斜放置(15°以内):测量误差增加但仍可接受
- 倾斜放置(>15°):测量误差显著增加
- 长时间稳定性测试:
- 连续运行 30 分钟,记录测量结果的稳定性
- 分析测量数据的漂移情况
测试结果表明,该超声波测距系统在正常使用环境下具有良好的稳定性和可靠性,测量误差在 ±2cm 以内,完全满足一般应用需求。
添加测距值与开发板 LED 的距离反馈
既然我们现在已经可以通过传感器获得距离输出,那就可以玩的有趣的应用,我想可以把开发板上的 6 个 LED 灯利用起来,提供测距预估距离反馈。距离如果在 10cm 以内,点亮 D1;然后距离每增加 10cm,就按顺序依次点亮直到 D6。
准备用自己 DIY 的开发板的 6 个 LED,提供距离反馈指示
以下是我开发的超声波测距测试程序,它能够测量物体与传感器之间的距离,并通过 LED 和串口输出提供反馈。
/**
* 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 数量变化互动的效果。
测量遇到的主要问题
在测量过程中,我遇到了以下几个主要挑战:
- 测量不稳定问题:
- 在某些情况下,测量结果会出现跳变或异常值。
- 特别是在测量不规则表面或倾斜物体时,测量结果不稳定。
- 盲区问题:
- 传感器在 2cm 以内的距离无法准确测量(盲区)。
- 需要避免将物体放置在盲区内。
- 角度限制:
- 传感器的测量角度有限(约 15 度),超出此范围的物体可能无法被准确检测。
- 需要确保被测物体在传感器的检测范围内。