ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

ETロボコン/UMLロボコンコミュのキャリブレーションについて

  • mixiチェック
  • このエントリーをはてなブックマークに追加
Mindstormsのより高度な制御のための「キャリブレーション」について、あれこれ

コメント(17)

 こんにちは、ここにも出没、すねいるです。

 計測機の世界における一般的なキャリブレーション(校正)の説明は Wikipedia に詳しく書かれていますので、こちらを参考にしてください。

 http://ja.wikipedia.org/wiki/%E6%A0%A1%E6%AD%A3_%28%E8%A8%88%E6%B8%AC%29

 次にETロボコンにおけるキャリブレーションの説明ですが、ほとんどの出場チームは光センサーによるコースライン(黒線/灰色マーカー)認識をより正確(確実)におこなうための調整のことをキャリブレーションと呼んでいます。
 人が目で見たときは「鉛筆で描いた黒いライン」も、「マジックで描いた黒いライン」も、「インクジェットプリンタで描いた黒いライン」も、「黒いラインは黒いライン」と同じように認識できます。しかしながら、それは人の脳がラインの暗さと周囲の明るさのコントラストを認識する高度な情報処理を行っている結果に他なりません。
 Mindstormsの単純な光センサーで、「鉛筆の黒」と「マジックの黒」と「インクジェットプリンタの黒」を測定したときの値は、全て微妙に変わってきます。たとえば、参加者が事前にコピー用紙にトナー式プリンターで印刷したテストコースで、黒いライン上の測定値が 30、白いコース上の測定値 50となっていても、本番のナイロン布地に特殊な染料で黒く染めたコースで測定したときは、黒いライン上で40、白いコース上で58になっているかもしれません。もし、この参加者のプログラムが40のときに灰色のマーカーだと判定していると、本番コースでは常にマーカー上にいると誤認してしまいます。
 実際にはコースの素材や染料/顔料の違いだけではなく、(ロボット持ち運び時の)光センサーの微妙な取り付け位置(高さ)のずれ、電池の消耗、会場の照明などにより、光センサーの測定値は必ずと言っていいほど微妙にずれます。
 ということで各チームとも本番走行直前の準備として、正しくラインやマーカーを認識できるように測定を行いプログラムのパラメータを調整(補正)したり、高度なチームはプログラムが自動でパラメータを調整(書き換え)できるように工夫を凝らしています。
 これらを総称してみなさん「キャリブレーション」と呼んでいます。

 その他にもステアリング(舵)の曲がり具合や前後進の進み具合も、コースの滑り加減や電池の消耗加減によって変わってきますので、広い意味でこれらの制御パラメーターの調整もキャリブレーションと呼んで実現しているチームもあるようです。

 はぁーなるほど!!
 そうなっていたんですね。丁寧な回答ありがとうございます。
  なんだかドラゴンボールにでてくる「スカウター」みたいですね。

 一点だけ質問ですが、測定値が40,50とかいろいろありますが
 これって誰がどう決めているんですかね?

 
 40 とか 50 と言った測定値は、brickOSの場合、LIGHT関数(マクロ)や LightSensor.get メソッドを呼んだときに却ってくる値のつもりでした。
 今まさに本番のコース上を走行している瞬間に、 40 というセンサーから取得した値を、ラインと認識するのか、マーカーと認識のするか、白い余白部分と認識のするのかは、プログラムの工夫、すなわちプログラマ/設計者/モデラーの工夫であり、臨機応変に環境に応じて変更するソフトウェア上の仕掛け、というのが、キャリブレーションです。というお答えで宜しいでしょうか?
 補足ですが、直前の出走者の競技(レース)が終わってから、自分たちがスタートするまでに認められるキャリブレーションの時間に Ir Tower (赤外線タワー) を介して、RCXのプログラムを書き換えることは「認められていません」。
 もし、このキャリブレーションの時間を有効に使いたいのであれば(但し、スタート直前のキャリブレーションが必要か否かはケース・バイ・ケース)、RCXのPrgmボタンやViewボタンなどを上手に利用して、キャリブレーションの結果をロボット本体に反映させるプログラム上の工夫が必要になるのでご注意ください。

なるほど。納得です。
 また疑問に思ったらレスするのでよろしくお願いします。

 またまた質問します。
 調べてたらキャリブレーションってログ?と関係があるらしいのですが
 どのように関係してるのか教えて下さい。
 
 
 キャリブレーションの実現方法は各チーム、各様ですのでキャリブレーションとログが関係しているのか、していないのか、は個別の工夫です。
 先の私の説明では、光センサーから取得した(測定した)値を「瞬間」、「瞬間」にプログラムが判断して、コースライン(黒)やマーカー(グレー)を認識するように書いていましたが、より高度な制御を行うのであれば、センサーの値の時系列(時間軸)での変化から判断する方法もあります。時系列でセンサー値をみるときにパラメーターの調整をどうするの?という問題を解決するために、試走でMindstormsを走らせながらセンサー等の値(=ログ)をRCX上に保存しておき、走行が終わってから 赤外線通信 でパソコンに、このログを吸い出してパラメータ調整に役立てているチームもあります。
なるほど、わかりました。
それでキャリブレーションで取得したログを「保存する関数(リングバッファ)」と「保存したログを取得する関数」をC言語で作ってみたんですが、意見を頂きたいです。コンパイルエラーは出てませんが、何か足りない気がします。
環境はボーランドを使っています。宜しくお願いします。


