1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

#6 フォースセンサと超音波センサの使い方【SPIKE-RTでロボコンに出よう!!】

Posted at

目次

タイトル 内容
1 はじめに 今回やることの説明
2 フォースセンサの使い方 フォースセンサの使用方法について解説
3 超音波センサの使い方 超音波センサの使用方法について解説
4 まとめ 今回のまとめ

1. はじめに

前回はカラーセンサの紹介を行いましたが、今回は「フォースセンサ」と「超音波センサ」の解説を行っていきます。

フォースセンサ、超音波センサのAPIの説明は以下のページに書かれています。本記事はそれをかみ砕いて、LEGO EducationのSPIKEソフトでの例も交えながら紹介していきます。

SPIKE-RT C API Reference [Japanese] / フォースセンサ

SPIKE-RT C API Reference [Japanese] / 超音波センサ

フォースセンサとは

以下に示すのが、「SPIKEプライム フォースセンサー」 です。

05_01-300x200.jpg

「フォースセンサ」はSPIKEプライムで新たに登場したセンサで、感覚的には「無段階のタッチセンサ」といった感じです。
そのイメージ通り、モノが触れているかどうかを確かめるタッチセンサとして扱うことが出来、EV3やNXTなどのタッチセンサとは異なりその「押され具合」まで感知することが出来るのです。

詳しい仕様はこちら👇から

超音波センサ

続いて以下に示すのが、「SPIKEプライム 距離センサー」 です。

03_01-300x200.jpg

SPIKE-RTの表記に合わせ、本記事では「超音波センサ」という名前で扱います。

目のようになっているところが超音波の送信部と受信部になっており、対象物に照射した超音波がどれだけの時間で跳ね返ってくるかを計測することで、距離を算出することが出来るセンサです。
目の部分の周りにはLEDが搭載されており、カラーセンサの時と同様にプログラムにより光り方を変えることが出来ます。

詳しい仕様はこちら👇から

これら二つのセンサの使い方を解説していきます。

2. フォースセンサの使い方

それではまず、フォースセンサの方から使い方を解説していきます。

フォースセンサの設定

前回のカラーセンサと同様に、センサを使う前には「ポート」と「フォースセンサ」の結びつけが必要です。

pup_device_t *pup_force_sensor_get_device(pbio_port_id_t port);

関数名はpup_force_sensor_get_deviceで、「ポート」の情報により、「フォースセンサ」と「PUPデバイスポインタ」を結びつけます。
引数として、「ポート」を指定すると、戻り値として「PUPデバイスポインタ」が返ってきます。

使い方の例も示しておきます。

Ex2-1. フォースセンサのセットアップ

spikert_6.c
#include <stdlib.h>
#include <stdio.h>
#include <kernel.h>

#include <spike/hub/system.h>

#include <spikert_6.h>

#include "spike/pup/motor.h"
#include "spike/pup/colorsensor.h"
#include "spike/pup/forcesensor.h"
#include "spike/pup/ultrasonicsensor.h"

#include "spike/hub/battery.h"
#include "spike/hub/button.h"
#include "spike/hub/display.h"
#include "spike/hub/imu.h"
#include "spike/hub/light.h"
#include "spike/hub/speaker.h"

#include <pbio/color.h>

#include "kernel_cfg.h"
#include "syssvc/serial.h"

pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
}

はじめにforceDという名前で「PUPデバイスポインタ」を宣言しています。
Main関数に入ったら、pup_force_sensor_get_device関数により「ポートD」が「フォースセンサ」であると結び付けて、forceDに格納しています。

こちらも#4・#5とお伝えしましたが、ポートの定数PBIO_PORT_ID_Dなどを使うためには、内部のファイルを編集する必要があります。
編集方法については#4の2章をご覧ください。

ファイルの編集を避ける場合は、以下のようにポートを指定することも出来ます。

Ex2-2. カラーセンサのセットアップ (ポート指定方法)

spikert_6.c
pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device('D');
}

力の測定

まずは、フォースセンサを使い、ボタンにどれくらいの力が加わっているかを測定してみましょう。

float pup_force_sensor_force(pup_device_t *pdev);

関数名はpup_force_sensor_forceで、ボタンが押された力の大きさを取得します。
引数に「PUPデバイスポインタ」を入力すると、戻り値として「押された力の大きさ」がfloat型(小数値)で返ってきます。
尚、力の単位はN(ニュートン)となります。

では、簡単な使い方の例を示しておきます。

Ex2-3. 押された力の大きさを測定する

