ガイド · 読了まで13分

Arduinoのレーダープロジェクト:HC-SR04、SG90サーボ、Processingのガイド

Arduinoのレーダープロジェクトをステップごとに作ります。HC-SR04とSG90サーボの配線、角度と距離のコード、Processingの緑のレーダー画面、OLEDのミニレーダー、よくある問題の直し方を解説します。

Arduinoのレーダーは、HC-SR04超音波距離センサーをSG90サーボモーターに載せて0〜180度の間で振り、近くの物の方向と距離の両方を測り、その結果を映画のような緑のレーダー画面としてパソコンに表示するプロジェクトです。Arduinoは各角度で測定し、「角度,距離」の行をシリアルポートで送ります。パソコンで動く小さなProcessingのプログラムがその数値を読み、回転するスイープ線と、検知した物を赤い線で描きます。

このガイドでは、部品リストと配線図から、Arduinoのコード、Processingのレーダー画面まで、すべてのステップを説明します。パソコンが要らないOLEDディスプレイのミニレーダー、ESP32とRaspberry Pi Picoの版、スイープの速度と測定精度の調整方法、最もよくあるシリアルポートのエラーの直し方も扱います。安く、午後のうちに完成でき、学校の発表で最も注目を集めるArduinoのプロジェクトの1つです。

Arduino Uno
Arduino Uno
HC-SR04
HC-SR04
SG90サーボ
SG90サーボ
ブレッドボード
ブレッドボード

Arduinoのレーダーはどう動く?

システムの心臓部はHC-SR04です。センサーの2つの円筒の片方(T)が、人には聞こえない40 kHzの短い音を出し、音が物に当たって跳ね返ると、もう片方(R)が反響を拾います。Arduinoはこの往復の時間をマイクロ秒で測ります。音は空気中を約343 m/sで進むので、1センチメートル進むのに約29マイクロ秒かかります。往復で2回進むので、式は距離(cm)= 時間(µs)/ 58になります。サーボがセンサーを1ステップずつ回し、各ステップで測定を繰り返します。結果は「この角度の、この何センチ先に何かある」という極座標です。画面に描くには、x = 距離 × cos(角度)とy = 距離 × sin(角度)で角度と距離をピクセルの位置に変換します。

部品役割主な値
HC-SR04超音波での距離測定40 kHz、2〜400 cm、測定角度約15°、5 V、15 mA
SG90サーボセンサーを0〜180°回す0.1 s/60°(4.8 V)、トルク1.8 kg·cm、4.8〜6 V、9 g
Arduino Uno測定し、サーボを動かし、データを送る16 MHz、5 Vのロジックレベル、USBシリアルポート
Processingパソコンでレーダー画面を描く無料、Javaベース、Serialライブラリ付き
OLED SSD1306(任意)パソコンなしのミニレーダー画面128×64ピクセル、I2C、アドレス0x3C

Arduinoのレーダーの部品リスト

  • Arduino UnoかNanoと、データを通すUSBケーブル(充電専用ケーブルではパソコンにポートとして表示されません)。
  • HC-SR04超音波距離センサー。
  • SG90マイクロサーボと、袋に入っているプラスチックのホーン。
  • ブレッドボードと、オス-オス、オス-メスのジャンパーワイヤー8〜10本。
  • 470 µF(10 V以上)の電解コンデンサ:サーボが動いたときに電源電圧が下がるのを防ぎます。
  • センサーをサーボに固定するための両面テープ、ホットボンド、または小さな3Dプリントのブラケット。
  • 任意:0.96インチのI2C OLEDディスプレイ(パソコンなしの版用)と外部の5 V 2 AのACアダプター。

Arduinoのレーダーの配線図

接続Arduino UnoESP32Raspberry Pi Pico
HC-SR04 VCC5VVIN(5 V)VBUS(40番ピン)
HC-SR04 GNDGNDGNDGND
HC-SR04 TrigD10GPIO 26GP14
HC-SR04 EchoD11GPIO 27(分圧回路を通す)GP13(分圧回路を通す)
SG90の茶(GND)GNDGNDGND
SG90の赤(+5 V)5VVIN(5 V)VBUS(40番ピン)
SG90のオレンジ(信号)D9GPIO 13GP15

Unoでは、センサーとサーボの5 VとGNDの線をブレッドボードのプラスとマイナスのレールにつなぎ、そのレールをArduinoの5VとGNDのピンにつなぐと、配線がすっきりします。470 µFのコンデンサは、長い足(+)を5 Vに、本体の帯で示された短い足をGNDにして、同じレールに挿します。サーボによっては線の色が黒、赤、白のものがあります。その場合は黒がGND、白が信号です。ボードのピンの位置を確かめるにはピン配置ツールを使ってください。

HC-SR04を5 Vで動かすと、Echoピンも5 Vを出します。Arduino Unoなら問題ありませんが、ESP32とRaspberry Pi Picoのピンは5 Vに耐えられません。Echoの線に1 kΩと2 kΩの分圧回路を入れてください(Echo → 1 kΩ → GPIO、GPIO → 2 kΩ → GND)。ピンの電圧が約3.3 Vに下がります。値は分圧回路の計算ツールで確認できます。

センサーをサーボに取り付ける

