OrangePi ZERO 2 外设应用程序开发之超声波模块(HC-SR04)

使用 HC-SR04 超声波距离传感器可以实现测距的功能,该传感器可以测量最远 13 英尺内(约为 3.96 米)的物体范围。它们具有低功耗(适用于电池供电的设备),价格低廉,易于连接以及在业余爱好者中疯狂流行的特点。另外,它甚至看起来很酷,就像一双 Walle 机器人的眼睛。

在这里插入图片描述

一、HC-SR04 硬件概述

HC-SR04 超声波距离传感器的核心是两个超声波传感器。一个用作发射器,将电信号转换为 40 KHz 超声波脉冲。一个是接收器,监听发射后被障碍物反弹回来的超声波。如果接收到返回的超声波,它将产生一个输出脉冲,其宽度可用于确定脉冲传播的距离。

1. 技术规范(Technical Specifications)

在这里插入图片描述

2. HC-SR04 超声波传感器引脚

在这里插入图片描述

VCC为 HC-SR04 超声波传感器供电,一般接 5V 电压。GND是接地引脚。

Trig (Trigger) 引脚用于触发超声波脉冲,通过将该引脚设置为HIGH(高)10µs,传感器启动超声波发射。当发送超声波脉冲串时,Echo引脚变高,并保持高电平,直到传感器接收到回声,然后变低。通过测量Echo引脚保持高位的时间计算距离。

二、HC-SR04 超声波距离传感器如何工作?

Trig引脚设置为高电平达 10µs 时,超声波距离传感器开始工作。随后传感器以 40KHz 发送八个超声波脉冲。这八个脉冲模式是专门设计的,使得接收器可以将发射的超声波与环境噪声区分开来。这八个超声波脉冲在远离发射器的空气中传播。同时,Echo引脚变为高电平,以接收回波返回信号。如果这些脉冲没有被反射回来,Echo信号就会超时,并在 38ms 后变为低电平。因此,38ms 的脉冲表示在传感器的范围内没有障碍物。

在这里插入图片描述

如果这些超声波脉冲被反射回来,那么一旦接收到信号,Echo引脚就会变低。这会在Echo引脚上产生一个宽度从 150µs 到 25ms 不等的脉冲,具体取决于接收信号所需的时间。

在这里插入图片描述

三、距离计算

接收脉冲的宽度用于计算与反射物体的距离。这可以用我们在中学就学到的距离-速度-时间方程来计算。假设我们在传感器前面有一个未知距离的物体,并且我们在Echo引脚上接收到 500µs 宽度的脉冲。为此,我们将使用以下等式:
D i s t a n c e = S p e e d ∗ T i m e Distance = Speed * Time Distance=SpeedTime
时间值即 500µs,速度是声速,速度为340 m/s。为了计算时比较贴合我们的常用单位,我将声速的单位转换为 cm/µs,也就是为 0.034 cm/μs。那么具体计算过程如下:
D i s t a n c e = 0.034 c m / µ s ∗ 500 µ s Distance = 0.034 cm/µs * 500 µs Distance=0.034cms500µs
但是还没有结束!请记住,回波脉冲表示信号被发送和反射回来所需的时间。因此上述公式得到的结果还需要除以 2,才是正确的距离。
D i s t a n c e = ( 0.034 c m / µ s ∗ 500 µ s ) / 2 Distance = (0.034 cm/µs * 500 µs) / 2 Distance=(0.034cms500µs)/2
用上述公式计算,我们知道物体距离传感器 8.5 厘米。

四、与 OrangePi ZERO 2 接线并用代码实现测距

1. 硬件接线

了解了 HC-SR04 超声波传感器的工作原理后,就可以将 HC-SR04 连接到 OrangePi ZERO 2 上了。将VCC引脚连接到 OrangePi ZERO 2 上的5V引脚,将GND引脚连接至 OrangePi ZERO 2 上的GND引脚,将Trig引脚接到 26 Pin 引脚的 11 号引脚,将Echo引脚接到 26 Pin 引脚的 13 号引脚。

具体接线如下图所示。

在这里插入图片描述

2. Linux 系统的日期时间头文件