spikert_6.c
pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    float force =  pup_force_sensor_force(forceD);

    char str[32];
    sprintf(str, "%lf", force);
    hub_display_text_scroll(str, 100);
  }
}

フォースセンサとポートの結びつけを行った後、無限ループに入り、ボタンが押された力の大きさを測定します。
測定値は小数値で取得されるので、sprintf関数を使用してchar型配列に格納し、ハブディスプレイに表示しています。

ここで実際に試していただくとわかりますが、データ型はfloatで小数値であるものの、実際に返される値は0~10の整数値のようです。
よって、以下のようなプログラムでも十分使えるかと思います。

Ex2-4. 押し込まれた長さを測定する(整数値)

spikert_6.c
pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    int force = (int)pup_force_sensor_force(forceD);
    hub_display_number(force);
  }
}

ここで、pup_force_sensor_force関数の前に(int)がついていますが、これは 「キャスト演算子」 と呼ばれるもので、データ型の変換を行います。
この場合、float型で得られる「力の大きさ」の値を、int型に変換して変数forceに代入しています。

ボタンの移動距離の取得

先ほどはボタンの押され具合を「力」で取得しましたが、次は「押し込まれた長さ」で取得したいと思います。

float pup_force_sensor_distance(pup_device_t *pdev);

関数名はpup_force_sensor_distanceで、ボタンが押された時の「押し込まれた長さ」を取得します。
引数に「PUPデバイスポインタ」を入力すると、戻り値として「押し込まれた長さ」がfloat型(小数値)で返ってきます。
長さの単位はmm(ミリメートル)となります。

ではこちらも、簡単な使い方の例を示しておきます。

Ex2-5. 押し込まれた長さを測定する

spikert_6.c
pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    float distance =  pup_force_sensor_distance(forceD);

    char str[32];
    sprintf(str, "%lf", distance);
    hub_display_text_scroll(str, 100);
  }
}

フォースセンサとポートの結びつけを行った後、無限ループに入り、ボタンが押し込まれた長さを測定します。
こちらも測定値は小数値で取得されるので、sprintf関数を使用してchar型配列に格納し、ハブディスプレイに表示しています。

ここで「力の測定」の時と同様に、データ型はfloatで小数値であるものの、実際にこの関数から返される値は0~7の整数値のようです。
よって、以下のようなプログラムでも十分使えるかと思います。

2-6. 押し込まれた長さを測定する(整数値)

spikert_6.c
pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    int distance = (int)pup_force_sensor_distance(forceD);
    hub_display_number(distance);
  }
}

ここでも「キャスト演算子」を利用しており、この場合、float型で得られる「押し込まれた長さ」の値を、int型に変換して、変数forceに代入しています。

ボタンが押されているかの取得 (閾値指定版)

ここまで「押された力の大きさ」と「押し込まれた長さ」を具体的な値で取得する方法を紹介してきましたが、ここからはEV3の「タッチセンサ」的な使い方、つまり 「押されている」「押されていない」 かを取得する方方法を説明していきたいと思います。

bool pup_force_sensor_pressed(pup_device_t *pdev, float force);

関数名はpup_force_sensor_pressedで、ボタンが「押されている」か「押されていない」かを取得します。
第一引数に「PUPデバイスポインタ」、第二引数に「押されたと判断する閾値」を指定します。
閾値は「押された力の大きさ」で指定し、指定した値以上となった時に、ボタンが「押された」としてtrueを返します。

では、簡単な例を示します。

Ex2-7. ボタンが押し込まれているか取得

spikert_6.c

pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    int force = (int)pup_force_sensor_force(forceD);
    hub_display_number(force);

    int pressed = pup_force_sensor_pressed(forceD, 5);
    if(pressed)
      hub_light_on_color(PBIO_COLOR_GREEN);
    else
      hub_light_on_color(PBIO_COLOR_RED);
  }
}

先ほどの Ex2-4. に、今回の関数を付け足しました。
pup_force_sensor_pressed関数の閾値として5を指定しており、「押された力の大きさ」が5以上になった時に、変数pressedtrueになります。
その後、pressedtrueの時にはハブのボタンのLEDをに、falseの時はにして、ボタンが「押されているか」を示します。

この関数は自分で閾値を設定することから、使う状況に応じて感度を調整出来るのが特徴です。

ボタンが触れているかの取得

先ほどはボタンが「押されているか」の閾値を自分で設定するタイプの関数を解説しましたが、同じような関数をもう一つ紹介します。

bool pup_force_sensor_touched(pup_device_t *pdev);

関数名はpup_force_sensor_touchedで、ボタンが「触れている」か「触れていない」かを取得します。
引数には「PUPデバイスポインタ」を指定します。

