2016b/Member
目次

初めに

今回の課題では、文字(漢字7画以上)をロボットで書くというものであった。 NXTを使いどのような構成でロボットを作り、プログラムを考えたかを以下にまとめていく。

構想

まずロボット自体の構造として、

  1. ロボット自身が走行して文字を書く
    • 初めから動きをプログラミングする
    • 光センサーで最初に文字をトレースし、そのデータを元に再現する
  2. ロボットをXYで制御して書く
    • プリンターのようにX軸を1進め、その一列に存在するY軸の書き込み対象をまとめて行う
    • 一画ずつ制御する(一筆書き)

以上のことを考えたが、そもそもレゴ初心者+ロボット初心者+プログラミング初心者の三点揃いであったため、
過去のレポートを参考にしつつ、ロボットを自走し、その経路をプログラミングすることにした。

ロボット本体

今回ロボットの制作にあたり、4世代の改良を行った
実際に文字を書くのに行き着いたのは、2世代と3世代である。
2世代

まず最初の機体を作る上で考慮したのは、ペンを回転軸の中心に置くことです。
中心にペンがない場合、プログラミングにおいて誤差を考慮しなければならず、
ただ動かすだけではうまく行かないと考えたからです。
初期型は考えなしに作りましたが、ペンの筆圧が不安定でした。
原因としては、使っているマイネームが、レゴのパーツと大きさの互換性がなく、ずれてしまう点でした。
これを解消すべく2号機ではレゴの棒を使うことにより、骨組みよりも細いことを利用して、
初号機のゴムでの固定をやめ、挟み込む設計に変更しました。
ペンの機構はモジュール化し、改良がしやすいように工夫をしています。
ペンモジュール
三号機は、発表会でのペンが引っかかる問題への解決策として、
ペンの機構自体を高めに変更して、なるべく垂直になり、 かつ、前後の運動に強くなるように、二箇所による固定をするように変更しました
3号機

プログラミング

今回は角度制御を行ってなるべく簡単に(誤差があると思われるので修正しながら)
動かし書くことができるようなプログラム
にしていきたいと思い考えました。

角度制御

角度制御では、

RotateMotor(OUT_AB,p,a)              //pはモーター出力、aは回転角度
RotateMotorEx(OUT_AB,p,a,s,true,true)//pはモーター出力、aは回転角度、sは同期割合,true=同期する,true=終了後モーターブレーキ

を使った。
資料にも乗っているが、こちらのサイトにかなり関数がまとまって紹介されている(ただし英語)
http://bricxcc.sourceforge.net/nbc/nxcdoc/nxcapi/group___output_module_functions.html
メインタスクでこのような関数を書き込み続けたら、読めなくなるのは必須であったので

#define move_AB(p,a) ResetTachoCount(OUT_AB);RotateMotor(OUT_AB,p,a);Wait(300);				//(p=出力,a=モータ回転角度)
#define turn_AB(p,a,s) ResetTachoCount(OUT_AB);RotateMotorEx(OUT_AB,p,a,s,true,true);Wait(300);		//(p=出力,aモータ回転角度,s=AB同期割合(右+,左-))

と定義した。※ResetTachoCount(OUT_AB)を行っているのは、後述の45度問題の名残

NXTに計算させてみよう(サブルーチン)

この定義によってかなり短く出来たが、これでも毎回回転する角度を、計算するのは面倒であるので
NXT自体に計算させることにした。

//計算ルーチンはじめ
float Calculation_move_AB(float d)//全身、後退したい(cm)を受け付け、モーターの回転角度を出力する
{
const float diameter=56;					//タイヤの直径(mm指定),constは変 数の値が変更禁止を表す
const float pi=3.1415;						//円周率(π)
float ang = -1*(d*10)*360/(pi*diameter);			//前進に必要な角度を計算する
return ang;							//ルーチンを呼び出したところに値を返す 
}

float calculation_turn_AB(float u)//その場でロボット本体の回転したい受け付け、モーターの回転角度を出力する
{
const float interval_of_tire=134;				//タイヤの間隔(mm指定)
const float diameter=56;					//タイヤの直径(mm指定)
float ang = interval_of_tire*u/diameter;			//全身に必要な角度を計算する
return ang;							//ルーチンを呼び出したところに値 を返す 
}
//計算ルーチン終わり

これは資料に乗っていたものを参考にし、前進後退用と、回転用の2種類を作った。

もっとtask mainに書くコード短くしたいな編

#define ですでに角度制御を行うマクロを作ったが、それでも

task main()
{
turn_AB(p,calculation_turn_AB(d),s)
・・・・・以下に続く
}

のように書くのは長くて辛いと感じました そこで新たにマクロを短くできないのかということで

#define move_AB_cm(p,c) move_AB(p,Calculation_move_AB(c));	//move_ABのcm作動型
#define turn_AB_Degree(p,d,s) turn_AB(p,calculation_turn_AB(d),s);//(p=出力,d=回転したい角度,s=AB同期割合(右+,左-))

を定義しました。
そしてよく使う
45度
90度
180度
を、すぐに呼び出せるように

#define turn_180 turn_AB_Degree(power,180,+100);		//180度ターン
#define turn_45r turn_AB_Degree(power,45,+100);			//右に45度ターン
#define turn_45l turn_AB_Degree(power,45,-100);			//左に45度ターン
#define turn_90r turn_AB_Degree(power,90,+100);			//右に90度ターン
#define turn_90l turn_AB_Degree(power,90,-100);			//左に90度ターン
#define turn_135r turn_AB_Degree(power,135,+100);		//右に135度ターン
#define turn_135l turn_AB_Degree(power,135,-100);		//左に135度ターン

と定義したので

task main()
{
turn_90l
}

とするだけで、90左ターンしてくれるようになりました。 これなら何度見ても可読性が高いのでなんとかなります。

45度問題

メインタスクに書き込む文字を簡単にできるようになったので、誤差を修正するために、

turn_90l

などを20回など繰り返しおこない、タイヤの間隔の誤差を修正することをしていきましたが、 45度の時だけ、2回目以降の駆動がよく回らない現象に出くわしました。 要するに、1回目の45度では45度ロボットが回れるぐらいにモーターを回しますが、 二回目以降明らかに回転が足りない、 ということです。 不思議なことにこの現象は45度の左右のみでしか起らないという点です。 計算ルーチンはうまく動いているはずなのになぜうまく行かないかまだ原因不明です。

45度問題への解決策

NXT自身のスペックは高くないというディスリスペクトをしてくれた方がいたので、 定義を

#define turn_180 turn_AB(power,471,+100);				//180度ターン
#define turn_45r turn_AB(power,108,+100);				//右に45度ターン
#define turn_45l turn_AB(power,108,-100);				//左に45度ターン
#define turn_90r turn_AB(power,217,+100);				//右に90度ターン
#define turn_90l turn_AB(power,190,-100);				//左に90度ターン
#define turn_135r turn_AB(power,323,+100);				//右に135度ターン
#define turn_135l turn_AB(power,323,-100);				//左に135度ターン

に変更して、一々値を計算しないように変更を行いました。 変更したところも45度問題はなくなりましたが、なぜこのようなことになったのかはわかりません。 実際のところ、電卓の変数機能を使って計算すれば、NXTに計算させなくてもなんとかなることがわかりました。 ですが、前後の移動は流石に数が多いので、計算ルーチンを引き続き有効にしていきたいと思います。
※問題は起きていないので

ペンの稼働のための定義

これもできるだけ短くかつ、わかりやすくしたいので、マクロを定義しました

#define on ResetTachoCount(OUT_C);RotateMotor(OUT_C,20,42);	//ペンを書き込み可能状態にする
#define off ResetTachoCount(OUT_C);RotateMotor(OUT_C,20,-42);	//ペンを書き込み不可能状態にする

ここで動かくモーターの角度は、世代ごとに異なるのでその都度調節します

文字を書いてみよう!編

前置きが長くなりましたがこれで、コンパクトに文字を書く時間がやって来ました。 今回挑戦したのは「食」です。 これを選んだ理由は、45,90,180,135度のターンと直線ができれば書くことができると考えたからです。 文字の設計段階では、文字を正方形のブロックをベースとして考えているため、45°での前進は、ブロックとの長さの辻褄を合わせるために√2をかけるものとします。
設計図

#define power 30   //モーターの出力を一律に設定する
#define move_AB(p,a) RotateMotor(OUT_AB,p,a);			//(p=出力,a=モータ回転角度)
#define turn_AB(p,a,s) RotateMotorEx(OUT_AB,p,a,s,true,true);	//(p=出力,aモータ回転角度,s=AB同期割合(右+,左-))
#define move_AB_cm(p,c) move_AB(p,Calculation_move_AB(c));	//move_ABのcm作動型
#define turn_AB_Degree(p,d,s) turn_AB(p,calculation_turn_AB(d),s);//(p=出力,d=回転したい角度,s=AB同期割合(右+,左-))
#define turn_180 turn_AB(power,471,+100);				//180度ターン
#define turn_45r turn_AB(power,108,+100);				//右に45度ターン
#define turn_45l turn_AB(power,108,-100);				//左に45度ターン
#define turn_90r turn_AB(power,217,+100);				//右に90度ターン
#define turn_90l turn_AB(power,190,-100);				//左に90度ターン
#define turn_135r turn_AB(power,323,+100);				//右に135度ターン
#define turn_135l turn_AB(power,323,-100);				//左に135度ターン 
#define on ResetTachoCount(OUT_C);RotateMotor(OUT_C,20,42);	//ペンを書き込み可能状態にする
#define off ResetTachoCount(OUT_C);RotateMotor(OUT_C,20,-42);	//ペンを書き込み不可能状態にする
#define move(c) move_AB_cm(power,c) //move_AB_cmのモーター出力をpowerに固定することで、一律に出力を変更可能にする
//計算ルーチンはじめ
float Calculation_move_AB(float d)     //全身、後退したい(cm)を変数dで受け付け、モーターの回転角度を出力する
{
   const float diameter=56;					//タイヤの直径(mm指定),constは変 数の値が変更禁止を表す
   const float pi=3.1415;						//円周率(π)
   float ang = 1*(d*10)*360/(pi*diameter);			   //前進に必要な角度を計算する
   return ang;							//ルーチンを呼び出したところに値を返す 
}