由于是在 Linux 系统中开发超声波传感器的程序,其中涉及到时间的读取,因此会用到与时间相关的头文件。一般使用 Linux系统的日期时间头文件,也就是sys/time.h,这个头文件里面也包含了time.h

这次程序会用到下面提的 API 和结构体,使用gettimeofday函数可以获取当地时区的时间,获取到的时间,会分别存放在以结构体timeval和结构体timezone定义的变量中。其中,以结构体timeval定义的变量中的tv_sec表示当地时区以秒为单位的变量,tv_usec表示当地时区以微秒为单位的变量。而结构体timezone在本次开发中没有用上,就不做介绍了。

#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz)
 
struct timeval
{ 
    long tv_sec;        //秒
    long tv_usec;       //微秒
};
 
/*
struct timezone {
	int tz_minuteswest;    //和 Greenwich 时间以西的分钟差值
	int tz_dsttime;        //DST 校正类型
};
*/

下面这个 Demo 就是这个 API 和结构体的使用方法。

#include <stdio.h>
#include <sys/time.h>

int main()
{
    struct timeval tv;

    if (gettimeofday(&tv, NULL) == 0)
        printf("Seconds: %ld\nMicroseconds: %ld\n", tv.tv_sec, tv.tv_usec);
    else
        printf("Error with gettimeofday.\n");

    return 0;
}

运行情况如下图,解释一下这个数字的意思,tv.tv_sec为 1970 年 1 月 1 日 0 时 0 分 0 秒到调用gettimeofday函数的秒数,tv.tv_usec为秒后面的零头。这些了解一下就好,方便我们后面使用这些函数。

在这里插入图片描述

3. 超声波测距代码解析