先ほどのpup_force_sensor_pressed関数との違いは、引数に「閾値を指定しない」点です。
この関数は閾値による判定ではなく、ほんの少しでもボタンが押されたらtrueを返します。

先ほどのEx2-7をベースに、この関数を使ってみたいと思います。

Ex2-8. ボタンが触れているか取得

spikert_6.c

pup_device_t *forceD;

void Main(intptr_t exinf)
{
  forceD = pup_force_sensor_get_device(PBIO_PORT_ID_D);
  
  while(1){
    int force = (int)pup_force_sensor_force(forceD);
    hub_display_number(force);

    int touched = pup_force_sensor_touched(forceD);
    if(touched)
      hub_light_on_color(PBIO_COLOR_GREEN);
    else
      hub_light_on_color(PBIO_COLOR_RED);
  }
}

動きは先ほどのEx2-7と全く同じで、違う点はpup_force_sensor_pressed関数をpup_force_sensor_touched関数に差し替え、変数名をtouchedに変更したところです。
実際に動かしてみると、trueとなる条件の違いが良くわかるかと思います。

以上で、フォースセンサの使い方は全て紹介いたしました。

3. 超音波センサの使い方

続いて、超音波センサの方から使い方を解説していきます。

超音波センサの設定

例のように、センサを使う前には「ポート」と「超音波センサ」の結びつけが必要です。

pup_device_t *pup_ultrasonic_sensor_get_device	(pbio_port_id_t port);

関数名はpup_ultrasonic_sensor_get_device で、「ポート」の情報により、「超音波センサ」と「PUPデバイスポインタ」を結びつけます。
引数として、「ポート」を指定すると、戻り値として「PUPデバイスポインタ」が返ってきます。

使い方の例も示しておきます。

Ex3-1. 超音波センサのセットアップ

spikert_6.c
#include <stdlib.h>
#include <stdio.h>
#include <kernel.h>

#include <spike/hub/system.h>

#include <spikert_6.h>

#include "spike/pup/motor.h"
#include "spike/pup/colorsensor.h"
#include "spike/pup/forcesensor.h"
#include "spike/pup/ultrasonicsensor.h"

#include "spike/hub/battery.h"
#include "spike/hub/button.h"
#include "spike/hub/display.h"
#include "spike/hub/imu.h"
#include "spike/hub/light.h"
#include "spike/hub/speaker.h"

#include <pbio/color.h>

#include "kernel_cfg.h"
#include "syssvc/serial.h"

pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  ultrasonicE = pup_ultrasonic_sensor_get_device(PBIO_PORT_ID_E);
}

はじめにultrasonicEという名前で「PUPデバイスポインタ」を宣言しています。
Main関数に入ったら、pup_ultrasonic_sensor_get_device関数により「ポートE」が「超音波センサ」であると結び付けて、ultrasonicEに格納しています。

定数PBIO_PORT_ID_Eなどを使わない場合のポートの指定方法も示しておきます。

Ex3-2. 超音波センサのセットアップ (ポート指定方法)

spikert_6.c
pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  ultrasonicE = pup_ultrasonic_sensor_get_device('E');
}

距離を測定する

まずは、超音波の基本的な使い方である「対象物との距離の測定」を行う関数を紹介します。

int32_t pup_ultrasonic_sensor_distance(pup_device_t *pdev);

関数名はpup_ultrasonic_sensor_distance で、超音波センサで対象物との距離を測定します。
引数として、「PUPデバイスポインタ」を指定すると、戻り値としてint型(整数値)で値を返します。単位はmm(ミリメートル)です。尚、対象物が存在しない場合は-1と返されます。

使い方の例も示しておきます。

Ex3-3. 超音波センサで対象物との距離を測定する

spikert_6.c
pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  ultrasonicE = pup_ultrasonic_sensor_get_device(PBIO_PORT_ID_E);

  while(1){
    int distance = pup_ultrasonic_sensor_distance(ultrasonicE);
    hub_display_number(distance);
  }
}

はじめにpup_ultrasonic_sensor_get_device関数により「ポートE」を超音波センサと紐づけ、その後は無限ループに入ります。
無限ループ内では、pup_ultrasonic_sensor_distance関数により対象物との距離を測定して、その値をint型変数distanceに格納しています。
格納された値はhub_display_number関数でハブディスプレイ上に表示しています。

ただし、hub_display_number関数は2桁までしか値を表示できない為、100mmを超えると>と表示され、具体的な値は分からなくなります。
その場合は、以下のようにシリアル通信を用いることで値を確認することが出来ます。
(シリアル通信の使い方はまだ解説していないので、コードの紹介までに留めておきます。)