SG90マイクロサーボモーター
SG90サーボ:茶がGND、赤が+5 V、オレンジが信号です。HC-SR04はサーボのプラスチックのホーンに接着します。

ホーンを付ける前に、サーボを90度にしておきます。radarServo.write(90)を含む短いテスト用スケッチを書き込み、サーボをその位置のままにするだけです。次に、センサーがまっすぐ前を向くようにホーンを取り付けます。こうすると0°が右、90°が正面、180°が左になり、Processingの画面の方向が実際の方向とぴったり一致します。HC-SR04を両面テープかホットボンドでホーンに貼るときは、2つの円筒が地面と平行になるようにしてください。センサーが下を向いていると、机の表面を物と間違えます。配線は180度回っても突っ張らないようにゆとりを持たせ、サーボは重い台か箱に固定します。ステップのたびに揺れる本体は、測定を台無しにします。

Arduinoのレーダーをステップごとに作る

  1. 上の表のとおりに部品を配線し、コンデンサを忘れずに付け、センサーをサーボに取り付けます。
  2. Arduino IDEを開きます。ServoライブラリはIDEに付属しているので、追加のインストールは不要です。
  3. 下のレーダーのコードを書き込みます。ボードやポートの選択で困ったら、Arduinoに書き込めないを参照してください。
  4. シリアルモニタを9600ボーで開きます。「30,45」のような角度と距離の行が流れていれば、Arduino側は準備完了です。そのあと、シリアルモニタを必ず閉じてください。
  5. パソコンにProcessing 4をインストールし、新しいスケッチを開いて、レーダー画面のコードを貼り付けます。
  6. Run(実行)を押します。下のコンソールにポートの一覧が表示され、レーダーのウィンドウが開きます。センサーの前で手を動かすと、赤い線が現れるはずです。

Arduinoのレーダーのコード

C++ (Arduino IDE)
1#include <Servo.h>
2
3const byte SERVO_PIN  = 9;     // SG90 turuncu kablo / SG90 orange wire
4const byte TRIG_PIN   = 10;    // HC-SR04 Trig
5const byte ECHO_PIN   = 11;    // HC-SR04 Echo
6const int  MIN_ANGLE  = 0;     // tarama başlangıcı / sweep start
7const int  MAX_ANGLE  = 180;   // tarama sonu / sweep end
8const int  STEP       = 2;     // adım başına derece / degrees per step
9const int  STEP_DELAY = 40;    // ölçümden önce bekleme (ms) / wait before measuring (ms)
10
11Servo radarServo;
12int angle = MIN_ANGLE;
13int dir = STEP;
14
15int measureCm() {
16  digitalWrite(TRIG_PIN, LOW);
17  delayMicroseconds(2);
18  digitalWrite(TRIG_PIN, HIGH);                         // 10 µs tetik darbesi / 10 µs trigger pulse
19  delayMicroseconds(10);
20  digitalWrite(TRIG_PIN, LOW);
21  unsigned long us = pulseIn(ECHO_PIN, HIGH, 30000UL);  // en çok 30 ms bekle / wait at most 30 ms
22  if (us == 0) return 0;                                // yankı yok = menzil dışı / no echo = out of range
23  return us / 58;                                       // gidiş-dönüş süresi → cm / round-trip time → cm
24}
25
26void setup() {
27  Serial.begin(9600);
28  pinMode(TRIG_PIN, OUTPUT);
29  pinMode(ECHO_PIN, INPUT);
30  radarServo.attach(SERVO_PIN);
31  radarServo.write(angle);
32  delay(500);                                           // başlangıç konumuna gitsin / reach the start position
33}
34
35void loop() {
36  radarServo.write(angle);                              // servoyu yeni açıya döndür / move the servo to the new angle
37  delay(STEP_DELAY);                                    // titreşim geçsin / let the vibration settle
38  int distance = measureCm();
39
40  Serial.print(angle);                                  // biçim: açı,mesafe / format: angle,distance
41  Serial.print(',');
42  Serial.println(distance);
43
44  if (angle + dir > MAX_ANGLE || angle + dir < MIN_ANGLE) dir = -dir;  // uçta yön değiştir / reverse at the ends
45  angle += dir;
46}

コードの仕組み

コードの部分役割
STEPとSTEP_DELAYサーボが1ステップで何度回るかと、測る前にどれだけ待つかを決める。この2つの定数でスイープの速度を調整する。
measureCm()Trigピンに10 µsのパルスを送り、pulseInでEchoピンがHIGHにとどまる時間を測り、58で割ってセンチメートルを求める。
pulseIn(..., 30000UL)最大30 ms待ち、反響が届かなければ0を返す。このタイムアウトがないと、何もない空間を向いているときに最大1秒止まってしまうことがある。
radarServo.write(angle)サーボを指定の角度に動かす。ライブラリが0〜180の値を544〜2400 µsのパルスに変える。
Serial.print ... println各測定を「角度,距離」の1行として送る。行末がProcessingに「データ完了」を伝える。
dir = -dir角度が180か0に達すると向きを反転させ、サーボが休まず往復するようにする。

