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のレーダーはどう動く?
システムの心臓部は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 Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| HC-SR04 VCC | 5V | VIN(5 V) | VBUS(40番ピン) |
| HC-SR04 GND | GND | GND | GND |
| HC-SR04 Trig | D10 | GPIO 26 | GP14 |
| HC-SR04 Echo | D11 | GPIO 27(分圧回路を通す) | GP13(分圧回路を通す) |
| SG90の茶(GND) | GND | GND | GND |
| SG90の赤(+5 V) | 5V | VIN(5 V) | VBUS(40番ピン) |
| SG90のオレンジ(信号) | D9 | GPIO 13 | GP15 |
Unoでは、センサーとサーボの5 VとGNDの線をブレッドボードのプラスとマイナスのレールにつなぎ、そのレールをArduinoの5VとGNDのピンにつなぐと、配線がすっきりします。470 µFのコンデンサは、長い足(+)を5 Vに、本体の帯で示された短い足をGNDにして、同じレールに挿します。サーボによっては線の色が黒、赤、白のものがあります。その場合は黒がGND、白が信号です。ボードのピンの位置を確かめるにはピン配置ツールを使ってください。
センサーをサーボに取り付ける

ホーンを付ける前に、サーボを90度にしておきます。radarServo.write(90)を含む短いテスト用スケッチを書き込み、サーボをその位置のままにするだけです。次に、センサーがまっすぐ前を向くようにホーンを取り付けます。こうすると0°が右、90°が正面、180°が左になり、Processingの画面の方向が実際の方向とぴったり一致します。HC-SR04を両面テープかホットボンドでホーンに貼るときは、2つの円筒が地面と平行になるようにしてください。センサーが下を向いていると、机の表面を物と間違えます。配線は180度回っても突っ張らないようにゆとりを持たせ、サーボは重い台か箱に固定します。ステップのたびに揺れる本体は、測定を台無しにします。
Arduinoのレーダーをステップごとに作る
- 上の表のとおりに部品を配線し、コンデンサを忘れずに付け、センサーをサーボに取り付けます。
- Arduino IDEを開きます。ServoライブラリはIDEに付属しているので、追加のインストールは不要です。
- 下のレーダーのコードを書き込みます。ボードやポートの選択で困ったら、Arduinoに書き込めないを参照してください。
- シリアルモニタを9600ボーで開きます。「30,45」のような角度と距離の行が流れていれば、Arduino側は準備完了です。そのあと、シリアルモニタを必ず閉じてください。
- パソコンにProcessing 4をインストールし、新しいスケッチを開いて、レーダー画面のコードを貼り付けます。
- Run(実行)を押します。下のコンソールにポートの一覧が表示され、レーダーのウィンドウが開きます。センサーの前で手を動かすと、赤い線が現れるはずです。
Arduinoのレーダーのコード
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()を使って書き直してください。
Processingの緑のレーダー画面
Processingはビジュアルプログラミングのために作られた無料のJavaベースのプログラムで、画面はArduino IDEによく似ています。コードを書いてRunを押すだけです。シリアル通信に必要なSerialライブラリはProcessingに付属しています。Sketch > Import Library(ライブラリのインポート)のメニューから追加することも、コードの先頭のimportの行に任せることもできます。下のコードはArduino IDEではなく、Processingに入れます。
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 = 100 | Processing | 画面が扱う最も遠い距離。外側の円弧が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_DELAY | 1回のスイープ(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 °C | 331.3 m/s | 60.4 µs | 約104 cm |
| 10 °C | 337.4 m/s | 59.3 µs | 約102 cm |
| 20 °C | 343.4 m/s | 58.2 µs | 約100 cm |
| 30 °C | 349.5 m/s | 57.2 µs | 約99 cm |
| 40 °C | 355.5 m/s | 56.3 µs | 約97 cm |
DHT11やDS18B20で室温を測って音速を考慮したり、3回の測定の中央値を取って単発の誤った値を除いたりできます。下の2つの関数をメインのコードに追加し、loop()の中のmeasureCm()の呼び出しをどちらかに置き換えるだけです。中央値のフィルターは1ステップで3回測るので、スイープが少し遅くなります。
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とサーボの配線は上と同じです。

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とは?から始めてください。
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のボーレートの値は関係なく、同じレーダー画面がそのまま動きます。
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 busy | Arduino 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 denied | Linuxで、自分のユーザーにシリアルポートへのアクセスが許可されていない。 | 自分のユーザーを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]);と書くか、ホーンを外して向きを変えて付け直す。 |
レーダーのプロジェクトをさらに発展させるアイデア
- 警報付きの防犯レーダー:物が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で距離を測定(シリアルモニタ表示)
HC-SR04センサーは、コウモリが暗闇で道を見つけるのと同じ仕組みで動作します。人には聞こえない音を出し、その反響を捉えて前方の物体までの距離を測ります。Arduinoは0.5秒ごとに距離をセンチメートル単位でコンピューターの画面に表示し、3〜100センチメートルの測定値を示します。駐車センサーや障害物を避けるロボットなどのプロジェクトの基礎になります。

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

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

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

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が破損します。

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

LCDディスプレイを使った距離測定
定規を使わずに距離を測る装置です。センサーの前で手を近づけたり遠ざけたりすると、数値が変わります。HC-SR04が音波を出して反響を捉え、Arduinoが距離をセンチメートル単位で計算し、2行のLCDディスプレイに1秒間に数回表示します。車の駐車センサーの仕組みを、机の上で確かめられます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

