[python]RaspberryPiで超音波距離センサを使った時の精度

## はじめに
これは「テックキャンプ公式コミュニティ Advent Calendar 2023」の
18日目の記事です。
https://adventar.org/calendars/8952

参加を決める時に何を書こうか考えて
先日東京に行った際に秋葉原の秋月電機で
超音波距離センサを購入したのを思い出しました。
センサを久しぶりのRaspberryPiで使ってみて
距離測定精度はどれぐらいか測定した結果を
Qiitaまとめようと思いました。

## 使うものと仕様
- Raspberry Pi4 ModelB 4GB
- HC-SR04(超音波距離センサ)
https://akizukidenshi.com/catalog/g/gM-11009/
測距範囲： 2 ~ 450 cm
(センサー基板正面を中心とした15度の範囲、分解能：0.3 cm)

## 測定方法
### レイアウト
RaspberryPiに接続した超音波センサから
メジャーを使い箱(高さ12cm×幅21cm)を設置し
その際のRaspberryPiの出力を計測

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632401/d3c85967-e66c-6bb5-97cf-d6b33244bb00.png)
### 参考資料（コードと接続方法）
こちらの記事を参考に
- 10回距離を取得して平均を出力

するように変更しました

https://sozorablog.com/%E3%80%90raspberry-pi%E5%85%A5%E9%96%80%E3%80%91%E3%83%A9%E3%82%BA%E3%83%99%E3%83%AA%E3%83%BC%E3%83%91%E3%82%A4%E3%81%A7%E8%B6%85%E9%9F%B3%E6%B3%A2%E8%B7%9D%E9%9B%A2%E8%A8%88%E3%82%92%E4%BD%9C/

```python
import RPi.GPIO as GPIO
import time
import sys 

# HIGH or LOWの時計測
def pulseIn(PIN, start=1, end=0):
    if start==0: end = 1
    t_start = 0
    t_end = 0
    # ECHO_PINがHIGHである時間を計測
    while GPIO.input(PIN) == end:
        t_start = time.time()
        
    while GPIO.input(PIN) == start:
        t_end = time.time()
    return t_end - t_start

# 距離計測
def calc_distance(TRIG_PIN, ECHO_PIN, num, v=34000):
    
    distance_list = [] #測定した距離を記録するlist
    
    for i in range(num):
        # TRIGピンを0.3[s]だけLOW
        GPIO.output(TRIG_PIN, GPIO.LOW)
        time.sleep(0.3)
        # TRIGピンを0.00001[s]だけ出力(超音波発射)        
        GPIO.output(TRIG_PIN, True)
        time.sleep(0.00001)
        GPIO.output(TRIG_PIN, False)
        # HIGHの時間計測
        t = pulseIn(ECHO_PIN)
        # 距離[cm] = 音速[cm/s] * 時間[s]/2
        distance = v * t/2
        # 算出した距離をlistに格納
        distance_list.append(distance)
        
    # 平均を算出して出力
    ave_distance = sum(distance_list) / len (distance_list)
    print(round(ave_distance,2), "cm")

    # ピン設定解除
    GPIO.cleanup()

#while True:
try:
    # TRIGとECHOのGPIO番号   
    TRIG_PIN = 13
    ECHO_PIN = 12

    # 気温20.5[℃]の場合の音速[cm/s]
    v = 33150 + 60*20.5
    
    # ピン番号をGPIOで指定
    GPIO.setmode(GPIO.BCM)
    # TRIG_PINを出力, ECHO_PINを入力
    GPIO.setup(TRIG_PIN,GPIO.OUT)
    GPIO.setup(ECHO_PIN,GPIO.IN)
    GPIO.setwarnings(False)
        
    # 距離計測(TRIGピン番号, ECHO_PIN番号, 計測回数, 音速[cm/s])
    calc_distance(TRIG_PIN, ECHO_PIN, 10, v)
    
    time.sleep(1)                          #1秒間待つ

except KeyboardInterrupt:       #Ctrl+Cキーが押された
    GPIO.cleanup()              #GPIOをクリーンアップ
    sys.exit()                  #プログラム終了


```