///////////データ保存関数/////////////////////
////////////////////////////////////////////


#include <stdio.h>

#define SIZE 10 /* 待ち行列に入るデータの最大数*/
#define SUCCESS 1 //成功
//プロトタイプ宣言
long data_in(long data);//保存関数
long log_data_first();//一番古いログデータを取得する関数
long log_data_next();//前回読みだした次のログデータを取得する関数


// 待ち行列データの構造体
typedef struct {
long data_set[SIZE]; // 大きさが SIZE の配列を宣言
long data_head; // データ先頭位置を表す変数
long data_tail; // データ終端位置を表す変数
}DATA;

//typedef struct data_struct DATA;//型

DATA data_data;//変数を使うために宣言 DATAは型でdata_dataは変数
//ここに宣言する事によってグローバル変数になる

void init(void)
{
data_data.data_tail = 0;//ここで初期化 他の構造体メンバも初期化!!
data_data.data_head = 0;
data_data.data_set[SIZE] = 0;
//mainを一回抜けると…
}

int main(void)
{

init();


data_in(1);//ここを回す

return 1;
}

long data_in(long data)
{
data_data.data_set[data_data.data_tail] = data; // 終端にデータを保存する

data_data.data_tail++; // 終端位置をずらす
if(data_data.data_tail == SIZE) // 配列の終端に達したら
{
data_data.data_tail = 0; //終端を配列の先頭にする
data_data.data_head ++;//一番古いデータ位置をずらしていく
}
return SUCCESS;
}

long log_data_first(void)
{

return data_data.data_head;
}

long log_data_next(void)
{


return data_data.data_head+1;
}

 よく考え抜かれたコードだと思います。
 一点だけ突っ込むとしたら data_tail のインクリメントを繰り返したときに、data_head を超える可能性があります。(SIZEを超えてデータを突っ込めてしまう) 逆も同じで data_head のインクリメントが繰り返されたときに data_tail を超える可能性があります。このときはデータの終端を超えて読み出すことになります。
 こんなところでしょうか?あせあせ(飛び散る汗)

 あぁ〜!!確かにそうですね!!
 絵に描いてみたんですけど、位置関係がそうじゃないとおかしいですよね。そこが分からなかったとこだと思います。


 なんですけど…どうやったらいいかわからないです。。。
 どのようにかいたらいいか教えて頂きたいです。
 お願いします。

 
 ということでUMLロボコンですから、(練習のために)UMLで記述してみてはいかがでしょうか?
 この程度の規模のアルゴリズムで、UMLを何枚も描くのは、ちょっと大げさな気もしますが、ユースケース図とステートマシン図とクラス図が描けるのではないでしょうか?
 で、あとはUMLモデリングのエキスパートの皆さんにお任せします。あせあせ

 UMLはちょっと早すぎるので、書きたいコードを日本語で書いて
 みます。なんなできそうなので、出来たらまた見て下さい。

以前指摘してもらった保存するところなんですけど、今だにわかりません…
実は今日がソースを発表する日でして、本当に申し訳ありませんが、教えていただけないでしょうか?

かなり切羽つまっています。
 ちょっとタイミングを逸してしまったかもしれませんが御勘弁ください。

 まずリングバッファを使ったログの記録方法ですが、シンプルな方法だとリングバッファの要素を指し示すポインタは一つだけ宣言して、新規のデータは常にポインタの指している位置に代入して、ポインタは一つ先に進める、という方法をとります。そしてデータを取り出すときは、現在、ポインタが指し示している位置から一つ先からリングバッファのサイズ(大きさ)だけデータを一周取り出すようにします。こう言う仕組みにするとデータは常に最新のものだけがリングバッファに残り、何らかのトリガー(例えばボタンが押された)で最新(最後)のデータをざくっと取り出すことができます。
 私のPathFinderは、このシンプルな方法でデータをロギングし、ログを解析したい想定外の動作(現象)に遭遇したら、慌ててボタンを押してロボットを停止した後にデータを吸い出す仕組みにしています。

 つぎに仙道さんが考えられた二つのポインタ( head と tail )を使う方法ですが、初期状態(データ0件)では、head と tail の差を 0 とします。この状態からデータを1件追加するには tail の位置にデータを書き込んで、tail を一つ進めますから、head と tail の差は 1 となります。同様にしてデータが2件追加されたときは、head と tail の差は 2、データが3件追加されたときは、head と tail の差が 3、...となります。データをバッファサイズの数(SIZE)追加すると head と tail の差が SIZE となりますが、リングバッファでは SIZE でぐるりと一周しますから結局 head と tail の差は 0 、つまり初期状態に戻ってしまいますから「うまく」ありません。ということでデータを追加するときは、head と tail が一致する直前(データ数が SIZE - 1)でデータ追加を停止する必要があります。逆にデータを取り出すときは初期状態 ( head と tail の差が 0 のとき)で停止する必要があります。
 リングバッファのサイズが SIZE であるにもかかわらず、SIZE - 1 個のデータしか格納できないのが、ちょっと無駄な気がしますが、逆にこの解決方法がありましたら、どなたかフォローをお願いします。耳

 すいません。ありがとうございます。
 
 今、初期状態とかhead と tail が上書く時の位置を
 考え中です。

ログインすると、残り2件のコメントが見れるよ

mixiユーザー
ログインしてコメントしよう!

ETロボコン/UMLロボコン 更新情報

ETロボコン/UMLロボコンのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング