Arduino uses the ultrasonic distance measuring module HC-SR04 to obtain accurate measurement values - the elimination of error data

    The most commonly used ranging module on the Arduino board is the ultrasonic sensor HC-SR04, because this module is easy to use and cheap (free shipping for about 4 yuan on a certain treasure). Its main performance indicators are: 40KHz ultrasonic wave, ranging: 2cm-400cm, resolution: 3mm, effective angle: <15°, measuring angle: 30°.

    Its working principle is: give a high-level signal of at least 10us at the trig end, and the module will transmit 8 40Khz pulses cyclically, and if there is an echo signal, a high-level echo signal will be generated at the echo end. Its duration is proportional to the measured distance, so as long as the duration is measured, it can be converted into distance. The timing diagram is as follows:

 

    When we get the high-level length of the echo terminal (that is, the time t it takes for the ultrasonic wave to receive the reflected wave after it is sent from the module, the unit is us), we can use the distance formula s=vt to calculate the distance (here v is the speed of sound) , but this includes the return time, so the real distance has to be divided by 2. Here I will expand on the speed of sound v here. Usually, the speed of sound we commonly use is 340m/s. In fact, this is only an approximate number. The speed of sound is related to air pressure, humidity, and temperature. It is still relatively large, let's look at the relationship between the speed of sound v and temperature T:

    v=331.45+0.61T/°C

    Among them, T is the temperature value in Celsius. From this formula, we can see that if the temperature changes by 1C°, the speed will change by 0.61m/s. The speed of sound is about 355m/s, that is to say, the difference of 1 meter measured at two temperatures is about 7 cm, so it is better to add a temperature measurement module when the measurement accuracy is relatively high.

    We usually use 340m/s for the speed of sound. In fact, from the above formula, it can be concluded that this is the speed of sound at about 15C°C. Most of the distance formulas for programming HC-SR04 on the Internet are: s= t/58, some people are confused about the divisor 58, here I explain the origin of the divisor 58. First of all, the speed of sound is about 346.7m/s at 25C°C. The unit of distance s is centimeters (cm), and the unit of time t is microseconds (us). Since time is the time for turning back once, the distance is still To divide by 2. Then there is s=346.7*100cm/1000000/2*t=0.0173*t, and 0.0173 is approximately equal to 1/58, so we get: s=t/58, but I think it is more worthwhile to use s=0.0173*t Some, and the speed of multiplication is also faster.

    Therefore, when we use HC-SR04 for distance measurement, if we want to obtain more accurate results, it is best to add a temperature measurement module. However, in practical applications, when using HC-SR04 for distance measurement, the largest measurement error mainly comes from the interference of the environment on ultrasonic waves, such as the refraction caused by the uneven surface of the measured object, or even the water vapor in the air (you can measure it in Instability of data in humid climates) and many other factors will cause large deviations in the sampling data we obtain. In order to solve this problem, we must remove some abnormal data sampled. This program uses the Wright and Grubbs criteria to eliminate these abnormal data (about the method of Wright and Grubbs criteria to eliminate these abnormal data, you can Refer to my previous article "Processing of Arduino Measurement Error Data - Wright and Grubbs Criteria to Eliminate Abnormal Data"), so that we can obtain more accurate distance measurement values.

    In this experiment, we use an Arduino UNO board, an ultrasonic sensor HC-SR04, a temperature and humidity measurement module DHT11 (or not), and several Dupont wires. The trigger signal trig pin of HC-SR04 is connected to Arduino D8, the feedback signal echo pin is connected to Arduino D9; the data pin of DHT11 is connected to Arduino D12. As shown below:

 

    The measurement data processing method of this experiment is: every cycle, sampling 20 times, and then remove the abnormal data from the 20 obtained data, and then calculate the average value from the remaining data as the final distance value. This program has been tested and run on the Arduino UNO board, just connect according to the above figure, and then download it directly to the Arduino UNO board, the complete program is as follows:

/* 
  超声波传感器HC-SR04测距:
 触发信号trig:触发高电平脉冲大于10us
 反馈信号echo:返回的高电平长度就是距离的us数字
 通过声波速度和采集到的时间计算出距离。
 测量数据处理方法为:每循环一次,采样20次,然后对这20个获得的数据进行异常数据的剔除,
 再由剩余的数据求得平均值,作为最后的距离值。 
 */
#include "DHT.h"
DHT dht;
const int DATAN=20; //每组处理测试数据的个数

// 引脚定义
const int trig = 8;    // 触发信号
const int echo = 9;    // 反馈信号
const int DHTPin =  12; // 定义DHTPin连接的引脚为D12
float thtmp; //存储温度值

double dt[DATAN];//存放一组从SR04读取的距离数据
double bdt[DATAN];//存放一组被剔除的数据
int dn;//每组处理测试数据个数,Detection返回时为有效数据个数
int bdn;//某组被剔除的数据个数

//初始化
void setup() {
  pinMode(echo, INPUT);//SR04的反馈端口echo设置为输入
  pinMode(trig, OUTPUT);//SR04的触发端口trig设置为输出
  pinMode(DHTPin, INPUT);  //设置DHT11的数据读入DHTPin    
  dht.setup(DHTPin); // 设置DHT11数据传输的引脚
  Serial.begin(9600);
}