要实现超声波模块的测距,除了对超声波模块输入控制信号,还要接收超声波模块发给香橙派的信号,所以需要使用到 wiringOP 库中的通用 GPIO 读取函数。(其他函数见上篇:《OrangePi ZERO 2 外设应用程序开发之用 wiringOP 编程点亮一个LED 与 shell 脚本编译》

int digitalRead (int pin)

函数具体说明如下:

通用GPIO控制函数原型函数参数函数说明
int digitalRead (int pin)pin:读取的引脚
返回:引脚上的电平,可以是 LOW HIGH 之一
读取一个引脚的电平值 LOW HIGH ,返回

考虑到代码的要有较高的可读性,又便于新手理解,我把整个流程分成三段,分别是初始化、启动超声波、获取距离,之后一直在main函数中每隔一秒循环获取一次距离并打印。下面是代码示例分段解释。

先是定义引脚,前面提到将Trig引脚和Echo引脚分别接到 26 Pin 引脚的 11 号引脚和 13 号引脚,而这两个引脚对应的 wPi 序号是 5 和 7,那么可以用宏定义来设置引脚序号。

#define TRIG 5
#define ECHO 7

然后就是初始化引脚,先用wiringPiSetup函数初始化,如果初始化失败(函数返回值为 -1 即为失败),则退出程序。然后将Trig引脚和Echo引脚分别设置为输入和输出。因为启动超声波传感器是把把Trig引脚拉高 10 μs,所以先把Trig引脚拉低到一个稳定的低电平并维持这个状态 500 ms。

void initUltrasonic(void)
{
    if (wiringPiSetup() == -1) {
        printf("[%s]: Failed to initialize wiringPi!\n", __func__);
        exit(-1);
    }

    pinMode(TRIG, OUTPUT);
    pinMode(ECHO, INPUT);
    digitalWrite(TRIG, LOW);
    delay(500);
}

在获取距离之前先要把超声波模块启动一下,先拉高Trig引脚 10 μs 再拉低。

void startUltrasonic(void)
{
    digitalWrite(TRIG, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG, LOW);
}

接着就是获取距离了,先定义一个double类型的变量distance,用于存放获取的距离。再定义两个结构体struct timeval变量startstop,分别用于存放Echo引脚由低电平变高电平的时间节点和由高电平变低电平的时间节点。用 while 循环可以很准确地判断Echo引脚的电平变化,同时用gettimeofday函数在Echo引脚发生变化的时候记录下时间节点。计算两时间节点的秒的差值,再计算两时间节点的微秒的差值,两数相加等到单位为微秒的时间,这个时间就是Echo引脚高电平的时间,最后乘以单位为 cm/μs 的音速再除以 2,得到的就是超声波传感器和障碍物之间的距离了。

double getDistance(void)
{
    double distance;
    struct timeval startTime;
    struct timeval endTime;


    startUltrasonic();        //启动超声波传感器

    while (!digitalRead(ECHO));        //等待 Echo 引脚由低变高
    gettimeofday(&startTime, NULL);    //记录下 Echo 引脚变高电平的时间节点

    while (digitalRead(ECHO));        //等待 Echo 引脚由高变低
    gettimeofday(&endTime, NULL);     //记录下 Echo 引脚变低电平的时间节点

    distance = (((endTime.tv_sec - startTime.tv_sec)         //计算两时间节点的秒的差值
                + (endTime.tv_usec - startTime.tv_usec))     //计算两时间节点的微秒的差值
                * 0.034) / 2;                                //乘以音速再除以 2,就是距离,单位为 cm

    return  distance;
}

上述的程序还是稍显臃肿,之所以这样写是为了新手阅读方便,如果你理解上面的函数,可以把getDistance函数改成简短一些,不过存在 0.017 这个让人不理解的魔鬼数字,虽然结合上文可以知晓是 0.034 cm/μs 这个音速值的一半,不过尽量还是用宏定义或者定义一个常量变量代替,我这里就不换了。

double getDistance(void)
{
    struct timeval startTime, endTime;

    startUltrasonic();
    while (!digitalRead(ECHO));
    gettimeofday(&startTime, NULL);
    while (digitalRead(ECHO));
    gettimeofday(&endTime, NULL);

    return ((endTime.tv_sec - startTime.tv_sec)
            + (endTime.tv_usec - startTime.tv_usec)) * 0.017;
}

整合上述所有代码,创建一个文件命名为“ultrasonic.c”,实现具体的功能,完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <wiringPi.h>

#define TRIG 5
#define ECHO 7

void initUltrasonic(void)
{
    if (wiringPiSetup() == -1) {
        printf("[%s]: Failed to initialize wiringPi!\n", __func__);
        exit(-1);
    }

    pinMode(TRIG, OUTPUT);
    pinMode(ECHO, INPUT);
    digitalWrite(TRIG, LOW);
    delay(500);
}

void startUltrasonic(void)
{
    digitalWrite(TRIG, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG, LOW);
}

double getDistance(void)
{
    struct timeval startTime, endTime;

    startUltrasonic();
    while (!digitalRead(ECHO));
    gettimeofday(&startTime, NULL);
    while (digitalRead(ECHO));
    gettimeofday(&endTime, NULL);

    return ((endTime.tv_sec - startTime.tv_sec)
            + (endTime.tv_usec - startTime.tv_usec)) * 0.017;
}

int main(void)
{
    initUltrasonic();
    while (1) {
	    printf("Distance = %.2lfcm\n", getDistance());
	    delay(1000);
    }
    
    return 0;
}

这个代码每秒会打印出一次传感器和障碍物之间的距离。getDistance函数最后的返回值,就是前后的时间差乘以音速(单位:cm/µs)的一半的值。

编译运行结果如下图。

在这里插入图片描述

五、HC-SR04 的使用限制

有时候我们只使用超声波传感器测距时,会发现有些时候会失灵,测出来的距离与实际情况差距太大,可能是因为不是因为代码的问题,而是这个模块本身就有一些使用场景的限制。以下说明一些使用场合的限制。

  1. 传感器与物体/障碍物之间的距离大于 13 英尺;

    在这里插入图片描述

  2. 物体的反射表面呈小角度,因此超声波不会反射回传感器;

    在这里插入图片描述

  3. 物体太小,无法将足够的声音反射回传感器;

    在这里插入图片描述

  4. 一些表面柔软、不规则的物体(如填充动物)会吸收声音而不是反射声音,因此 HC-SR04 可能很难检测到这些物体。

    在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Grayson Zheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值