## 結果

### 床置き（センサ高さ0㎝）
- 測定範囲外の1cmは誤差が大きく、仕様通り使用不可
- 30cm以上は精度良く測定できる
- 15cm未満で誤差が1cmを超えるところが複数個所あり
13cmと14㎝の誤差が大きい

| 距離(cm) | 測定値(cm) |誤差(cm)|
|:--------:|:----------:|:-------:|
| 1        | 5.10       | 使用範囲外   | 
| 2        | 3.33       | +1.33   | 
| 3        | 2.74       | -0.26   |
| 4        | 4.85       | +0.85   |
| 5        | 6.16       | +1.16   |
| 6        | 6.72       | +0.72   |
| 7        | 8.37       | +1.37   |
| 8        | 8.95       | +0.95   |
| 9        | 9.55       | +0.55   |
| 10       | 11.15      | +1.15   |
| 11       | 11.92      | +0.92   |
| 12       | 12.50      | +0.50   |
| 13       | 14.64      | +1.64   |
| 14       | 15.30      | +1.30   |
| 15       | 15.96      | +0.96   |
| 30       | 30.28      | +0.28   | 
| 50       | 49.49      | -0.51   | 
| 100      | 99.34      | -0.66   | 
| 150      | 150.17     | +0.17   | 
| 200      | 200.57     | +0.57   | 

誤差が大きい15㎝以下を追加検証をする

### 本1冊分浮かす（センサ高さ3.5cm）
超音波センサの床の反射が影響していると推定し、
センサの下に本を挟み高さを上げた
- 床置きより10㎝以下の誤差が小さくなった
- 12cm、14㎝に大きい誤差が出る

| 距離(cm) | 測定値(cm) |誤差(cm)|
|:--------:|:----------:|:-------:|
| 1        | 3.62       | 使用範囲外   |
| 2        | 1.85       | -0.15   |
| 3        | 2.73       | -0.27   |
| 4        | 4.54       | +0.54   |
| 5        | 6.00       | +1.00   |
| 6        | 6.89       | +0.89   |
| 7        | 7.48       | +0.48   |
| 8        | 8.32       | +0.32   |
| 9        | 9.85       | +0.85   |
| 10       | 10.54      | +0.54   |
| 11       | 11.95      | +0.95   |
| 12       | 14.65      | +2.65   |
| 13       | 13.34      | +0.34   |
| 14       | 15.34      | +1.34   |
| 15       | 15.01      | +0.01   |

### 本2冊分浮かす（センサ高さ5.5cm）
更に高くする
- 高さの変化で誤差が大きく出る部分が変化（12,14cm → 9,12,15cm）

| 距離(cm) | 測定値(cm) |誤差(cm)|
|:--------:|:----------:|:-------:|
| 1        | 2.43       | 使用範囲外   |
| 2        | 2.44       | +0.44   |
| 3        | 3.31       | +0.31   |
| 4        | 5.09       | +1.09   |
| 5        | 5.66       | +0.66   |
| 6        | 6.26       | +0.26   |
| 7        | 7.15       | +0.15   |
| 8        | 8.61       | +0.61   |
| 9        | 10.76      | +1.76   |
| 10       | 10.67      | +0.67   |
| 11       | 11.86      | +0.86   |
| 12       | 14.52      | +2.52   |
| 13       | 13.95      | +0.95   |
| 14       | 14.57      | +0.57   |
| 15       | 16.23      | +1.23   |

## まとめ
センサを浮かすことによって精度が変化することから
推定ではあるが今回のような測定では床に反射した超音波を
意識して設置する必要があることが分かりました

記述はしませんでしたが反射の影響を取り除く検証として
センサと箱の間の床に柔らかいハンカチを置きましたが効果はありませんでした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632401/f33e59c4-fecd-666b-6940-d93e569d362b.png)

不完全感がありますが今回はこの程度にしておこうと思います
- センサの設置角度が床に対して垂直に設置
- 障害物となる箱の置き方がセンサに水平に設置

のような管理が雑な部分が多かったので
また準備して挑戦したいと思います