課題1は、15cm四方の中に「松本での新生活を表す漢字を書くロボット」を作成する課題である。
詳しくは2017a/Mission1を参照。
大学生になり、自由な時間が多くなる分、自分で自分の生活を管理し律することがより必要になるということで、「律」という漢字を書くことにした。
書き順は次のようにした。これは後でするプログラムの説明の時にも参考にすると良いかもしれない。
書き順が正しい書き順と違っているのは、4・5・6画目を一続きで書いた方がプログラムを作る際に楽になると思ったためである。
ロボットを組み立てるときにまず考えたことは、できるだけプログラムによるモータの時間制御が行いやすい機構にするということである。
そこで、旋回の動作を行わない機構を考えることにした。旋回を行わない理由としては、旋回の動作は時間の設定によって角度が決まるため、正確な時間を設定しないと線の位置がずれてしまうということ。それから、線を引くたびに旋回を行わなくてはならず、その都度時間を細かく設定しないとならないということが考えられたからである。
以上のことから直線運動のみで字を書くことのできる機構にしようと考えた。具体的にはロボットにアームを取り付け、ロボットの前後移動でY軸方向の移動を、アームの駆動でX軸方向の移動を行い字を書く機構にしている。
ロボット本体は前後移動のみで良いため、モータは1個とキャタピラで走るようにした。キャタピラを選んだのは、タイヤなどよりも広く床に設置しているため、安定感があると考えたからである。
初めは振動が大きいのではないかと不安もあったが、補助輪をつけるなど調整をしたことで全く問題ないほどに振動を抑えることができた。
アーム部分は、ピニオンギアとラックを使用して駆動させている。ここでは、アームに取り付けている支えの部分とペンの駆動・保持の部分について説明していこうと思う。
製作の途中で、アームが伸びてロボットから離れると先端の重みに耐えきれずに傾いてしまうことが分かった。そこで、ローラーを取り付けてアームを支えることで安定してアームを動かすことができるように工夫している。支えはアームの両端にそれぞれつけているが、位置関係は下の【アーム全体】の写真で見てほしい。
ペンの上げ下げはモーターの回転をウォームギア(ギア比1:3)で減速させ、その回転をさらにプーリーで伝達させることで行っている。また、これらの機構とモーターを密集させると、先ほど書いたアーム部分の傾きと安定性に影響を及ぼしてしまうように感じた。そのため、モーターをシャフトを使って延長しながらペン保持部と反対の端に設置している。ウォームギアとプーリの部分は下の写真のようになっている。
モータに関しては、【アーム全体】の写真で確認してほしい。
ペンはシンプルに輪ゴムで取り付けている。
プログラムについては、変数宣言・マクロ・サブルーチン、メインタスクの順に説明していく。
#define UD_TIME 100 //ペンの上げ下げのための時間(1秒) int dif_y , dif_x; //現在の場所から次の場所までのX軸方向・Y軸方向の移動時間 int leng_y , leng_x; //線の長さ(時間) sub Set_RU() //ペンを書きはじめの位置にセット<右上> { OnFwd(OUT_A); Wait(dif_y); Off(OUT_A); OnFwd(OUT_C); Wait(dif_x); Off(OUT_C); } sub Set_RD() //ペンを書きはじめの位置にセット<右下> { OnRev(OUT_A); Wait(dif_y); Off(OUT_A); OnFwd(OUT_C); Wait(dif_x); Off(OUT_C); } sub Set_LU() //ペンを書きはじめの位置にセット<左上> { OnFwd(OUT_A); Wait(dif_y); Off(OUT_A); OnRev(OUT_C); Wait(dif_x); Off(OUT_C); } sub Set_LD() //ペンを書きはじめの位置にセット<左下> { OnRev(OUT_A); Wait(dif_y); Off(OUT_A); OnRev(OUT_C); Wait(dif_x); Off(OUT_C); } sub Pen_Up() //ペンを持ち上げる { OnRev(OUT_B); Wait(UD_TIME); Off(OUT_B); } sub Pen_Down() //ペンを下ろす { OnFwd(OUT_B); Wait(UD_TIME); Off(OUT_B); } sub Write_Line_Fwd() //線を引く:正方向(下→上、左→右) { OnRev(OUT_A); Wait(leng_y); Off(OUT_A); OnFwd(OUT_C); Wait(leng_x); Off(OUT_C); } sub Write_Line_Rev() //線を引く:逆方向(上→下、右→左) { OnFwd(OUT_A); Wait(leng_y); Off(OUT_A); OnRev(OUT_C); Wait(leng_x); Off(OUT_C); }
グローバル変数とマクロに関しては、コメント文で説明できていると思うので省略する。
サブルーチンに関しては大きく分けて3種類、全部で8個設定している。基本的にこちらもコメント文に書いてあるが、それぞれ説明していこうと思う。
ペンを各線の書き始めの位置へ移動させる。移動する方向でそれぞれ作っており、右上・右下・左上・左下の4方向で分けている。
名前の通りペンの上げ下げを行うためのものである。UD_TIMEを100(1秒)に設定しているので、ペンは90°ほどの角度で上げ下げを行う。
線を引くためのものである。ロボットを初めに左下においてから動作を始めるため、左下を原点と考えている。そのため「左から右」と「下から上」を正方向、「右から左」と「上から下」を逆方向としている。
当たり前のことだが、漢字(他の文字もそうだが...)を書くということは線を引くという動作を繰り返すことである。つまり、毎回行う動作は向きの違いはあっても基本的には同じ動き(線を引く動作)なのだ。そこで、共通している動作を繰り返すプログラムをベースとして、各線で違うもの(変数の数値や移動方向)だけ場合分けして設定できるようにした。具体的には以下のような繰り返しを行っている。
ペンを持ち上げる→書き始めの位置・線の長さを指定→書き始めの位置へ移動→ペンを下ろす→線を引く(→初めに戻る)
実際に使用したのは次のプログラムである。詳しい説明はプログラムの下に書く。
task main() { int str_cnt=0; //画数 int i=0; //for文用変数 /*-----1画毎ループ 10回繰り返し-----*/ for(i=1;i<11;i++){ str_cnt++; //画数+1→次の画へ /*-----ペンを持ち上げる 1・5・6画目以外-----*/ else if(str_cnt==2){ Pen_Down(); } else if(str_cnt==3){ Pen_Down(); } else if(str_cnt==4){ Pen_Down(); } else if(str_cnt==7){ Pen_Down(); } else if(str_cnt==8){ Pen_Down(); } else if(str_cnt==9){ Pen_Down(); } else if(str_cnt==10){ Pen_Down(); } /*-----0.1秒待ち-----*/ Wait(10); /*-----ペンセットの位置・線の長さを指定-----*/ if(str_cnt==1){ dif_x=20; dif_y=100; leng_x=15; leng_y=15; } else if(str_cnt==2){ dif_x=15; dif_y=0; leng_x=15; leng_y=15; } else if(str_cnt==3){ dif_x=10; dif_y=13; leng_x=0; leng_y=90; } else if(str_cnt==4){ dif_x=30; dif_y=105; leng_x=35; leng_y=0; } else if(str_cnt==5){ dif_x=0; dif_y=0; leng_x=0; leng_y=40; } else if(str_cnt==6){ dif_x=0; dif_y=0; leng_x=35; leng_y=0; } else if(str_cnt==7){ dif_x=15; dif_y=20; leng_x=60; leng_y=0; } else if(str_cnt==8){ dif_x=40; dif_y=35; leng_x=20; leng_y=0; } else if(str_cnt==9){ dif_x=35; dif_y=20; leng_x=55; leng_y=0; } else if(str_cnt==10){ dif_x=28; dif_y=100; leng_x=0; leng_y=120; } /*-----0.1秒待ち-----*/ Wait(10); /*-----ペンをセット(書き始めの位置へ移動)-----*/ if(str_cnt==1){Set_RU();} else if(str_cnt==2){Set_RD();} else if(str_cnt==3){Set_RU();} else if(str_cnt==4){Set_RU();} else if(str_cnt==7){Set_LU();} else if(str_cnt==8){Set_LD();} else if(str_cnt==9){Set_LD();} else if(str_cnt==10){Set_LU();} /*-----0.1秒待ち-----*/ Wait(10); /*-----ペンを下ろす(5・6画目はそのまま続けて書く)-----*/ if(str_cnt!=5){ if(str_cnt!=6){ Pen_Down(); } } /*-----0.1秒待ち-----*/ Wait(10); /*-----線描画(1・2画目は斜め、6画目は直線(逆方向)、それ以外は直線(正方向))-----*/ if(str_cnt==1){ OnRev(OUT_A); OnRev(OUT_C); Wait(leng_x); Off(OUT_AC); } else if(str_cnt==2){ OnRev(OUT_A); OnRev(OUT_C); Wait(leng_x); Off(OUT_AC); } else if(str_cnt==6){ Write_Line_Rev(); } else{ Write_Line_Fwd(); } /*-----0.1秒待ち-----*/ Wait(10); } //end for Pen_Up(); //最後にペンを持ち上げて終了 } //end main
テキストには載っていなかったが、自分で調べてfor文も使用してプログラムを作成している。for文は全体の繰り返しに使っていて、「律」は10画の漢字のため10回繰り返すようになっている。
for文で1周するたびに変数“str_cnt”に+1することで画数をカウントしている。この値をif文で判断することで、各線によって違う箇所(変数へ入れる数値の設定や動作など)をそれぞれ行えるようにしている。
また、【選んだ漢字】のところでも説明しているが、4・5・6画目の線はペンを上げる必要が無い書き順のため、つなげて書いている。それから、それぞれの動作毎に0.1秒待たせているのは、動きを確認しやすくするためである。
全体的にはバランス良く字が書けているように思うが、行人偏の斜めの線が歪んでしまったのが何度調整しても直らずとても残念だった。ロボットの振動が完全に抑えられていないことが原因だと思う。アームの支えをさらに改良すれば良くなったかもしれない。それに加えて、X軸方向とY軸方向のモータの動きが完全に同時ではないことも原因の1つかもしれない。
また、偏と旁が上下にずれてしまっているのだが、これは斜めの線の歪みと違い時間があればなんとかなったはずである。これに関しては、間違いなく時間に余裕を持って製作できなかったことが原因であり、今回の課題の1番の反省点だと思う。もっと計画的に製作を進めるべきだった。
問題として解決せずに残ってしまった点や反省点もいくつかあるが、課題1の製作全体で見れば良くできたと感じる。最初の課題だったこともありなかなかスムーズに作業を進めることができなかったが、ある程度納得のいくロボットを製作することができたと思う。
今回の課題で製作のコツも少しずつだが分かってきたように感じる。今回学んだことを生かして、特に【まとめ】で述べた反省点の「計画的な製作」は次の課題2で実行できるようにしたい。