//主循环
void loop() {
  long IntervalTime=0; //定义一个时间变量
  int i=0;
  thtmp=(float) dht.getTemperature(); //从DHT11读取温度
  Serial.println(thtmp);//通过串口输出温度
  for(i=0;i<DATAN;i++) //进行一组数据,20(DATAN)次采样,并进行计算后送dt[0]—dt[19]
  {  
    digitalWrite(trig, 1);//置高电平
    delayMicroseconds(15);//延时15us
    digitalWrite(trig, 0);//设为低电平
    IntervalTime=pulseIn(echo, HIGH);//用自带的函数采样反馈的高电平的宽度,单位us
    if (isnan(thtmp)) dt[i]=0.0173*IntervalTime; //使用摄氏25C°时的声速约为346m/s计算出距离,单位cm(若不用测温模块)
    else dt[i]=(331.45+0.61*thtmp)/20000.00*IntervalTime; //使用v=331.45+0.61t计算出距离,单位cm(用DHT11温湿度测量模块)
  }
  dn=DATAN;//每组采用数据个数
  Serial.println(Detection(dt,bdt,dn,bdn,2));//输出经过剔除误差数据的距离均值,这里取自定义准则2
  Serial.println(bdn);//被剔除的数据个数
  for(i=0;i<bdn;i++)  Serial.println(bdt[i]);//被剔除的数据值
  delay(10000);//延时间隔决定采样的频率,根据实际需要变换参数,可以为0
}

//误差数据剔除程序,返回有效数据的平均值
//参数data输入为原始测量数据,返回时,前datanum个为有效数据
//参数baddata无输入数据,输出为被剔除的数据
//参数datanum输入为原始测量数据个数
//参数badnum无输入数据,输出为剔除的数据个数
//参数rule为莱特or格拉布斯准则选择,3为莱特准则,4为格拉布斯95%,5为格拉布斯99%,小于3为自定义准则
double Detection(double data[],double baddata[],int datanum,int &badnum,int rule)
{
  double data_b[datanum];//临时存放保留的数据
  double v[datanum]; //残差
  double g95[]={1.15,1.46,1.67,1.82,1.94,2.03,2.11,2.18,2.23,2.29,2.33,2.37,2.41,2.44,2.47,2.50,2.53,2.56,2.58,2.60,2.62,2.64,2.66,2.74,2.81,2.87,2.96,3.17};//格拉布斯95%
  double g99[]={1.16,1.49,1.75,1.94,2.10,2.22,2.32,2.41,2.48,2.55,2.61,2.66,2.71,2.75,2.79,2.82,2.85,2.88,2.91,2.94,2.96,2.99,3.01,3.10,3.18,3.24,3.34,3.58};//格拉布斯99%
  double bsl; //贝塞尔公式结果
  double maxdev; //有效的莱特 or 格拉布斯准则的最大偏差
  double sum; //累加临时存储
  double average; //平均值
  int badindex;//某次剔除数据数
  int validNum=0;//有效数据数
  int proindex=0;//循环的次数
  double lg;//莱特 or 格拉布斯准则的系数
  int i;
  if (rule<=3) //当rule小于等于3时,直接用莱特系数3或自定义的rule值
    lg=rule;
  else if(rule>5) //当rule大于5时,强制设为莱特准则
    lg=3;
  badnum=0;
  while(1)
  {
    if(rule==4) //格拉布斯95%
    {
      if(datanum>=100) lg=g95[27];//数据个数大于100个时
      else if(datanum>=50) lg=g95[26];
      else if(datanum>=40) lg=g95[25];
      else if(datanum>=35) lg=g95[24];
      else if(datanum>=30) lg=g95[23];
      else if(datanum>=25) lg=g95[22];
      else lg=g95[datanum-3];
    }
    else if(rule==5)//格拉布斯99%
    {
      if(datanum>=100) lg=g99[27];
      else if(datanum>=50) lg=g99[26];
      else if(datanum>=40) lg=g99[25];
      else if(datanum>=35) lg=g99[24];
      else if(datanum>=30) lg=g99[23];
      else if(datanum>=25) lg=g99[22];
      else lg=g99[datanum-3];
    }
    proindex++;
    sum=0;
    for(i=0;i<datanum;i++)
      sum+=data[i];
    average=sum/datanum; //计算平均值
    sum=0;
    for(i=0;i<datanum;i++)
    {
      v[i]=data[i]-average; //计算残差
      sum+=v[i]*v[i]; //计算残差平方和
    }
    bsl=sqrt(sum/(datanum-1)); //计算贝塞尔公式标准差
    maxdev=lg*bsl; //计算最大偏差
    //剔除坏值,即剔除粗差数据
    validNum=0;
    badindex=0;
    for(i=0;i<datanum;i++)
      if(fabs(v[i])>=maxdev && maxdev!=0) //当|Vi|>准则偏差值时
      {
        baddata[badnum++]=data[i];//将该Xi作为粗差数据,放入坏数据数组
        badindex++;
      }
      else data_b[validNum++]=data[i];//否则将效数数据暂存到data_b数组
        for(i=0;i<validNum;i++) //将暂存的效数数据送回数据数组data
          data[i]=data_b[i];
    datanum=validNum;//将当前有效数据个数作为数据个数
    if(datanum>5)//有效数据大于5个,则继续进行处理
    {
      if(badindex==0) //若没有可剔除的粗差数据
        break;//跳出循环,即粗差数据处理完毕
    }
    else break;//有效数据小于等于5个,直接跳出循环
  }
  return average;//子程序返回有效数据的均值
}

    My online name is "Mr. Unforgiveness". The articles published on CSDN are all my original works, and they are only published on the CSDN website. The reprints on other websites have not been authorized by me.

Guess you like

Origin blog.csdn.net/m0_61543203/article/details/127185686