データをこれほど単純にしているのには理由があります。カンマで区切った1行はシリアルモニタで読みやすく、Processing、Python、Excelで1行のコードで解析できます。距離0は「この角度の範囲内には何もない」という意味で、Processingはその値を描きません。同じデータをArduino IDEのシリアルプロッタで開くと、角度がのこぎりの歯のように上下し、センサーが物を通過するたびに距離が跳ね上がるのがわかります。このプロジェクトではdelayを使って問題ありません。レーダーを動かしながらボタンを読んだりLEDを点けたりしたい場合は、loopをmillis()を使って書き直してください。

サーボがちょうど0°や180°でジーと鳴って震える場合は、機械的な限界に当たっています。スイープを10〜170°に狭めるか、radarServo.attach(SERVO_PIN, 500, 2400)でサーボを起動し、パルスの範囲をサーボに合わせて調整してください。

Processingの緑のレーダー画面

Processingはビジュアルプログラミングのために作られた無料のJavaベースのプログラムで、画面はArduino IDEによく似ています。コードを書いてRunを押すだけです。シリアル通信に必要なSerialライブラリはProcessingに付属しています。Sketch > Import Library(ライブラリのインポート)のメニューから追加することも、コードの先頭のimportの行に任せることもできます。下のコードはArduino IDEではなく、Processingに入れます。

C++ (Arduino IDE)
1// Processing 4 çizimi: Arduino IDE'ye değil, Processing'e yapıştır / Processing 4 sketch: paste into Processing, not the Arduino IDE
2import processing.serial.*;                       // Serial kütüphanesi / Serial library
3
4Serial port;
5int angle = 0;                                    // son açı / last angle
6int distance = 0;                                 // son mesafe (cm) / last distance (cm)
7final int MAX_CM = 100;                           // ekrandaki en uzak mesafe / farthest distance on screen
8float R;                                          // radar yarıçapı (piksel) / radar radius (pixels)
9
10void setup() {
11  size(1200, 700);
12  R = width * 0.45;
13  printArray(Serial.list());                      // portları konsola yaz / print the ports to the console
14  String portName = Serial.list()[0];             // gerekirse [1] ya da "COM4" yaz / use [1] or "COM4" if needed
15  port = new Serial(this, portName, 9600);        // Arduino ile aynı baud / same baud as the Arduino
16  port.bufferUntil('\n');                         // satır sonunu bekle / wait for the end of a line
17  background(0);
18}
19
20void draw() {
21  noStroke();
22  fill(0, 12);                                    // yarı saydam siyah: solan iz / translucent black: fading trail
23  rect(0, 0, width, height);
24  translate(width / 2, height - 60);              // radar merkezi alt ortada / radar centre at the bottom middle
25  drawGrid();
26  drawSweep();
27  drawObject();
28  drawLabels();
29}
30
31void drawGrid() {
32  noFill();
33  strokeWeight(2);
34  stroke(98, 245, 31);
35  for (int i = 1; i <= 4; i++) {
36    float d = 2 * R * i / 4;
37    arc(0, 0, d, d, PI, TWO_PI);                  // 25-50-75-100 cm yayları / 25-50-75-100 cm arcs
38  }
39  for (int a = 0; a <= 180; a += 30) {
40    line(0, 0, R * cos(radians(a)), -R * sin(radians(a)));   // 30 derecelik çizgiler / 30-degree lines
41  }
42}
43
44void drawSweep() {
45  strokeWeight(5);
46  stroke(30, 250, 60);
47  line(0, 0, R * cos(radians(angle)), -R * sin(radians(angle)));   // tarama çizgisi / sweep line
48}
49
50void drawObject() {
51  if (distance <= 0 || distance > MAX_CM) return;  // menzil dışı / out of range
52  float p = R * distance / MAX_CM;                 // cm → piksel / cm → pixels
53  float c = cos(radians(angle));
54  float s = sin(radians(angle));
55  strokeWeight(7);
56  stroke(255, 10, 10);
57  line(p * c, -p * s, R * c, -R * s);              // nesneden dışa kırmızı çizgi / red line from the object outwards
58}
59
60void drawLabels() {
61  resetMatrix();
62  noStroke();
63  fill(0);
64  rect(0, height - 40, width, 40);                 // yazı şeridi / text strip
65  fill(98, 245, 31);
66  textSize(20);
67  for (int i = 1; i <= 4; i++) {
68    text(MAX_CM * i / 4 + " cm", width / 2 + R * i / 4 + 4, height - 66);   // yay etiketleri / arc labels
69  }
70  String d = (distance > 0 && distance <= MAX_CM) ? distance + " cm" : "-";
71  text("Açı / Angle: " + angle + "°", 40, height - 12);
72  text("Mesafe / Distance: " + d, width / 2, height - 12);
73}
74
75void serialEvent(Serial p) {
76  String msg = p.readStringUntil('\n');            // "açı,mesafe" satırı / "angle,distance" line
77  if (msg == null) return;
78  String[] parts = split(trim(msg), ',');
79  if (parts.length == 2) {
80    angle = int(parts[0]);
81    distance = int(parts[1]);
82  }
83}

コードで最も重要な部分はserialEvent()関数です。bufferUntil('\n')のおかげで、Processingは行末が届くまで受信したデータをためておき、完全な1行がそろうとこの関数を自動で呼びます。行はtrim()で空白と\r文字を取り除かれ、split()でカンマのところで2つに分けられ、int()で数値に変換されます。一方、draw()は1秒に約60回動きます。まず画面に半透明の黒い長方形を塗り(fill(0, 12))、次に格子、スイープ線、物を描き直します。古い線はフレームごとに少しずつ暗くなるので、本物のレーダー画面で見られる消えていく残像が自然に現れます。

設定場所役割
MAX_CM = 100Processing画面が扱う最も遠い距離。外側の円弧が100 cm、内側の円弧が25、50、75 cmを示す。
Serial.list()[0]Processingパソコンの最初のシリアルポートを選ぶ。Arduinoが一覧のもっと下にあるなら番号を変えるか、「COM4」のように名前を直接書く。
9600両方ボーレート。ArduinoのSerial.beginとProcessingの値は一致させる必要がある。
fill(0, 12)Processing消えていく速さ。5なら長い残像、30なら残像のほとんどない画像になる。
size(1200, 700)Processingウィンドウの大きさ。画面に収まらなければ小さくする。半径は幅に合わせて決まるので、高さは幅の半分より大きくしておく。

サーボの速度とスイープの時間の調整

データシートによると、SG90は4.8 Vで60度を0.1秒で回るので、2度のステップは数ミリ秒で終わります。スイープの時間を実際に決めるのはサーボではなく、各ステップでの待ち時間と測定時間です。サーボが止まったあと、ホーンとセンサーは一瞬揺れます。その瞬間に測ると距離が跳ねます。HC-SR04のメーカーも、2回の測定の間を約60 ms空けるよう推奨しています。前のパルスの反響が部屋の中で跳ね返り、次の測定に紛れ込むことがあるからです。実際には、1ステップあたり合計50〜60 ms(待ち + 測定)がちょうどよいバランスです。1回のスイープにかかる時間はおよそ(180 / ステップ + 1) × ステップの時間です。

ステップSTEP_DELAY1回のスイープ(0→180°)結果
1°20 ms約7 s最もなめらかな画像だが遅い。同じ物を何度も測る。
2°40 ms約5 sおすすめの設定:なめらかで安定。
3°50 ms約4 s速い。センサーのビームが15°あるので、物を見逃さない。
5°70 ms約3.5 s画面がカクカクして見える。ステップが大きいほど揺れも大きいので、待ち時間を長くする必要がある。

動きをなめらかにするには、ステップを小さく、待ち時間を短くします。速さを求めるなら、ステップを大きくして待ち時間を少し延ばします。スイープを関心のある範囲(たとえば45〜135°)に限ると、時間も半分になります。サーボが勝手にピクッと動く、Arduinoがときどき再起動する、Processingの接続が切れる、といった場合は、問題は速度ではなく電源です。SG90は動いている間に約100〜250 mA、負荷がかかると最大600 mAを引くことがあります。コンデンサを追加し、必要ならサーボをArduinoとGNDを共通にした外部の5 V電源から給電してください。

測定精度を上げる

HC-SR04は資料上は3 mmの分解能をうたっていますが、レーダーの精度を決めるのは3つのこと、つまりビームの幅、音速、表面の種類です。センサーの音のビームは約15度の円錐のように広がります。そのため、50 cm先の細いペンは画面上で点ではなく、幅15〜20度の円弧として現れます。レーダーの角度分解能を決めるのはサーボのステップではなくこのビームで、物の実際の方向は円弧の中央です。音速は温度で変わり、v = 331.3 + 0.606 × T(m/s、Tは°C)で計算します。コードの58という数は約22 °Cに相当します。寒いガレージでは、測定値が数パーセント長く出ます。

温度音速1 cmあたりの往復時間100 cm先の物(/58で計算)
0 °C331.3 m/s60.4 µs約104 cm
10 °C337.4 m/s59.3 µs約102 cm
20 °C343.4 m/s58.2 µs約100 cm
30 °C349.5 m/s57.2 µs約99 cm
40 °C355.5 m/s56.3 µs約97 cm

DHT11やDS18B20で室温を測って音速を考慮したり、3回の測定の中央値を取って単発の誤った値を除いたりできます。下の2つの関数をメインのコードに追加し、loop()の中のmeasureCm()の呼び出しをどちらかに置き換えるだけです。中央値のフィルターは1ステップで3回測るので、スイープが少し遅くなります。

C++ (Arduino IDE)
1// Ana radar koduna eklenecek fonksiyonlar / functions to add to the main radar sketch
2
3// Sıcaklığa göre düzeltilmiş ölçüm / temperature-compensated reading
4int measureCmTemp(float tempC) {
5  digitalWrite(TRIG_PIN, LOW);
6  delayMicroseconds(2);
7  digitalWrite(TRIG_PIN, HIGH);
8  delayMicroseconds(10);
9  digitalWrite(TRIG_PIN, LOW);
10  unsigned long us = pulseIn(ECHO_PIN, HIGH, 30000UL);
11  if (us == 0) return 0;                                // yankı yok / no echo
12  float v = 331.3 + 0.606 * tempC;                      // ses hızı (m/s) / speed of sound (m/s)
13  return (int)(us * v / 20000.0 + 0.5);                 // µs × (m/s) / 20000 = cm
14}
15
16// Üç ölçümün ortancası: tek seferlik sıçramaları atar / median of three: drops one-off spikes
17int medianCm() {
18  int a = measureCm(); delay(20);
19  int b = measureCm(); delay(20);
20  int c = measureCm();
21  return max(min(a, b), min(max(a, b), c));
22}
23
24// loop() içinde kullanım / use inside loop():
25// int distance = medianCm();
26// int distance = measureCmTemp(18.5);                  // sıcaklık DHT11'den gelebilir / temperature can come from a DHT11
  • 柔らかい面(カーテン、布、スポンジ)は音を吸収します。物があるのに表示されないことがあります。
  • 斜めの面は音を別の方向に反射します。センサーから45°以上回った平らな箱は、何もない空間と読まれることがあります。
  • 2 cmより近い物は測れません。実際には3〜4 cm未満は信頼できないものとして扱ってください。
  • 部屋の角は多重反射を起こし、実際にはない遠くの「ゴースト」を作ることがあります。何もない開けた場所でテストしてください。
  • 測定範囲:データシートでは400 cmですが、小さな物で安定して測れるのはたいてい約2メートルまでです。MAX_CMはそれに合わせて選んでください。

OLEDディスプレイのミニレーダー(パソコンなし)

レーダーをパソコンの要らない、電池で動く持ち運べる機器にしたい場合は、Processingの代わりに0.96インチのOLEDディスプレイを使えます。128×64のSSD1306の画面の下辺の中央がレーダーの中心になり、半径60ピクセルの半円、スイープ線、各角度で最後に測った物を示す点が描かれます。ディスプレイの配線(SDA → A4、SCL → A5)とライブラリのインストールはSSD1306 OLEDディスプレイのガイドを参照してください。HC-SR04とサーボの配線は上と同じです。

0.96インチI2C OLEDディスプレイSSD1306
0.96インチのOLEDディスプレイ:パソコンなしのミニレーダーでは、128×64ピクセルの半円のスイープ画面になります。
C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_GFX.h>
3#include <Adafruit_SSD1306.h>
4#include <Servo.h>
5
6const byte SERVO_PIN = 9;                 // SG90 sinyal / SG90 signal
7const byte TRIG_PIN  = 10;                // HC-SR04 Trig
8const byte ECHO_PIN  = 11;                // HC-SR04 Echo
9const int  MAX_CM    = 50;                // ekrandaki en uzak mesafe / farthest distance on screen
10const int  STEP      = 3;                 // adım başına derece / degrees per step
11const int  CX = 64, CY = 63, R = 60;      // radar merkezi ve yarıçapı (piksel) / radar centre and radius (px)
12
13Adafruit_SSD1306 display(128, 64, &Wire, -1);
14Servo radarServo;
15uint8_t dist[180 / STEP + 1];             // her açının son ölçümü (cm) / last reading per angle (cm)
16int angle = 0;
17int dir = STEP;
18
19int measureCm() {
20  digitalWrite(TRIG_PIN, LOW);
21  delayMicroseconds(2);
22  digitalWrite(TRIG_PIN, HIGH);
23  delayMicroseconds(10);
24  digitalWrite(TRIG_PIN, LOW);
25  unsigned long us = pulseIn(ECHO_PIN, HIGH, 30000UL);
26  if (us == 0) return 0;                  // yankı yok / no echo
27  return us / 58;
28}
29
30void setup() {
31  pinMode(TRIG_PIN, OUTPUT);
32  pinMode(ECHO_PIN, INPUT);
33  radarServo.attach(SERVO_PIN);
34  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
35    for (;;);                             // ekran bulunamadı / display not found
36  }
37  display.setTextColor(SSD1306_WHITE);
38}
39
40void loop() {
41  radarServo.write(angle);
42  delay(20);                              // çizim de zaman aldığı için kısa / short, drawing takes time too
43  int d = measureCm();
44  dist[angle / STEP] = (d > 255) ? 255 : d;   // byte'a sığdır / fit into a byte
45
46  display.clearDisplay();
47  display.drawCircle(CX, CY, R, SSD1306_WHITE);       // dış yay, alt yarısı ekran dışında / outer arc, lower half off-screen
48  display.drawCircle(CX, CY, R / 2, SSD1306_WHITE);   // orta yay = MAX_CM / 2 / middle arc = MAX_CM / 2
49  for (int i = 0; i <= 180 / STEP; i++) {
50    if (dist[i] > 0 && dist[i] <= MAX_CM) {
51      float rad = radians(i * STEP);
52      int r = map(dist[i], 0, MAX_CM, 0, R);          // cm → piksel / cm → pixels
53      display.fillCircle(CX + r * cos(rad), CY - r * sin(rad), 1, SSD1306_WHITE);
54    }
55  }
56  float rad = radians(angle);
57  display.drawLine(CX, CY, CX + R * cos(rad), CY - R * sin(rad), SSD1306_WHITE);  // tarama çizgisi / sweep line
58  display.setCursor(0, 0);
59  display.print(angle);                   // açı / angle
60  display.setCursor(98, 0);
61  display.print(d);                       // mesafe / distance
62  display.print(F("cm"));
63  display.display();
64
65  if (angle + dir > 180 || angle + dir < 0) dir = -dir;   // uçta yön değiştir / reverse at the ends
66  angle += dir;
67}

この版では距離を角度ごとにdist配列に保存しているので、スイープ線が通り過ぎても点が画面に残り、次の通過で更新されます。円の中心が画面の下辺にあるので、下半分は画面の外になってライブラリが描かず、きれいな半円になります。1フレームを画面に送るのに400 kHzのI2Cで約25 msかかるので、待ち時間を20 msに縮め、ステップを3度に上げました。1回のスイープは約4〜5秒です。小さな画面では50 cmのスケールのほうが読みやすいので、MAX_CMは50にしています。Unoの2 KBのメモリの半分がディスプレイのバッファに使われるので、配列をバイト(uint8_t)にしておくことが重要です。

ESP32でArduinoのレーダーを作る

ArduinoのServoライブラリはESP32では動きません。代わりにライブラリマネージャからESP32Servoライブラリをインストールしてください。コードの残りはほぼ同じです。ESP32は3.3 Vで動くので、HC-SR04のEchoの線の分圧回路を忘れないでください。一方、Trigの線の3.3 Vのパルスは、ほとんどのセンサーをトリガーするのに十分です。このコードは115200ボーを使うので、Processingのコードの9600も115200に変えてください。ESP32のWi-Fiを使えば、あとでレーダーのデータをパソコンではなくスマホのWebページに送ることもできます。ESP32が初めてならESP32とは?から始めてください。

C++ (Arduino IDE)
1#include <ESP32Servo.h>                // Kütüphane Yöneticisi: ESP32Servo / Library Manager: ESP32Servo
2
3const int SERVO_PIN  = 13;             // SG90 sinyal / SG90 signal
4const int TRIG_PIN   = 26;             // HC-SR04 Trig
5const int ECHO_PIN   = 27;             // 1k/2k bölücüden / through the 1k/2k divider
6const int STEP       = 2;              // adım başına derece / degrees per step
7const int STEP_DELAY = 40;             // ölçümden önce bekleme (ms) / wait before measuring (ms)
8
9Servo radarServo;
10int angle = 0;
11int dir = STEP;
12
13int measureCm() {
14  digitalWrite(TRIG_PIN, LOW);
15  delayMicroseconds(2);
16  digitalWrite(TRIG_PIN, HIGH);
17  delayMicroseconds(10);
18  digitalWrite(TRIG_PIN, LOW);
19  unsigned long us = pulseIn(ECHO_PIN, HIGH, 30000UL);
20  if (us == 0) return 0;               // yankı yok / no echo
21  return us / 58;
22}
23
24void setup() {
25  Serial.begin(115200);                       // Processing'de de 115200 / 115200 in Processing too
26  pinMode(TRIG_PIN, OUTPUT);
27  pinMode(ECHO_PIN, INPUT);
28  radarServo.setPeriodHertz(50);              // standart servo 50 Hz / standard servo 50 Hz
29  radarServo.attach(SERVO_PIN, 500, 2400);    // SG90 darbe aralığı (µs) / SG90 pulse range (µs)
30  radarServo.write(angle);
31  delay(500);
32}
33
34void loop() {
35  radarServo.write(angle);
36  delay(STEP_DELAY);
37  int distance = measureCm();
38  Serial.printf("%d,%d\n", angle, distance);  // açı,mesafe / angle,distance
39  if (angle + dir > 180 || angle + dir < 0) dir = -dir;   // uçta yön değiştir / reverse at the ends
40  angle += dir;
41}

Raspberry Pi PicoとMicroPythonでレーダーを作る

Picoでは専用のサーボライブラリは不要で、50 HzのPWM信号のパルス幅をduty_ns()で設定するだけです。反響の時間はmachine.time_pulse_us()で測ります。タイムアウトすると負の数を返します。Picoのピンも3.3 Vなので、Echoの線の分圧回路は必須です。Thonnyでコードをmain.pyとしてPicoに保存したら、Thonnyを閉じてください。そうしないとポートが使われたままになり、Processingが接続できません。PicoはUSBで仮想シリアルポートを開くので、Processingのボーレートの値は関係なく、同じレーダー画面がそのまま動きます。

MicroPython
1from machine import Pin, PWM, time_pulse_us
2import time
3
4servo = PWM(Pin(15))                     # SG90 sinyal / SG90 signal
5servo.freq(50)                           # standart servo 50 Hz / standard servo 50 Hz
6trig = Pin(14, Pin.OUT)
7echo = Pin(13, Pin.IN)                   # bölücüden gelir / comes through the divider
8
9def set_angle(a):
10    # 0° = 500 µs, 180° = 2400 µs darbe / pulse
11    servo.duty_ns(500000 + a * 1900000 // 180)
12
13def measure_cm():
14    trig.value(0)
15    time.sleep_us(2)
16    trig.value(1)                        # 10 µs tetik / 10 µs trigger
17    time.sleep_us(10)
18    trig.value(0)
19    t = time_pulse_us(echo, 1, 30000)    # en çok 30 ms bekle / wait at most 30 ms
20    if t < 0:
21        return 0                         # yankı yok / no echo
22    return t // 58
23
24angle, step = 0, 2
25set_angle(angle)
26time.sleep_ms(500)
27
28while True:
29    set_angle(angle)
30    time.sleep_ms(40)                    # servo otursun / let the servo settle
31    print("{},{}".format(angle, measure_cm()))   # açı,mesafe / angle,distance
32    if angle + step > 180 or angle + step < 0:
33        step = -step                     # uçta yön değiştir / reverse at the ends
34    angle += step

よくある間違いと解決法

症状またはエラーメッセージ原因解決法
エラー:Error opening serial port COM3: Port busyArduino IDEのシリアルモニタかシリアルプロッタ、Thonny、または2つ目のProcessingのウィンドウがポートを使っている。シリアルモニタとほかのプログラムを閉じ、Processingのスケッチをもう一度実行する。必要ならUSBケーブルを抜いて挿し直す。
エラー:ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0(ポートの一覧が空)Serial.list()が空:ボードがつながっていない、ケーブルが充電専用、またはCH340のドライバが入っていない。データ通信用ケーブルを使い、ドライバをインストールする。Arduino IDEがポートを認識すれば、Processingも認識する。
ウィンドウは開くが、スイープ線が動かない違うポートを選んでいる(Windowsでは一覧の最初がCOM1のことが多い)か、ボーレートが違う。コンソールの一覧でArduinoの位置を探してSerial.list()[1]などに変え、両側で同じボーレートを使う。
エラー:The package “processing.serial” does not exist(パッケージがない). You might be missing a library.Serialライブラリが見つからない。Processing 4.4.8のインストーラーにはこれが含まれていなかった。Processingを新しい版に更新するか4.4.7を使い、importの行が正しく書かれているか確認する。
エラー:Error opening serial port /dev/ttyACM0: Permission deniedLinuxで、自分のユーザーにシリアルポートへのアクセスが許可されていない。自分のユーザーをdialoutグループに追加し(sudo usermod -a -G dialout $USER)、ログアウトしてログインし直す。
Processingを開いているとArduinoにコードを書き込めないポートをProcessingが使っている。書き込む前に、StopボタンでProcessingのスケッチを止める。
距離が常に0か、でたらめTrigとEchoが逆、センサーに電源がない、または物が範囲外。配線を表と照らし合わせ、まず距離だけを測る簡単なスケッチでテストする。
サーボが震え、Arduinoが勝手にリセットされるUSBポートがサーボの電流を供給できず、電圧が下がっている。470 µFのコンデンサを追加するか、GNDを共通にした外部の5 V電源からサーボに給電する。
レーダー画面で左右が逆になるサーボかセンサーが逆向きに取り付けられている。Processingでangle = 180 - int(parts[0]);と書くか、ホーンを外して向きを変えて付け直す。
デバッグはこの順番で進めてください。まずシリアルモニタで「角度,距離」の行を確認し、次にシリアルモニタを閉じてProcessingを実行します。Arduino側でデータが正しく流れていれば、問題はほぼ必ずポートの選択かボーレートです。

レーダーのプロジェクトをさらに発展させるアイデア

  • 警報付きの防犯レーダー:物が30 cmより近づいたらブザーを鳴らして赤いLEDを点けます。Arduinoの警報システムのアイデアと組み合わせましょう。
  • 障害物を避けるロボット:レーダーをロボットの前に付けます。ロボットが止まると左右をスキャンし、最も開けた方向へ曲がります。詳しくは障害物を避けるロボットにあります。
  • 部屋の地図:スイープのデータをSDカードモジュールに保存し、パソコンで点群として描きます。
  • 無線のレーダー:ESP32から測定値をWi-Fiでスマホのページに送ります。ESP32のWebサーバーを使えばリアルタイムで描けます。
  • より鮮明な地図:HC-SR04の代わりにビームの狭いレーザー(ToF)センサーを使います。選択肢は距離センサーの比較にあります。
  • スマート白杖:距離センサーと振動モーターで、視覚障害のある人に障害物を知らせる杖を作ります。スマート白杖のプロジェクトを参照してください。

Arduinoのレーダープロジェクトは、距離センサー、サーボ制御、シリアル通信、パソコンでのグラフィックの描画を1つの作品にまとめているので、初心者の次のステップに最適です。基本を固めるには、HC-SR04距離センサーとサーボモーターのガイドも見てください。BlueGraysアプリでは、HC-SR04での距離測定、駐車センサー、サーボモーターの使い方、障害物を避けるロボットのプロジェクトを、回路図と解説付きのコードでステップごとに紹介しています。

よくある質問

Arduinoのレーダーは本物のレーダー?

技術的にはいいえ、ソナーです。本物のレーダーは電波を送りますが、このプロジェクトではHC-SR04の40 kHzの音波を使います。「Arduinoのレーダー」と呼ばれるのは、動作の原理が同じで(波を送り、反響の時間を測り、方向と一緒に描く)、表示がレーダー画面のように見えるからです。

Arduinoのレーダーはどのくらいの距離まで検知できる?

HC-SR04のデータシート上の範囲は2〜400 cmです。壁のような大きく平らな面なら3〜4メートルまで測れますが、椅子の脚や人のような物では、確実に測れるのはたいてい1.5〜2メートルです。ProcessingのコードのMAX_CMをこの範囲に合わせると、最もはっきりした画像になります。

Arduinoのレーダーを作るには何が必要?

Arduino UnoかNano、HC-SR04超音波センサー、SG90サーボ、ブレッドボード、ジャンパーワイヤー数本、サーボ用の470 µFのコンデンサがあれば十分です。無料のProcessingのプログラムがパソコンにレーダー画面を描きます。パソコンなしの版には、0.96インチのOLEDディスプレイを追加します。

ProcessingなしでもArduinoのレーダーは作れる?

はい。SSD1306 OLEDディスプレイを使えば、Arduinoで直接レーダーを描き、電池で動く持ち運べる機器にできます。ディスプレイがなくても、Arduino IDEのシリアルプロッタで角度と距離の値をグラフとして見られます。

Processingの「Port busy」エラーを直すには?

このエラーは、ほかのプログラムがシリアルポートを使っていることを意味します。Arduino IDEのシリアルモニタとシリアルプロッタ、Thonny、開いたままのほかのProcessingのスケッチを閉じて、もう一度試してください。それでも直らなければ、USBケーブルを抜いて挿し直してください。

サーボが震えてArduinoがリセットされるのはなぜ?

SG90は動くときに短時間大きな電流を引き、USBで給電したArduinoの5 Vラインが一瞬下がります。サーボの電源に470 µFのコンデンサを入れればたいてい解決します。解決しない場合は、ArduinoとGNDを共通にした外部の5 V電源からサーボに給電してください。

全プロジェクトを見る
HC-SR04で距離を測定(シリアルモニタ表示)
#27初級

HC-SR04で距離を測定(シリアルモニタ表示)

HC-SR04センサーは、コウモリが暗闇で道を見つけるのと同じ仕組みで動作します。人には聞こえない音を出し、その反響を捉えて前方の物体までの距離を測ります。Arduinoは0.5秒ごとに距離をセンチメートル単位でコンピューターの画面に表示し、3〜100センチメートルの測定値を示します。駐車センサーや障害物を避けるロボットなどのプロジェクトの基礎になります。

4.6(46)
71
サーボモーターの使い方
#35初級
プレミアム

サーボモーターの使い方

小型サーボモーターのアームが、一方の端、中央、もう一方の端の3つの位置へ順番に回り、それぞれの位置で2秒間止まります。Arduinoが保持する角度をモーターに指示すると、モーターはその位置まで動いて止まります。ロボットアームや自動開閉するふたが動く仕組みを学べます。

4.7(48)
67
HC-SR04で作る駐車センサー
#28中級
プレミアム

HC-SR04で作る駐車センサー

車の駐車センサーを小さく再現します。物体が近づくほど、ビープ音の間隔が短くなります。距離センサーが前方の物体までの距離を測り、20センチメートルより遠ければ緑のLEDが点灯します。近づくと赤いLEDが点滅してブザーが鳴り、さらに近づくほど警告の間隔が短くなります。車が壁にぶつからずに駐車できる仕組みを、机の上で確かめられます。

4.8(64)
85
障害物回避ロボット
#81上級
プレミアム

障害物回避ロボット

自分で進む道を見つける、賢い車を作ってみませんか?障害物回避ロボットは、前面の超音波センサーで周囲を常に調べます。障害物を見つけると停止し、サーボモーターで左右を確認して、最も障害物の少ない方向を選んで進みます。自動運転の基礎を学ぶのにぴったりの入門プロジェクトです。

5.0(38)
49
ESP32とHC-SR04で作る超音波距離計
#116中級

ESP32とHC-SR04で作る超音波距離計

HC-SR04超音波センサーは、発した音波が物体に反射して戻るまでの時間(Time of Flight)を測定して距離を計算します。このプロジェクトでは、ESP32からセンサーのTRIGピンに10マイクロ秒のパルスを送り、ECHOピンからの反射波の持続時間をpulseIn()で読み取ります。計算した距離をcm単位で16x2 I2C LCDに表示します。HC-SR04は5Vで動作するため、ECHOピンには分圧回路が必須です。分圧しないとESP32の3.3V GPIOが破損します。

4.9(28)
45
ESP32駐車センサー(HC-SR04 + 5連LEDバー + ブザー + OLED)
#142上級
プレミアム

ESP32駐車センサー(HC-SR04 + 5連LEDバー + ブザー + OLED)

車の駐車センサーをESP32で作ります!HC-SR04超音波センサーで前方の物体までの距離を測ります。距離が縮まるほど5連LEDバーが緑 → 黄 → 赤の順に点灯し、ブザーの鳴る間隔が短くなります。10 cm未満まで近づくとアラームが鳴り続け、すべてのLEDが点灯します。SSD1306 OLEDには現在の距離をcm単位の大きな文字で表示し、状態メッセージ(落ち着いて / 注意 / 接近 / 停止! / 危険)とLEDバーのシミュレーションも表示します。HC-SR04のpulseIn、複数のデジタル出力、ブザーを学べる定番のプロジェクトです。

0.0(0)
14
LCDディスプレイを使った距離測定
#31上級
プレミアム

LCDディスプレイを使った距離測定

定規を使わずに距離を測る装置です。センサーの前で手を近づけたり遠ざけたりすると、数値が変わります。HC-SR04が音波を出して反響を捉え、Arduinoが距離をセンチメートル単位で計算し、2行のLCDディスプレイに1秒間に数回表示します。車の駐車センサーの仕組みを、机の上で確かめられます。

4.6(45)
59