マイクロビットを使ってみる 〜潜水艦ソナー

Processing

マイクロビットと「サーボモーター」をつかった作品のネタをいろいろ考えていたのですが、「潜水艦のソナー画面」や「空港の管制レーダー画面」のようなものをつくったら面白いのではないか?と思いつきました。


サーボモーターに「超音波センサ」を取り付け、サーボモーターの「角度」情報と、超音波センサから取得した「障害物までの距離」情報を組み合わせて、PC画面上に表示するものです。
サーボモーターの首振り角度を変えるだけでなく、その角度情報をPC画面での表示時にも活用することになり、サーボモーターをつかった作品として適しているのでは?と思います。

さっそくつくってみることにしました。

まずはハードウェアです。

マイクロビットの小さい端子から信号を取り出すために、今回は「KEYESTUDIOモータードライバー拡張ボード for BBC micro:bit(KS4033)」を使います。


単三電池4本用の電池ボックスを取り付けておきます。

サーボモーターは秋月電子でも販売されている「FS90」を、超音波センサは定番の「HC-SR04」を使います。
木片に超音波センサをねじ止めし、その木片の底面にサーボホーンを取り付けます。
サーボモーターの各端子は以下のようにつなぎます。

サーボモーター拡張ボード
VCC5V
GNDG
SIGP8

超音波センサの各端子は以下のようにつなぎます。

超音波センサ拡張ボード
VCC5V
GNDG
TrigP2
EchoP1

サーボモーターにサーボホーンを差し込みます。
あたま(超音波センサ側)が重くなってサーボモーターが倒れてしまうので、固定するために両面テープで電池ボックスに貼り付けておきました。

マイクロビット用プログラムはこちらです。

Aボタンを押すたびに、変数「動作中」が「真」と「偽」で切り替わります。
「動作中」が「真」のとき、サーボが指定された「角度」に動き、超音波センサが障害物との「距離」を測定します。
なお、超音波センサで距離が正常に測定できなかったとき、測定値は「0」になってしまうので、そのときは「距離」を「500」に変更しています。
サーボの「角度」と、超音波センサで測定した「距離」を、「シリアル通信」でPCに送信します。
これらの処理を50ミリ秒間隔で繰り返します。その際、角度は「0度」〜「180度」の範囲で「1度」ずつ変動します。

マイクロビットから送られてきたデータを、PC画面上で表示するために「Processing」を使います(Processingのインストール方法などは こちら)。
Processing用プログラムはこちらです。

import processing.serial.*;
Serial myPort;

float angle = 90;
float distance = 20;
boolean hasData = false;
int lastReceiveTime = 0;

final int originX = 480;
final int originY = 540;
final int radarRadius = 500;
final int distanceScale = 10;

void serialEvent(Serial myPort) {
  String myString = myPort.readStringUntil('\n');
  myString = trim(myString);
  String[] values = split(myString, ',');
  if (values.length >= 2) {
    angle = float(values[0]);
    distance = float(values[1]);
    hasData = true;
    lastReceiveTime = millis();
  }
}

void setup() {
  size(960, 540);
  smooth();
  background(0);
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[2], 115200);
  myPort.bufferUntil('\n');
}

void draw() {
  noStroke();
  fill(0, 0, 0, 5);
  rect(0, 0, 960, 540);

  stroke(0, 255, 0);
  strokeWeight(1);
  noFill();
  for(int i=0; i<=radarRadius*2; i+=radarRadius*0.4) {
    arc(originX, originY, i, i, PI, TWO_PI);
  }
  for(int i=0; i<=180; i+=30) {
    line(originX, originY, originX-radarRadius*cos(radians(i)), originY-radarRadius*sin(radians(i)));
  }

  if(millis() - lastReceiveTime > 500) {
    hasData = false;
  }

  if(hasData) {
    stroke(0, 255, 0, 10);
    strokeWeight(10);
    float x = cos(radians(angle));
    float y = sin(radians(angle));
    line(originX, originY, originX+radarRadius*x, originY-radarRadius*y);
    if(distance <= radarRadius/distanceScale) {
      noStroke();
      fill(255,0,0);
      ellipse(originX+distance*distanceScale*x, originY-distance*distanceScale*y, 10, 10);
    }
  }
}

画面の背景はこちらです。

「draw()」が実行される度に「fill(0, 0, 0, 5);」と「rect(0, 0, 960, 540);」で画面全体を半透明で塗りつぶしており、これにより画面表示内容がだんだん消えていくようにしています。そのため、「draw()」が実行されるたびにこの背景を再描画しています。

マイクロビットからデータを受信する度に、受信した「角度」情報をもとにして緑色の太いラインを描画しています。
また、受信した「距離」情報が「50センチ」以内だった時、その「角度」と「距離」の場所に赤色の丸を描画しています。

動かしたときの表示内容はこちらです。

なかなかいい感じに表示されています。

知り合いの小学生に見せたところ、かなり面白がってくれました。
「これをロボカーに取り付けて、受信したデータに応じて進行方向を制御するようにしたらもっと面白いのでは?」などと、新たなアイデアも出してくれました。
それも良さそうですね。

 

なお、私がマイクロビットの使い方を習得するのにあたっては、以下の書籍を参考にさせていただきました。


初心者向けから、比較的高度なものまで、さまざまな情報が記載されているだけでなく、子供向けの作例も多数掲載されていますので、「プログラミング教育」のための題材さがしなどにもおすすめです。