超音波距離センサー HC-SR04 を動かしてみる

ラズパイで、距離計測をしてみました。
使ったのは、超音波距離センサー HC-SR04です。
その昔、学校にいたころに触ったこともあったので気軽に買ったのですが、
Node.js なのか、Raspberry Pi なのか何が理由かうまくいかなくて、
悩みながらの作成になってしまいました。

それでは、ハードウェアの接続からです。

目次

ハードウェア

以下の写真のように接続します。

と言ったってやっぱりわかりにくい・・・。
文字に起こすと以下の様になります。

    ラズパイ側          :    HC-SR04側
  物理ピン 2番(+5v)       =>  vcc ピン 
  物理ピン12番(GPIO18番)  =>  Echoピン
  物理ピン24番(GPIO14番)  =>  Trigピン
  物理ピン39番(GND)       =>  Gnd ピン

お次はコードです。

コード

とりあえず、どこかでディレクトリを作ってそのフォルダに移動、
npm initを実行

今回マイクロ秒単位で、時間を取得したいので
npm i microsecondsを実行し、パッケージを取得します。

uss.js として以下を保存します。(ultrasonic sensor なので)

uss.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const fs = require("fs");
const path = require("path");
const microsec = require("microseconds");

//使用するgpioピンナンバー
let trpinnum = 23;
let ecpinnum = 18;

//使用する仮想ファイルのパスを定義
let pathstr = "/sys/class/gpio/";
let gpio23 = path.join(pathstr, "gpio" + trpinnum);
let gpio18 = path.join(pathstr, "gpio" + ecpinnum);

let echostate = 0;

//gpio使用の準備
fs.writeFileSync(path.join(pathstr, "export"), trpinnum);
fs.writeFileSync(path.join(gpio23, "direction"), "out");
fs.writeFileSync(path.join(pathstr, "export"), ecpinnum);
fs.writeFileSync(path.join(gpio18, "direction"), "in");

let microsleep = (time) => {
let starttime = microsec.now();
while (true) {
if (microsec.since(starttime) > time) {
break;
}
}
};

//
let getdata3 = (trigpin, echopin) => {
let starttime = 0;
let startexetime = -1;
let time = 0;

//超音波発信
fs.writeFileSync(path.join(trigpin, "value"), 0);
microsleep(10);
fs.writeFileSync(path.join(trigpin, "value"), 1);
microsleep(10);
fs.writeFileSync(path.join(trigpin, "value"), 0);

//計測
startexetime = microsec.now();
while (fs.readFileSync(path.join(echopin, "value"), "utf8") == 0) {
if (microsec.since(startexetime) > 50000) return -1;
}
//echopinのup時刻を記録
starttime = microsec.now();
while (fs.readFileSync(path.join(echopin, "value"), "utf8") == 1) {
if (microsec.since(startexetime) > 50000) return -2;
}
//echopinのupになった時刻との差分でechopinがupだった時間を計算
time = microsec.since(starttime);

//0.017175=(33150 + 60 * 20) / 2 / 1000000
let kyori = time * 0.017175;
return kyori;
};

//gpioの読み込みルーチン処理
let loop = setInterval(async () => {
console.log("///////////////////////////");
let buf = getdata3(gpio23, gpio18);
console.log(":" + buf);
}, 500);

//Ctrl+Cでの終了時の処理を定義
process.on("SIGINT", () => {
clearInterval(loop);
fs.writeFileSync(path.join(pathstr, "unexport"), trpinnum);
fs.writeFileSync(path.join(pathstr, "unexport"), ecpinnum);
console.log("PROCESS EXIT");
process.exit(1);
});

実行は、sudo node uss.jsです。
終了するときはCtrl+Cを入力します。
実行すると以下のように表示されます。

///////////////////////////
:10.702154643917083
///////////////////////////
:14.519985409641265
///////////////////////////
:14.277577486896515
///////////////////////////
:21.380127002620696
///////////////////////////
:14.053941813468933
///////////////////////////
:15.71150110015869
///////////////////////////
:14.367024890041352
^CPROCESS EXIT

測定ができました。

しかし!

HC-SR04 の分解能は 0.3cm と記載があるんですが、
実際は+-3cm 程度の誤差が出てしまったんですが、これが直せない・・・。
調べた範囲では Raspberry Pi の raspbian がリアルタイム OS ではないから、
計測処理中に OS で割り込みが入っている?という結論でした。
(そういうリアルタイム向けの処理のために RTLinax ってものもあるようです。)

とはいえ、ドアの開け閉め検知や目の前の障害物の存在判定ぐらいなら問題なさそうです。

参考にしてくれる人がいれば何よりです。

ではでは。