float calculation_turn_AB(float u)     //その場でロボットを回転したい角度を変数uで受け付け、モーターの回転角度を出力する
{
   const float interval_of_tire=134;				//タイヤの間隔(mm指定)
   const float diameter=56;					//タイヤの直径(mm指定)
   float ang = interval_of_tire*u/diameter;			//全身に必要な角度を計算する
   return ang;							//ルーチンを呼び出したところに値 を返す 
}
//計算ルーチン終わり



task main()
{
   const float x=0.5;						//xは全体の拡大率(cm)
   const float sqrt_2=1.41421356237;				//√2の値


   turn_45r;

   on;			//一画目
   move(4*sqrt_2*x);           //xによって文字の大きさを一律に変更可能にする、斜め45度での前進は、正方形では一辺の√2となるので、これがないと文字に誤差が生じる

   off;
   turn_180;
   move(4*sqrt_2*x);
   turn_90r;

   on;			//2画目
   move(3*sqrt_2*x);

   off;
   turn_180;
   move(3*sqrt_2*x);
   turn_135l;

   on;			//3画目
   move(4*x);

   off;
   turn_90r;
   move(2*x);
   turn_180;

   on;			//4画目
   move(4*x);
   turn_90r;
   move(3*x);
  
   off;
   turn_90r;
   move(4*x);
   turn_90r;
   move(1.5*x);
   turn_90r;

   on;			//5画目
   move(4*x);

   off;
   turn_90r;
   move(1.5*x);
   turn_90r;
   move(4*x);
   turn_180;

   on;			//6画目
   move(4*x);
 
   off;
   turn_90l;
   move(3*x);
   turn_90l;
   move(4*x);
   turn_90l;

   on;			//7画目
   move(8*x);
   turn_135l;
   move(2*sqrt_2*x);


   off;
   turn_90r;
   move(2*sqrt_2*x);
   turn_45r;
   move(x);
   turn_135r;

   on;			//8画目
   move(5*sqrt_2*x); 

   off;
   turn_180;
   move(1.5*sqrt_2*x);

   on;			//9画目
   turn_90r;

   move(2*sqrt_2*x);
   off;
}

実際に書いてみて

実際に書いた文字
カレンダー級の大きさで書いた時は、あまり気にならなかったところが、小さく書くと目立ち始めるようになってきた。 画像では5cm*7cmの枠に入るサイズで書かれている 原因としては、

  • 完璧には中心を捉えきれていないこと
  • 移動中にペンがずれてしまうこと
  • 積み重なるターン時の誤差
  • ペンを垂直におろしきれていないので、ペンの着地地点がずれる

等が考えられる。

まとめ

プログラミング自体は厳格にしなければならないが、ロボットは厳格には動かないというジレンマを抱えていることを身を持って経験しました。 一番身にしみたのは、コンパイルエラーは、「コンパイルした時点で起きたエラー」であることでした。 マクロや、ルーチンを組んでいる場合、そちらにエラーがあっても、呼び出されるまでは、 エラーにならないということを考えると、マクロも複雑にならないようにすることが、望ましいようです。 実際にロボコンに参加している人は裏でかなり苦労していることを想像するのは難しくありません。 今回はロボットを出来る限りの問題に対応できるよう考えて作りましたが、それでも問題に直面します。 今回の経験を元に、新しいロボットを作っていけるようにしていきたいです。 そして、今回の課題を作る上で、いろいろと協力してくれた受講生の皆さんにこの場でお礼をしたいと思います。 ありがとうございました。


添付ファイル: filesyoku_sekkeizu.jpeg 133件 [詳細] filetaberu.jpg 117件 [詳細] filerobo_2.jpg 124件 [詳細] filepen_1.jpg 133件 [詳細] filerobo_1.jpg 133件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-11-18 (金) 14:53:05