Ex3-4. 超音波センサで対象物との距離を測定する(シリアル通信利用)

spikert_6.c
pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  char msg[32] = "Program Start\n";

  serial_opn_por(SIO_USB_PORTID); // USBシリアル通信の開通
  serial_wri_dat(SIO_USB_PORTID, msg, sizeof(msg)); // "Program Start"を送信

  ultrasonicE = pup_ultrasonic_sensor_get_device(PBIO_PORT_ID_E);

  while(1){
    int distance = pup_ultrasonic_sensor_distance(ultrasonicE);
    hub_display_number(distance);

    sprintf(msg,"distance: %d\n", distance);
    serial_wri_dat(SIO_USB_PORTID, msg, sizeof(msg));
    dly_tsk(500*1000);
  }
}

超音波センサの検証

さて、返される値についてですが、ある程度計測のテストを行いました。
環境は以下の画像に示すような、一直線上にブロックを置いた状態です。

IMG_7518.jpg

又、ブロックについては横幅が6ポッチの「大ブロック」と、横幅4ポッチの「小ブロック」の二種類を用意しました。

IMG_7522.jpg

(小ブロックはパーツが足りず、黄色いパーツが混じっていますが、測定には赤い面を向けるため問題ありません。)

結果は以下のようになりました。

実際の距離 [mm] 大ブロック [mm] 小ブロック [mm]
10 40 40
20 40 40
30 40 40
40 40 48
50 51 -1
60 59 85
70 72 82
80 88 87
90 97 96
100 107 104
110 117 114
120 125 122
130 139 131
140 148 141
150 154 151
160 158 160
170 165 169
180 176 177
190 186 186
200 196 197
250 240 246
300 288 295
350 339 346
400 388 397
450 440 450
500 489 499
550 542 549
600 587 603
650 647 655
700 690 707
750 747 761
800 796 809
850 849 883
900 908 -1
950 -1 -1
1000 -1 -1

グラフにすると以下のようになります。

超音波グラフ.png

グラフの見方として、青線が実際の距離のため、オレンジや緑の印が青線に近ければ近いほど良い、ということになります。

この結果についてですが、まず大ブロック・小ブロックとも40mm未満では測定不可能です。
ブロックの距離を変化させても、40mmと返されます。

又、40~90mmの範囲では、大ブロックはそこそこの精度が出ているものの、小ブロックはとても正確に測れているとは言えません。
従って、SPIKE Primeの超音波センサは、近距離の測定には不向きだと言えるでしょう。

一方で、100~800mmの範囲では比較的良い精度で測定が出来ています。
この範囲での実際の距離と測定された距離の差の平均は、大ブロックで3.8mm、小ブロックで-2.1mmですので、実用上も問題ない精度でしょう。

最後に900mm以上ですが、このあたりで測定が出来なくなってきます。
たまに900台や1000台の値が表示されますが、9割方は-1が返ってくる結果となりました。

これらの結果から、なんとも使いどころが難しいセンサですが、使い方を工夫すれば使えないことは無いのかな…と感じました。

超音波信号を検出する

次に紹介する方法は「超音波信号を検出する」というものです。
こちら、最初は自身が反射した超音波も含むのかなと考え、「超音波センサの測定範囲内に、対象物があるかどうか」を判定するものかと思っていましたが、それは間違いでした。

正しくは、文字通り「超音波を検出したとき」に「検出した」と返すものです。
まずは関数を紹介してしまいます。

int32_t pup_ultrasonic_sensor_distance(pup_device_t *pdev);

関数名はpup_ultrasonic_sensor_distance で、超音波センサが超音波を検出したかどうかを取得します。
引数として、「PUPデバイスポインタ」を指定すると、戻り値として「検出した」trueか「検出しなかった」falseの二通りが返されます。

前述の謎を解明するため、以下のようなコードを作成しました。

Ex3-5. 超音波センサで超音波を検出

spikert_6.c
pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  ultrasonicE = pup_ultrasonic_sensor_get_device(PBIO_PORT_ID_E);

  while(1){
    int presence = pup_ultrasonic_sensor_presence(ultrasonicE);
    if(presence)
      hub_light_on_color(PBIO_COLOR_GREEN);
    else
      hub_light_on_color(PBIO_COLOR_RED);
  }
}

この関数により、超音波を検出したらボタンのLEDをに、検出しなかった時はにして、超音波が「検出されているか」を示します。

まず、以下の図のように超音波センサの前にブロックを置いた状態でプログラムを実行しました。

