ホームページ作成者スアマとalpha(ハンドルネームです)
見ましょう
A3用紙に相手の似顔絵を書くロボットを作成する。
以下のお達しがありその制限の元に作らせていただきました。
・一人一プログラム!
・ペン以外はキットしか使用不可!
・輪ゴムをきるな!
・後の改良や調整のためにプログラムは可能な部分はサブルーチン、関数、マクロを使う
さて待望のロボ作りです!僕たちが渡されたLegoMindStormsには1,2,3,・・・さまざまな部品がたくさんありました。まあ、二人とも初めてなんで説明書見ながらあくせくと足がキャタピラタイプのを作ってみました〜〜
ところが、ペンを持たせて図形を描かせるには問題点がありました。次の「ペンを持たせる」で詳しく説明します。(相棒のスアマの力作)
ロボにペンを持たせる段階で一番の考えどころはやっぱりペンの上げ下げですね。まさか一筆書きで似顔絵を書かそうなんていうチャレンジャーなことは(少しだけしか)するつもりありませんでしたよ。まあ、実際やってみると意外とすいすいできて取り付けたわれらがロボの勇姿は下をご覧ください。
ところがペンをつけてみると意外と位置が高い。よって次の変形はこうなりました。
しかしいざ書かせてみると、キャタピラだとガタガタ揺れてそりゃもうギアがずれるわ、まともに円も書けんわで大変で急遽大幅路線変更!!キャタピラから安定性の高いタイヤへとカスタマイズ!ほんとに間に合うのかこれ?
まあ、何回も書くのもあれなんで、ぶっちゃけ重心が不安定で前後に行くとペンを持つ側に倒れるので最終形態を見てわかるとおりこうやって安定させました。
いよいよプログラミングです!正直な話相手の顔を書くなんて自分の手でも難しいのにロボにやらせるなんてもっと難しいですよ
とりあえず書いてみました
―――と、いうわけでできたのがこれ。
前進、後退、右回転、左回転、ペンを離す、ペンを押し付ける、その場で回転、直角に左右、60°で左右を省略。これでだいぶ楽になったはず
#define fwd(time) OnFwd(OUT_A+OUT_C); Wait(time); Off(OUT_A+OUT_C); //前進time分 #define back(time) OnRev(OUT_A+OUT_C); Wait(time); Off(OUT_A+OUT_C); //後進time分 #define right(time) OnRev(OUT_A); OnFwd(OUT_C); Wait(time); Off(OUT_A+OUT_C); //time分右回転 #define left(time) OnFwd(OUT_A); OnRev(OUT_C); Wait(time); Off(OUT_A+OUT_B); //time分左回転 #define penup OnFwd(OUT_B); Wait(5); Off(OUT_B); //ペンを浮かす #define pendown OnRev(OUT_B); Wait(5); Off(OUT_B); //ペンを押し付ける #define circle OnFwd(OUT_A); OnRev(OUT_C); Wait(200); Off(OUT_A+OUT+C); //360°回転する #define rightq OnRev(OUT_A); OnFwd(OUT_C); Wait(50); Off(OUT_A+OUT_C); //90°右回転 #define leftq OnFwd(OUT_A); OnRev(OUT_C); Wait(50); Off(OUT_A+OUT_B); //90°左回転 #define rightsixty OnRev(OUT_A); OnFwd(OUT_C); Wait(33); Off(OUT_A+OUT_C); //60°右回転 #define leftsixty OnFwd(OUT_A); OnRev(OUT_C); Wait(33); Off(OUT_A+OUT_B); //60°左回転
完成しましたした。ではいよいよテスト。
結構自信作なんで。一発はありえないと思うけど、いい感じになるかと
task main() { pendown; //顔の輪郭を描いています circle; back(8); //メガネのフレーム中央線 penup; back(16); pendown; back(4); penup; back(16); pendown; back(8); penup; rightq; //下側線の準備 fwd(4); leftq; fwd(8); pendown; //メガネフレーム下側線 fwd(16); penup; fwd(4); pendown; fwd(16); penup; fwd(8); leftq; //上側線の準備 fwd(8); rightq; back(8); //メガネのフレーム上側線 pendown; back(16); penup; back(4); pendown; back(16); penup; back(8); fwd(28); //下唇準備 rightq; back(2); left(17); pendown; //下唇 rightsixty; penup; fwd(28); //上唇準備 leftsixty; pendown; //上唇、口完成 back(28); penup; fwd(28); leftsixty; fwd(28); right(67); fwd(2); rightq; fwd(19); leftq; back(19); pendown; //メガネフレーム縦a back(8); penup; fwd(8); leftq; fwd(16); rightq; pendown; //メガネフレーム縦b back(8); penup; fwd(8); leftq; fwd(2); rightq; pendown; //メガネフレーム縦c back(8); penup; fwd(8); leftq; fwd(16); rightq; pendown; //メガネフレーム縦d back(8); penup; }
これはひどいですね〜
人の顔にさえなってません。
気を取り直してもう一度。
今度こそ完成か?――この展開でもう一度とかだったらいやになるかも。語ってないけどチェックでいろいろ大変でした・・・
気を取り直して今度こそ!!
こっちは相方のスアマのプログラミング。
ここからはスアマにバトンタッチだ。
…ということで、バトンタッチされましたスアマです。
顔の輪郭は、ロボットをその場で回転させた際のペンの軌跡をそのまま利用します。つまり円になります。
測定したところ、円の半径は約6.5cmでした。この数値をベースにメガネ等のパーツの大きさを決定します。ちなみにロボットは2秒(プログラム用の数値で言うと200)で1回転します。
この2つの数値はロボットに絵を描かせる上では非常に重要な意味を持ちます。そのため、正確な数値を測定するために時間がかかるのは仕方ないことです。
さて、プログラムを書く上で、めんどくさがることはなかなかに重要な事だと思います。
めんどくさがるというのはつまり、一連の作業の中で同じ動作をする必要が何度も出てくるなら、その都度細かく命令するのではなく、命令をまとめてすっきりしたプログラムにする、ということです。
「風呂場に行って、服を脱いで、頭を洗って、顔を洗って、体を洗って、湯船に浸かって、湯船からあがって、体を拭いて、服を着て、戻ってきなさい」という命令を「風呂に入ってこい」の一言でまとめるようなモノですね。
相方のalphaは #define を使ってマクロ化することでまとめていましたが、私はどちらかといえば関数やサブルーチンのほうが好きです。何行かに分けて書けるもんね。
さあ、めんどくさがりましょう。
///// サブルーチン化、または関数化 ///// //ペンを押し付けるサブルーチン sub pen(){ OnFwd(OUT_B); Wait(7); Off(OUT_B); Wait(10); } //ペンをもちあげるサブルーチン sub unpen(){ OnRev(OUT_B); Wait(5); Off(OUT_B); Wait(10); } //時計回りにまわる関数。200で1回転。 void clockwise(int t){ OnRev(OUT_A); OnFwd(OUT_C); Wait(t); Off(OUT_A+OUT_C); Wait(10); } //反時計回りにまわる関数。200で1回転。 void c_clockwise(int t){ OnFwd(OUT_A); OnRev(OUT_C); Wait(t); Off(OUT_A+OUT_C); Wait(10); } //前に進む関数。30で6.5cm。 void drive(int t){ OnFwd(OUT_A); OnFwd(OUT_C); Wait(t); Off(OUT_A+OUT_C); Wait(10); } //バックする関数。30で6.5cm。 void back(int t){ OnRev(OUT_A); OnRev(OUT_C); Wait(t); Off(OUT_A+OUT_C); Wait(10); }
回転や直進、後退は秒数の指定ができるように関数を使用、ペンの昇降には変数が必要ないのでコンパイルの際に展開されないサブルーチンを使用しました。
精密に動いて欲しいので、各動作のあとに100ミリ秒の待ち時間を作りました。
顔を描くわけですが、このロボは四角形を書くのが非常に苦手です。相方は四角いメガネをかけているのですが、四角形に見えることに重点を置いて絵を描かせてみます。
以下は手書きの設計図です。ロボ書きではありません。
上の通り、メガネを-[ ]-[ ]-で表現しました。苦肉の策というかなんというか。
-[の部分は、
中心角が非常に小さいおかげで、直線に見えてくれます。上下のフレームも実は平行ではないですが無視できる程度です。ゴマカシ
では、これをプログラムに起こしましょう。
///// ぼちぼち描きはじめましょう。 ///// task main (){ pen(); clockwise(200); //輪郭。普通に1回転させます。 //ちなみにペンは紙につきっぱなしです。 //眼鏡を描きはじめます。 repeat(2){ //レンズは2つある。繰り返しましょう。 back(12); //フレーム unpen(); clockwise(3); //レンズの左がわ back(2); pen(); drive(2); c_clockwise(6); back(2); unpen(); drive(2); clockwise(3); back(12); clockwise(3); //レンズの右がわ drive(2); pen(); back(2); c_clockwise(6); drive(2); unpen(); back(2); clockwise(3); pen(); } back(12); //実はまだ右端のフレームが残っていた unpen(); //鼻 drive(60); c_clockwise(50); back(25); pen(); drive(10); c_clockwise(2); unpen(); clockwise(2); //口 drive(10); clockwise(25); pen(); c_clockwise(50); unpen(); }
これを動かした結果がこちら。
メガネをあともうちょっとだけ四角形っぽくできたらよかったかもしれません。
授業でもらったA3の紙がなくなってしまった関係でA4のコピー用紙を使っています。
とりあえず二人でがんばった工夫点とかを写真とか文章でいい感じにまとめようかと
まあ、ここまできたら最後まで付き合ってやってください。
後輪代わり。実はこのロボ、二つしかタイヤがないというつくり。
不安定なのでこれで代わりにバランスをとってるが意外な問題点がこの後浮上した。
実はこれがさっきの後にできた問題点を改善した工夫。
走らせて見てようやくわかったのだが、前に走らせて急に止まったり、逆走したりすると前のめりになって、前にも支えが必要だという結論に達した。
左右でモーターの速さが違うのにも苦労した。
ペンの上下のタイミングをどうすればわからなくて描けてなかったり、逆にペン先を押しつぶしそうなほど紙に押し付けたりしてしまった。
最初はゴムで回転を伝達していたがペンを上下するのに使うこまごまとした動きに向いていなくて、ギアに変えるのに苦労した。
最初に設定されている速さが速過ぎて、ペンで書くのがついていけていない
等等、プログラミング関係で苦労した点は多々あり、列挙していけばきりがないのでここで打ち止めにします。エンドロールまであと少し。
眠気にまかせてぶちぎれてごめんよ。
とりあえずロボットを作ってるときが楽しかった。でもプログラミングがなかなかできないのに苛立った。
いろいろ考えながら実践していき自分なりの正解に行き着くのはとても新鮮な体験だった。
とりあえず最後にしゃれで作ったロボットです。
ぜひコメントを
時間をかけて丁寧に書いており、良いです。 FI