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?

Koushiroによる WRO / EV3rt / SPIKE-RT などなどのAdvent Calendar 2023

Day 15

#7 カラーセンサを使おう【もっと!後輩たちのためのEV3rt講座】

Last updated at Posted at 2023-12-14

目次

タイトル 内容
1 はじめに 今回やることの説明
2 センサの設定 センサの設定方法について説明
3 EV3カラーセンサで値を測る EV3カラーセンサの基本的な測定方法
4 EV3カラーセンサでRGB値を測る EV3カラーセンサでのRGB値の測定方法
5 HiTechnicカラーセンサの使い方 HiTechnincカラーセンサでの色判定
6 まとめ 今回のまとめ

1. はじめに

前回は、モータを動かすAPIを紹介しました。

今回から2回に渡り、センサを使うためのAPIを紹介していきます。
特に今回は、カラーセンサで色や反射光などを測るAPIを紹介します。
センサ系のAPIの説明は以下のページに書かれています。
前回同様、本記事はそれをかみ砕いて、EV3ソフトでの例も交えながら紹介していきます。

EV3RT C API Reference / 各種センサ

2. センサの設定

EV3のセンサポートについて

EV3インテリジェントブロックのセンサ用のポートは、1~4までの4つがあります。

IMG_7149.jpg

EV3rtには、これら4つのポートを指定するための定数が定義されています。

typedef enum {
    EV3_PORT_1 = 0,
    EV3_PORT_2 = 1,
    EV3_PORT_3 = 2,
    EV3_PORT_4 = 3,
    TNUM_SENSOR_PORT = 4,
} sensor_port_t;
ポート 定数
1 EV3_PORT_1 0
2 EV3_PORT_2 1
3 EV3_PORT_3 2
4 EV3_PORT_4 3

モータ用のポートと同様に、定数は「列挙型」というデータ型で定義されています。
実際に使うときは、ポート1を使用したければEV3_PORT_1と書きます。

2種類あるカラーセンサ

EV3に使えるカラーセンサは、実は2種類あります。

一つは、EV3のキットに内蔵の「EV3カラーセンサ」。
もう一つは、EV3より一世代前のロボット「NXT」用に用意された、HiTechnic社製の「HiTechnic カラーセンサ」です。

color_type.png

色々と異なる点があるのですが大きな違いとして、カラーモードでの色の割り当てが上記の画像のように全然違います。
EV3カラーセンサでは0~7の間、HiTechnincカラーセンサでは0~17の間の整数値で、色を表現します。
又、センサの測り方も異なるため、EV3カラーセンサの方が周りの光の環境に影響されにくい、HiTechnincカラーセンサの方が遠くの物体を測れるという特徴もあります。

残念ながらHiTechnicカラーセンサは既に入手が困難なため、大半の方はEV3カラーセンサを使用することになるかと思いますが、本記事ではどちらのセンサの使い方も紹介していきます。

さて、センサの種類を表す定数も以下のように「列挙型」により定義されています。

typedef enum {
    NONE_SENSOR = 0,
    ULTRASONIC_SENSOR,
    GYRO_SENSOR,
    TOUCH_SENSOR,
    COLOR_SENSOR,
    INFRARED_SENSOR,
    HT_NXT_ACCEL_SENSOR,
    HT_NXT_COLOR_SENSOR,
    NXT_TEMP_SENSOR,
    TNUM_SENSOR_TYPE
} sensor_type_t;

このうち、今回使用するのは以下の二つです。

センサタイプ 定数
EV3カラーセンサ COLOR_SENSOR 4
HiTechnicカラーセンサ HT_NXT_COLOR_SENSOR 7

実際に使用する際は、EV3カラーセンサを使用するときはCOLOR_SENSOR、HiTechnicカラーセンサを使用するときはHT_NXT_COLOR_SENSORと書けばOKです。

センサの接続設定関数

モータの時と同様、main_taskが始まったらセンサの設定を行う必要があります。
センサ設定関数は以下のように定義されています。

ER ev3_sensor_config(sensor_port_t port, sensor_type_t type);

関数の名前はev3_sensor_configで、引数として、 「ポート」「センサタイプ」 の2つを指定します。

実際に使用するときは、以下のように記述します。

Ex 1. EV3カラーセンサをポート1に接続するとき

void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_1, COLOR_SENSOR);
}

Ex 2. HiTechnicカラーセンサをポート2に接続するとき

void main_task(intptr_t unused)
{
  ev3_sensor_config(EV3_PORT_2, HT_NXT_COLOR_SENSOR);
}

この設定を忘れないようにしましょう。

3. EV3カラーセンサで値を測る

では実際に、値を測る方法を説明していきます。
まずは、EV3カラーセンサの方の紹介からです。

反射光を測る

まずは、反射光を測る方法です。
下の画像のように、EV3カラーセンサからの光だけが発光され、対象物から反射された光を読み取る方式です。

IMG_7152.jpg

EV3ソフトではこの👇ブロックにあたります。

ev3_ref.png

uint8_t ev3_color_sensor_get_reflect(sensor_port_t port);

関数名はev3_color_sensor_get_reflectで、EV3カラーセンサで反射光を得る関数です。
引数として 「ポート」 を指定すると、戻り値として 「反射光の値」0 ~ 100までの間の整数値で返ってきます。

例として、int型変数に値を代入する方法を示します。

Ex 3. EV3カラーセンサで測った反射光の値を変数に格納する

void main_task(intptr_t unused)
{
  int ref;

  ev3_sensor_config(EV3_PORT_3, COLOR_SENSOR);

  ref = ev3_color_sensor_get_reflect(EV3_PORT_3);
}

これにより、変数refの中に反射光の値が格納されます。

周辺光を測る

次に、周辺光を測る方法です。
下の画像のように、EV3カラーセンサからの光だけが発光され、対象物の周辺光を読み取る方式です。

IMG_7153.jpg

EV3ソフトではこの👇ブロックにあたります。

ev3_amb.png

uint8_t ev3_color_sensor_get_ambient(sensor_port_t port);

関数名はev3_color_sensor_get_ambientで、EV3カラーセンサで周辺光を得る関数です。
引数として 「ポート」 を指定すると、戻り値として 「周辺光の値」0 ~ 100までの間の整数値で返ってきます。

例として、int型変数に値を代入する方法を示します。

Ex 4. EV3カラーセンサで測った周辺光の値を変数に格納する

void main_task(intptr_t unused)
{
  int amb;

  ev3_sensor_config(EV3_PORT_3, COLOR_SENSOR);

  amb = ev3_color_sensor_get_ambient(EV3_PORT_3);
}

これにより、変数ambの中に周辺光の値が格納されます。

色を測る

最後に、色を測る方法です。
下の画像のように、EV3カラーセンサからの光が順番に発光され(肉眼では一緒に光っているように見えます)、対象物の色を判定する方式です。

IMG_7151.jpg

EV3ソフトではこの👇ブロックにあたります。

ev3_color.png

ここで、EV3ソフトでは「色」が「数字」と結びついていました。
記事の冒頭で示した画像をもう一度示します。

ev3_color_map.png

上記の結びつきはEV3rtでも同じで、またしても「列挙型」により「色」と「値」が定義されています。

typedef enum {
    COLOR_NONE   = 0,
    COLOR_BLACK  = 1,
    COLOR_BLUE   = 2,
    COLOR_GREEN  = 3,
    COLOR_YELLOW = 4,
    COLOR_RED    = 5,
    COLOR_WHITE  = 6,
    COLOR_BROWN  = 7,
    TNUM_COLOR
} colorid_t;
定数
物体無し COLOR_NONE 0
COLOR_BLACK 1
COLOR_BLUE 2
COLOR_GREEN 3
COLOR_YELLOW 4
COLOR_RED 5
COLOR_WHITE 6
COLOR_BROWN 7

色を判定するときは、赤はCOLOR_REDなどの定数を使用することができます。

では、色を判定する関数です。

colorid_t ev3_color_sensor_get_color(sensor_port_t port);

関数名はev3_color_sensor_get_colorで、EV3カラーセンサで光を判定する関数です。
引数として 「ポート」 を指定すると、戻り値として 「色の値」0 ~ 7までの間の整数値で返ってきます。

次に例として、「カラーセンサで赤を読み取るまで直進する」プログラムを示します。

Ex 5. EV3カラーセンサで赤色を検知するまで直進する

void main_task(intptr_t unused)
{
    //プログラムをここから書く
    ev3_motor_config(EV3_PORT_A, LARGE_MOTOR);
    ev3_motor_config(EV3_PORT_D, LARGE_MOTOR);

    ev3_sensor_config(EV3_PORT_3, COLOR_SENSOR);

    while(ev3_color_sensor_get_color(EV3_PORT_3) != COLOR_RED){
        ev3_motor_set_power(EV3_PORT_A, 50);
        ev3_motor_set_power(EV3_PORT_D, 50);
    }

    ev3_motor_stop(EV3_PORT_A, true);
    ev3_motor_stop(EV3_PORT_D, true);
}

意味としては、「ポート3のカラーセンサの値が赤でない限り、直進を続ける」といった感じです。
このように、色の判定には定義された色の定数COLOR_REDなどを使うことができるので、わざわざ色番号を覚えなくとも色の判定が可能です。
(もちろん、定数を使わずに色番号(赤だと5)を使っても問題ありません。)

4. EV3カラーセンサでRGB値を測る

実は、EV3カラーセンサには隠しモードが存在します。
それは、測定値をに分解する「RGBモード」です。

カラーセンサは、対象物に照射した光の反射光を読み取っているわけですが、光には「光の三原色」があります。
「光の三原色」は赤(Red)緑(Green)青(Blue)のことであり、この3色の混ぜ具合で、どんな色でも表現することが出来ます。

このRGB値によりの値をバラバラに取得することで、色判定の精度を向上させることが可能です。

構造体について

RGB値を得る関数を紹介する前に、まだ紹介していないC言語の機能である「構造体」について説明しておきます。

「構造体」(こうぞうたい)とは、「関連するデータをひとかたまりにまとめたもの」です。

例えば、「ある人の健康診断の値を集計したい」とします。
収集するデータは「名前」「身長」「体重」としましょう。
今までの知識で考えると、以下のように独立した変数(配列)を定義することになるでしょう。

struct1.png

Ex 6. 変数で情報を整理する例

char name[128]; // 名前
int height; // 身長(整数値)
double weight; // 体重 (小数値)

name = "Taro";
height = 178;
weight = 65.3;

これでも良いのですが、もっと検査項目が増えたら?複数人のデータを収集したいとしたら?
この状態だとそれぞれが独立した変数なので、データが散らばってしまいますよね。

そこで、「構造体」の出番です。
これら3つのデータを「個人データ」というくくりでまとめてしまいます。

struct2.png

typedef struct person{
  char name[128]; // 名前
  int height; // 身長(整数値)
  double weight; // 体重 (小数値)
} personal_data;

先ほど定義した3つの変数をpersonという名前の構造体でまとめています。
又、構造体にも「型」が存在し、この場合はpersonal_dataが「型」にあたります。

中に入っている変数のことは 「メンバ」 と呼び、異なる「型」の変数(配列)を「メンバ」として加えることが可能です。
(今回の場合、char型配列、int型変数、double型変数が混在している)

このように定義した「構造体」は、以下のコードのように定義、データの読み書きが可能です。

Ex 7. 構造体で情報を整理する例

//構造体の定義
typedef struct person{
  char name[128]; // 名前
  int height; // 身長(整数値)
  double weight; // 体重 (小数値)
} personal_data;

int main(){
  personal_data Taro; // 構造体を使用した構造体変数の定義

  Taro.name = "Taro";
  Taro.height = 178;
  Taro.weight = 65.3;
}

struct3.png

まず定義ですが、普通に変数を定義するように、「構造体の型」を示した後に、その「構造体変数の名前」を書きます。

次に読み書きですが、構造体変数名.メンバ名という風に指定することで、構造体変数の中のメンバを操作することができます。
例ではTaro.nameのようにして構造体変数Taroの中のnameを指定し、"Taro"を代入しています。

ここまで、かなりざっくりとC言語の構造体の使い方について説明してきましたが、もっと詳しく知りたい方は以下のページ等をご参照ください。

RGB値を格納する構造体

先ほどの章で構造体の基本概念について説明しました。
話をRGB値の測定に戻します。

EV3rtにはAPIとして、「RGB値を格納する構造体」が既に定義されています。

typedef struct {
    uint16_t r;
    uint16_t g;
    uint16_t b;
} rgb_raw_t;

rgb_raw_t.png

この構造体の型はrgb_raw_tで、メンバとしてint型変数「r」「g」「b」が定義されています。
次に紹介するRGB値を取得する関数を使うには、この構造体を使うことが必須なので使えるようにしておきましょう。

ひとまず、以下のように構造体変数を宣言することにしましょう。

Ex 8. 構造体変数の宣言

void main_task(intptr_t unused)
{
  rgb_raw_t rgb_val;
}

rgb_val.png

RGB値を得る関数

では、EV3カラーセンサでRGB値を得る関数を紹介します。

void ev3_color_sensor_get_rgb_raw(sensor_port_t port, rgb_raw_t *val);

関数名はev3_color_sensor_get_rgb_rawです。
引数として、 「ポート」「rgb_raw_t型の構造体変数のポインタ」 を指定します。
戻り値はvoidなのでありません。

さて、ここで新たな概念 「ポインタ」 が出てきました。
ポインタの話を始めると非常に長くなってしまい、又簡単に理解できるものでもないと感じているので、本当にざっくりとだけお話します。
(ここからの説明は話を簡単にするために、一部本来のシステムとは異なるところがあります。悪しからずご了承ください。)

ポインタとアドレス

まず「ポインタ」とは、変数や配列、そして先ほど紹介した構造体変数などの「アドレス」を指し示す機能です。
変数等のデータは、コンピュータ上のRAM(Random Access Memory)に保存されますが、どの変数をRAM上のどこに置いたかを把握する必要があります。
どこに何があるか把握できないと、代入や参照が出来ません。
「アドレス」はRAM上での居場所を示すものであり、日本語では「番地」(ばんち)とも呼ばれます。

pointor_address.png

変数や配列、構造体などの「アドレス」は、変数名の前に&を付けることで生成できます。
これが「ポインタ」となるわけです。

さて、これまでの説明では関数により算出された値は「戻り値」として返ってきていました。
イメージとしては、以下のように「関数」という値計算マシーンから出てきた値がベルトコンベアに乗って流れてきて、右端に値を入れる箱、つまり格納先の変数を置いていたわけです。

func_var.gif

一方、今回の関数の場合、構造体変数の場所「アドレス」を指し示す「ポインタ」を引数として指定します。
算出された値は「戻り値」として返されるのではなく、「ポインタ」により指し示された「アドレス」の場所にある構造体変数に、関数が直接値を代入します。
分かりやすく言えば、このポインタが「荷物の送り状」のようなものになっているわけです。

func_pointor.gif

GIF内のポインタの表記が間違っておりますので訂正いたします。
誤) *rgb_val
正) &rgb_val

以上が、関数にポインタを指定することによる値代入の流れです。

関数の使い方

では、先ほど紹介したRGB値を得る関数ev3_color_sensor_get_rgb_rawの使い方を示します。
構造体変数rgb_valを定義し、ここに値を代入させます。

Ex 9. EV3カラーセンサでのRGB値の取得方法

void main_task(intptr_t unused)
{
  rgb_raw_t rgb_val;

  ev3_sensor_config(EV3_PORT_3, COLOR_SENSOR);

  ev3_color_sensor_get_rgb_raw(EV3_PORT_3, &rgb_val);

  int red = rgb_val.r;
  int green = rgb_val.g;
  int blue = rgb_val.b;
}

流れとしては、構造体変数の定義⇒センサの設定⇒ポインタを与えて計測値を得る、となります。

又、構造体変数名.メンバ名で値を取り出せるので、例えば赤成分の値を取得したい場合は、rgb_val.rとすれば値を取り出せます。

今ままでの関数の使い方と異なり、少し難しいかとは思いますが、RGB値を使えるのは大きなアドバンテージとなるので、是非使えるようになりましょう。

5. HiTechnicカラーセンサの使い方

ここからは、冒頭紹介したHiTechnicカラーセンサの使い方を説明します。

HiTechnicカラーセンサの値取得関数は、どちらもポインタを引数として渡すタイプなので、前章のEV3カラーセンサでRGB値を取得する方法も参考にしながらご覧ください。

色を測る

まずは、色を判定をする方法です。
HiTechnincカラーセンサにも、色を判定する関数があり、以下の画像のように「色」と「数値」が結びついています。

ht_color_map.png

EV3カラーセンサの時と異なり、「特定の色」と「数値」が明確に結びついているわけではなく、例えばであれば大体2~3、であれば8~9、といった具合です。
実際測ってみると、どちらかに決まるわけではなく、対象物との距離や角度など様々な要因により値がふらつきます。
場合によっては、11~16の方の値が出てしまい、正しく測定できない事もしばしばあります。

と、ネガティブな書き方となってしまいましたが、手軽に使うことは可能かと思いますので、状況を見極めて使っていきましょう。
では、その判定を行う関数についてです。

bool_t ht_nxt_color_sensor_measure_color(sensor_port_t port, uint8_t *color);

関数名はht_nxt_color_sensor_measure_colorで、HiTechnincカラーセンサで色判定を行う関数です。
引数として 「ポート」「判定値を格納する変数のポインタ」 を指定します。
戻り値として、値が取得できた場合はTrue、エラーが発生した場合はFalseが返されます。

では、簡単な関数の使い方です。

Ex 10. HiTechnicカラーセンサでの色判定値の取得方法

void main_task(intptr_t unused)
{
  int value;

  ev3_sensor_config(EV3_PORT_4, HT_NXT_COLOR_SENSOR);

  ht_nxt_color_sensor_measure_color(EV3_PORT_4, &value);
}

int型変数valueを宣言し、ここに値を入れることとします。
関数ht_nxt_color_sensor_measure_colorの第2引数にvalueのポインタとして、&valueを指定します。
すると、valueの中に色の判定値が格納されます。

ちなみにEV3ソフトに逆翻訳すればこんな👇感じ。
(HiTechnincカラーセンサの拡張ブロックを使用しています。)

ht_color_to_value.png

尚、この関数はエラーの判定が可能なので、エラーが出た場合は値を取り直すような処理を行うとより安全ですね。
例えばこんな👇感じ。

while(!ht_nxt_color_sensor_measure_color(EV3_PORT_4, &value)){
  tslp_tsk(1);
}

RGB値を得る

次に、RGB値を得る方法です。
こちらは、EV3カラーセンサでRGB値を得る方法と全く同じです。

bool_t ht_nxt_color_sensor_measure_rgb(sensor_port_t port, rgb_raw_t *val);

関数名はht_nxt_color_sensor_measure_rgbで、HiTechnincカラーセンサでRGB値を得る関数です。
引数として、 「ポート」「rgb_raw_t型の構造体変数のポインタ」 を指定します。
先ほど同様、戻り値として、値が取得できた場合はTrue、エラーが発生した場合はFalseが返されます。

では、使用方法です。

Ex 11. HiTechnincカラーセンサでのRGB値の取得方法

void main_task(intptr_t unused)
{
  rgb_raw_t rgb_val;

  ev3_sensor_config(EV3_PORT_4, HT_NXT_COLOR_SENSOR);

  ht_nxt_color_sensor_measure_rgb(EV3_PORT_4, &rgb_val);

  int red = rgb_val.r;
  int green = rgb_val.g;
  int blue = rgb_val.b;
}

この通り、EV3カラーセンサの時と全く同じように使うことができます。

尚、こちらに関してもエラーの判定が可能なので、以下のようにエラー判定処理を行うのも良いでしょう。

while(!ht_nxt_color_sensor_measure_rgb(EV3_PORT_4, &rgb_val)){
  tslp_tsk(1);
}

以上がHiTechnincカラーセンサの使い方です。

6. まとめ

今回はカラーセンサの使い方を解説していきました。
WROをはじめとしたロボットコンテストでは必須のセンサだと思いますので、しっかり使えるようにしましょう。
又、色判定時はRGB値も使いこなせると、より確実性のある判定が出来ると思います。
ぜひ試してみてください。

参考リンク

EV3カラーセンサでの色判定については、様々な方が調査・研究を行われています。
私の記事も含め、いくつかをご紹介しておきますので、参考にしてみてください。

次回はセンサの使い方第2回として、超音波センサ、ジャイロセンサ、タッチセンサの使い方を解説します。

前回: #6 モータを動かそう
次回: #8 超音波・ジャイロ・タッチセンサを使おう

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?