IMG_7523.jpg

このとき、ボタンのLEDはのまま、つまり「検出していない」ということになります。

次に、以下の図のように超音波センサと、別の超音波センサを向かい合わせた状態でプログラムを実行しました。
(手元にSPIKE Primeの超音波センサは1台しかないため、EV3のものを使用しました。EV3の方は電源が付いているだけです。)

IMG_7524.jpg

すると、見事にボタンのLEDがに光りました。

ということで、説明の言葉通り、超音波を検出したら、「検出した」と返す関数でした。
使いどころがなかなか想像つきませんが、ひとまず紹介いたしました。

ライトの設定

カラーセンサの時にもありましたが、超音波センサにも以下の図のように4個のライトが搭載されています。

IMG_7525.jpg

これらのLEDの点灯・消灯・および点灯時の明るさ(パーセント)を個別に指定することが出来ます。

ライト全消灯

まずは、4つのLEDを全て消す関数です。

pbio_error_t pup_ultrasonic_sensor_light_off(pup_device_t *pdev);

関数名はpup_ultrasonic_sensor_light_offで、4つのLEDを全て消灯します。
引数には「PUPデバイスポインタ」を入力します。

この関数を実行することで、「PUPデバイスポインタ」により指定したポートの超音波センサのLEDを全消灯します。

ライト全点灯

続いて、4つのLEDを全て点ける関数です。

pbio_error_t pup_ultrasonic_sensor_light_on(pup_device_t *pdev);

関数名はpup_ultrasonic_sensor_light_onで、4つのLEDを全て点灯します。
引数には「PUPデバイスポインタ」を入力します。

この関数を実行することで、「PUPデバイスポインタ」により指定したポートの超音波センサのLEDを全点灯します。

ライトの明るさを個別に指定

最後に、LEDを1個ずつ個別に明るさを変更する関数です。

pbio_error_t pup_ultrasonic_sensor_light_set(pup_device_t *pdev, int32_t bv1, int32_t bv2, int32_t bv3, int32_t bv4);

関数名はpup_ultrasonic_sensor_light_setで、LEDを1個ずつ個別に明るさを変更します。
第一引数には「PUPデバイスポインタ」を入力します。
第二・三・四・五引数には、それぞれのLEDでの明るさを$0\sim100$の範囲で指定します。

それぞれの引き数と場所の対応は以下の図の通りです。

IMG_7525 _2.jpg

引き数番号 図の番号
2
3
4
5

これらの関数の使い方についても簡単に例を示しておきます。

Ex3-6. 超音波センサのライトを設定

spikert_6.c
pup_device_t *ultrasonicE;

void Main(intptr_t exinf)
{
  ultrasonicE = pup_ultrasonic_sensor_get_device(PBIO_PORT_ID_E);

  pup_ultrasonic_sensor_light_on(ultrasonicE);
  dly_tsk(3*1000*1000);
  pup_ultrasonic_sensor_light_off(ultrasonicE);
  dly_tsk(3*1000*1000);
  pup_ultrasonic_sensor_light_set(ultrasonicE,100,0,0,0);
  dly_tsk(3*1000*1000);
  pup_ultrasonic_sensor_light_set(ultrasonicE,0,100,0,0);
  dly_tsk(3*1000*1000);
  pup_ultrasonic_sensor_light_set(ultrasonicE,0,0,100,0);
  dly_tsk(3*1000*1000);
  pup_ultrasonic_sensor_light_set(ultrasonicE,0,0,0,100);
  dly_tsk(3*1000*1000);
}
全点灯
↓
全消灯
↓
各部ごとの点灯(1→2→3→4)

の順番でLEDを点灯・消灯しています。

SPIKE-RTでは、デフォルトではLEDは点灯していないので、もし必要であれば上記の関数を用いて光りを出すことが出来ます。
ただライトセンサと違い、センサとしては「光」は一切関係ないので、基本的には見た目の問題になるかと思います。

4. まとめ

今回は「フォースセンサ」と「超音波センサ」の使い方を紹介しました。
これにて、ハブブロックと接続するモータやセンサ(PUPデバイス)の使い方は全て解説が終了しました。

これである程度ロボットとしては形になってくるかと思いますが、SPIKE Primeの最大の強みである、ある機能の紹介がまだ終わっていません。
それは、「ジャイロセンサ」を内蔵した「ハブブロック」です。
次回はその「ハブブロック」の使い方について、説明していきたいと思います。
かなり機能てんこもりの「ハブブロック」ですので、頑張っていきましょう。

前回: #5 カラーセンサの使い方
次回: 執筆中

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?