センサーの値を眺めていても、何が起きているかはなかなかつかめません。Processing を使うと、マイコンから送られてくる値を、そのまま絵にできます。
Processing とは ― Arduino IDE の絵を描く版
Processing は、絵を描くためのプログラミング環境です。画面は Arduino IDE によく似ています。
似ているのは偶然ではありません。Arduino IDE は Processing から生まれました。だから書き方もほとんど同じで、違うのは「どこで動くか」です。
| Arduino IDE | Processing | |
|---|---|---|
| 動く場所 | マイコンの上 | パソコンの上 |
| 得意なこと | センサーを読む、モーターを回す | 絵を描く、音を出す |
| 言語 | C++ | Java(書き方はほぼ同じ) |
ダウンロードして入れる
無料です。processing.org から、自分のパソコンに合うものを選びます。
インストーラーはありません。ダウンロードしたものを開いて、中の Processing を好きな場所へ置くだけです。Mac なら「アプリケーション」フォルダへ、Windows なら解凍してできたフォルダを、そのまま置きます。
はじめて起動すると、書いたプログラムを置く場所(書類の中の Processing フォルダ)が自動で作られます。
最初の1枚 ― 数行で円を描く
新しいスケッチに、これを貼ります。
void setup() {
size(640, 480); // 窓の大きさ
}
void draw() {
background(250); // ぬりつぶす
ellipse(320, 240, 100, 100); // 円
}
▶ を押すと別の窓が開き、まん中に円が出ます。止めるときは ■ です。
setup と draw ― Arduino の setup と loop
Arduino を書いたことがあれば、覚えることは1つだけです。
| Arduino | Processing | いつ動くか |
|---|---|---|
setup() | setup() | 最初に1回だけ |
loop() | draw() | 繰り返す(1秒に60回) |
loop() が draw() になっただけと思えば、だいたい合っています。
background() は、窓を一色でぬりつぶす命令です。draw() の先頭に置くことで、前の絵が消えます。置かないと、描いたものが残り続けます。消さないでおくと通った跡が線になって残るので、わざと置かないこともあります。
画面の座標 ― 下に行くほど y が大きい
ここが、はじめに引っかかるところです。
数学のグラフは上がプラスですが、画面は下がプラスです。だから「真上に140の線を引く」は、マイナスで書きます。
line(0, 0, 0, -140); // 真上に140伸ばす
覚える命令は、これだけで足ります
| 描く | |
|---|---|
size(640, 480) | 窓の大きさ。setup() の一番最初に書く |
background(250) | ぬりつぶす(=消す) |
ellipse(x, y, 幅, 高さ) | 円 |
rect(x, y, 幅, 高さ) | 四角 |
line(x1, y1, x2, y2) | 線 |
text("あ", x, y) | 文字 |
| 色 | |
|---|---|
fill(255, 0, 0) | 中の色(赤・緑・青を 0〜255 で) |
stroke(0) | 線の色 |
strokeWeight(4) | 線の太さ |
noFill() / noStroke() | 中を塗らない/線を描かない |
数字を1つだけ書くと灰色の濃さになります。fill(0) が黒、fill(255) が白です。
絵を回す ― 紙のほうを回す
針やメーターを描くときに使います。考え方はひとつだけです。ペンを斜めに動かすのではなく、紙を回してからまっすぐ引きます。
pushMatrix(); // ① しおりを挟む
translate(320, 200); // ② 原点をここへ動かす
rotate(radians(30)); // ③ 紙を30度まわす
line(0, 0, 0, -140); // ④ まっすぐ線を引くだけ
popMatrix(); // ⑤ しおりの場所へ戻す
④の線はいつも「まっすぐ上」でよく、傾きは③が作ります。
⑤を忘れると、次に描くものまで全部傾きます。pushMatrix() と popMatrix() は、かならず対で書きます。
rotate() は度ではなくラジアンで受け取るので、radians() で変換します。
マイコンとつなぐ ― シリアル
ここからが本題です。マイコン側は、1行に1つ、値を送るだけにしておきます。
void loop() {
Serial.println(値); // 1行に1つ
delay(20);
}
Processing 側は、こう受け取ります。
import processing.serial.*; // 最初に1行
Serial port;
float value = 0;
// マイコンのポートを「名前」で探す
String findBoard() {
String[] list = Serial.list();
printArray(list); // 一覧を下の欄に出す
for (int i = 0; i < list.length; i++) {
if (list[i].contains("usbmodem")) return list[i];
}
return null;
}
void setup() {
size(640, 480);
String name = findBoard();
if (name == null) { println("見つかりません"); return; }
port = new Serial(this, name, 115200);
port.bufferUntil('\n'); // 改行まで待ってから知らせて、と頼む
}
void serialEvent(Serial p) { // 1行届くたびに呼ばれる
String raw = p.readStringUntil('\n');
if (raw == null) return;
float v = float(trim(raw));
if (Float.isNaN(v)) return; // 数字でない行は捨てる
value = v;
}
作例では Serial.list()[0]、つまり「ポートの1番目」と書かれていることが多くあります。ところが1番目がマイコンとはかぎりません。Bluetooth の機器や別のマイコンが先に並ぶことがあります。そこで名前に usbmodem を含むものを探しています。
serialEvent() は、1行届くたびに自動で呼ばれる場所です。draw() とは別に動くので、ここでは値を受け取るだけにして、絵は draw() で描きます。
数字でない行は捨てています。マイコンが起動時にメッセージを出しても、これで無視できます。
作ってみる ― ジャイロの向きを針で表す
ここまでの道具だけで、実際に使えるものが作れます。ジャイロセンサーの向きを、時計の針のように表すものです。数字を眺めていても、ずれていく様子はなかなかつかめません。
マイコン側は、ジャイロセンサーの記事のプログラムのままです。1行に1つ、角度を送ります。
Processing 側は、受け取った角度で針を回し、同時に記録を線で残します。
import processing.serial.*;
Serial port;
boolean connected = false; // つながったか
int rx = 0; // 受け取った行数
float heading = 0; // 届いた角度
float zero = 0; // 0 とみなす向き
float[] trail = new float[600]; // ずれの記録
int head = 0;
// マイコンのポートを「名前」で探す。番号で決め打ちしない
String findBoard() {
String[] list = Serial.list();
printArray(list); // 一覧を下の欄に出す
for (int i = 0; i < list.length; i++) {
if (list[i].contains("cu.usbmodem")) return list[i];
}
for (int i = 0; i < list.length; i++) {
if (list[i].contains("usbmodem")) return list[i];
}
return null;
}
void setup() {
size(680, 520);
String name = findBoard();
if (name == null) return; // 見つからない。draw() が知らせる
println("使うポート: " + name);
port = new Serial(this, name, 115200);
port.bufferUntil('\n');
connected = true;
}
void draw() {
background(250);
if (!connected) {
fill(200, 60, 40);
textSize(17);
text("マイコンが見つかりません。", 24, 60);
fill(90);
textSize(14);
text("・USB を挿す\n・Arduino IDE のシリアルモニタを閉じる\n・▶ を押し直す", 24, 95);
return;
}
float h = wrap(heading - zero);
// ---- 方位の針 ----
pushMatrix();
translate(width/2, 200);
noFill();
stroke(215);
strokeWeight(1);
ellipse(0, 0, 300, 300);
line(0, -165, 0, -140); // 正面の目印
rotate(radians(h));
stroke(30);
strokeWeight(4);
line(0, 0, 0, -140);
popMatrix();
// ---- 数字 ----
fill(30);
noStroke();
textSize(30);
text(nf(h, 0, 1) + "°", 24, 46);
fill(130);
textSize(13);
text("c キー … いまの向きを 0 にする", 24, 72);
text("受信 " + rx + " 行", 24, 92);
// ---- ずれの記録 ----
stroke(225);
line(24, 420, width - 24, 420);
noFill();
stroke(215, 95, 35);
strokeWeight(2);
beginShape();
for (int i = 0; i < trail.length; i++) {
int k = (head + i) % trail.length;
vertex(map(i, 0, trail.length, 24, width - 24), 420 - trail[k] * 1.5);
}
endShape();
fill(130);
noStroke();
textSize(12);
text("静止させたまま、この線が傾いていけばドリフト", 24, 462);
}
float wrap(float d) {
while (d > 180) d -= 360;
while (d < -180) d += 360;
return d;
}
void serialEvent(Serial p) {
String raw = p.readStringUntil('\n');
if (raw == null) return;
String s = trim(raw);
if (s.length() == 0) return;
float v = float(s);
if (Float.isNaN(v)) return; // 数字でない行は捨てる
heading = v;
rx++;
trail[head] = wrap(heading - zero);
head = (head + 1) % trail.length;
}
void keyPressed() {
if (key == 'c') {
zero = heading;
for (int i = 0; i < trail.length; i++) trail[i] = 0;
}
}
動かすと、こうなります。
ポートを番号ではなく名前で探しているのと、受信した行数を画面に出しているのには理由があります。前者は前の項のとおりで、後者は切り分けのためです。針が動かないとき、原因が「マイコンが送っていない」のか「受け取り方が違う」のかを、ここで切り分けられます。0 行のままならマイコン側、数字が増えるのに針が止まっていれば Processing 側です。
センサーを机に置いて、動かさずにおいてみます。BNO055 なら、針も下の線もほとんど動きません。止まっているあいだに、センサーが自分でバイアスを引いているからです。
ずれを見たいときは、止めずに回し続けてから、はじめの向きに戻します。針が 0 に帰ってこなければ、そのぶんがずれです。
ロボサイエンスでは、こうして部品の素の性質を測ってから、プログラムの設計を決めます。どのくらいずれるのかを知らないと、ハーフの10分を持たせられるのかどうかも決められません。
センサーの側の話は ジャイロセンサー BNO055 と BNO055 のキャリブレーション にあります。
動かないときに見るところ
| 症状 | 見るところ |
|---|---|
| ポートが開けない | Arduino IDE のシリアルモニタを閉じる。シリアルポートは1つのプログラムしか開けません |
| 絵が線だらけになる | background() を draw() の先頭に書いているか |
| 何も届かない | 先に Arduino IDE のシリアルモニタで、値が流れているか確かめる |
| 上下が逆に動く | y は下がプラス |
size() でエラー | setup() の一番最初に書く |
いちばん多いのは1つめです。Arduino IDE でマイコンの様子を見たあと、閉じずに Processing を動かすと、ここで止まります。
「何も届かない」ときは、Processing を疑う前にマイコンを確かめます。Arduino IDE のシリアルモニタに値が流れていなければ、原因はマイコン側です。
ロボカップジュニア2027 に向けて、レスキュー(災害救助)とサッカーの2種目に取り組みます。プログラミングと電子工作の経験は問いません。