[[2016b/Member]] #contents * 概要 [#k332b24c] 今回の[[課題:http://yakushi.shinshu-u.ac.jp/robotics/?2016b%2FMission1]]は書道ロボットである. 一般的なローバーにペンをつけただけでは,H/Wは簡単でもS/Wで困難を極めるので,縦軸を動くロボットと横軸を動くロボット,そしてペンの昇降装置をつくり,それぞれに役割を分担させることにした.こうすることでコーディングが簡単になる. * ハードウェアについて [#fe7dae33] #ref(2016b/Member/ta.kashi/Mission1/Fig_001.JPG,80%,完成したロボット) もともとは自動車工場などで稼働しているような「アームロボット」型のものを作る予定だっただが,部品点数の少なさやS/Wが複雑になりすぎることが判明したので断念した. かわって,冒頭で記した通り,縦軸(以降Y軸と呼ぶ)を動くローバーロボットに高架を接続し,その上に,横軸(以降X軸と呼ぶ)を動くローバーロボットを走らせる.そのローバーロボットにペンの昇降を制御するアームを取り付け,Y軸移動,X軸移動,ペンの昇降をそれぞれ独立して制御するようにした. ** Y軸制御ロボット [#w18d7065] #ref(2016b/Member/ta.kashi/Mission1/Fig_002.JPG,80%,Y軸制御ロボット) 写真の通り,基本的なローバーロボットに高架を接続した. ローバーロボット本体に関しては途中で曲がらずに,より直線的に移動する必要があり,また,高架にアーム付きローバーロボット(重い)が乗ることを考え,できるだけ安定する必要がある.そのためにホイールスペースをできるだけ長くとった. また,ローバーロボットと反対側を支える支柱はできるだけ滑らかに動作し,引っかかることのないようにしなければならなかったため,あえてタイヤ(ゴム)を外したが,あまり改善されなかった. 高架部分は長いブロックを使用し,重さに耐えられるようにした.途中つなぎ目部分で段差があるが,動作に支障はなかった. ** X軸制御/ペン昇降ロボット [#bf60eca9] #ref(2016b/Member/ta.kashi/Mission1/Fig_003.JPG,80%,X軸制御/ペン昇降ロボット) 基本的なローバーロボットであるが,車輪の幅が高架の幅と一致するようになっている.また,動作時の振動や通信ケーブルのねじれによって脱輪することのないように,ロボット下側にガイドを取り付けた. #ref(2016b/Member/ta.kashi/Mission1/Fig_004.JPG,50%,ガイド) Y軸制御ロボットを作った段階で部品が不足していたため,アームの取り付けにはかなり無理をしている(ただし動作に支障はなかった). 当初は,ペンの昇降は"4211510"と呼ばれる部品(ねじ状の部品)を使い,エレベーターのように行う予定であったが,部品の不足と重量の問題で断念し,かわりにモーターにペンを取り付けるだけのシンプルな構成にした.これによって部品を大幅に節約できるが,ペン先を紙面に対して垂直に当てられないため,ペンを降ろす際の力加減によっては,これが動作時の微妙なずれの原因となるようである.これはS/Wによるキャリブレーションで解決している. * ソフトウェアについて [#x786cf6a] 課題では「7画以上の漢字」を書く必要があるが,H/Wの仕様上斜めに線を描くことが難しい(実際に試したが無視できないほどの誤差が生じてしまった)ので,極力水平・垂直な線の多い文字を選ぶ必要がある.今回は「車」「豆」という文字を選んだ. 以下がそのコード(「車」)である. //OUT_A -> Y軸移動(縦) //OUT_B -> X軸移動(横) //OUT_C -> Z軸移動(ペン) #define MP_XY 20 //モーターの強さは20に固定 #define MP_Z 10 //ただしペンの昇降は弱めにする sub X_ML(int time) //X軸左方向 { OnRev(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub X_MR(int time) //X軸右方向 { OnFwd(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_UP(int time) //Y軸上方向 { OnRev(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_DWN(int time) //Y軸下方向 { OnFwd(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Z_UP() //ペンをあげる { RotateMotor(OUT_C,MP_Z,-20); Off(OUT_ABC);Wait(1000); } sub Z_DWN() //ペンをおろす { RotateMotor(OUT_C,MP_Z,20); Off(OUT_ABC);Wait(1000); } task main() { /* "Z_DWN()"から"Z_UP()"までの動作で線を描画する. それ以外の動作は移動のみで,実際に描画はしない. ペン先を紙面に当てた状態で実行すること. */ Z_UP(); Z_DWN();X_MR(1000);Z_UP(); Y_DWN(200); Z_DWN();X_ML(1000);Y_DWN(600);Z_UP(); Y_UP(600);X_MR(1000); Z_DWN();Y_DWN(600);Z_UP(); Y_UP(400); Z_DWN();X_ML(1200);Z_UP(); Y_DWN(400); Z_DWN();X_MR(1200);Z_UP(); Y_DWN(300); Z_DWN();X_ML(1200);Z_UP(); Y_UP(1500);X_MR(600); Z_DWN();Y_DWN(1600);Z_UP(); } ** モーターの対応 [#nf95a1f0] //OUT_A -> Y軸移動(縦) //OUT_B -> X軸移動(横) //OUT_C -> Z軸移動(ペン) これは,それぞれの出力が対応するモーターを示している(コメント). ** モーターのトルクを定義 [#d3f0263b] #define MP_XY 20 //モーターの強さは20に固定 #define MP_Z 10 //ただしペンの昇降は弱めにする これは,モーターのトルクをマクロ置換している(MP=Moter Power). H/Wの動作,特に,ペンの昇降についてはゆっくりと行わないと誤差の原因となる.X軸制御/ペン昇降ロボットについては,あまり高速に動作させると高架から脱輪することもあった. ** 各種動作の定義 [#i2b29751] sub X_ML(int time) //X軸左方向 { OnRev(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub X_MR(int time) //X軸右方向 { OnFwd(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } これは,X軸方向の移動をsubroutineとして定義したものである. 動作時間(=移動距離)を引数としている.動作後には1秒間静止するようにしている.これは,振動によるズレを防ぐためである. 以下も同様 sub Y_UP(int time) //Y軸上方向 { OnRev(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_DWN(int time) //Y軸下方向 { OnFwd(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Z_UP() //ペンをあげる { RotateMotor(OUT_C,MP_Z,-20); Off(OUT_ABC);Wait(1000); } sub Z_DWN() //ペンをおろす { RotateMotor(OUT_C,MP_Z,20); Off(OUT_ABC);Wait(1000); } ペンの昇降についてはOnFwd(OnRev)ではなく,回転の角度を指定できるRotateMotorを使用した. ** task main() [#s7e23c40] task main() { /* "Z_DWN()"から"Z_UP()"までの動作で線を描画する. それ以外の動作は移動のみで,実際に描画はしない. ペン先を紙面に当てた状態で実行すること. */ Z_UP(); Z_DWN();X_MR(1000);Z_UP(); Y_DWN(200); Z_DWN();X_ML(1000);Y_DWN(600);Z_UP(); Y_UP(600);X_MR(1000); Z_DWN();Y_DWN(600);Z_UP(); Y_UP(400); Z_DWN();X_ML(1200);Z_UP(); Y_DWN(400); Z_DWN();X_MR(1200);Z_UP(); Y_DWN(300); Z_DWN();X_ML(1200);Z_UP(); Y_UP(1500);X_MR(600); Z_DWN();Y_DWN(1600);Z_UP(); } 最初はペン先を紙面に当てた状態にしておく.これは,線を描く際に確実にペン先を紙面に当てるためである.プログラムを実行すると同時にペン先は指定した角度(20度)上昇する. 以降,Z_DWN()でペン先が紙面に当たり,Z_UP()でペン先が離れるわけなので,Z_DWN()からZ_UP()で挟まれた部分は実際に紙面に描画され,それ以外は描画されない,ただの移動となる(ペン先は空中を移動する). 注意する点は,特にY軸方向に描画する際に,図のように手前方向へ移動するのはOKだが,奥方向はNGである(線がかすれてしまう). #ref(2016b/Member/ta.kashi/Mission1/Fig_005.JPG,80%,ペン先の移動) ** 別のコード [#a97277bb] 「車」以外にも「豆」という漢字も書くことができた. //OUT_A _> Y軸移動(縦) //OUT_B _> X軸移動(横) //OUT_C _> Z軸移動(ペン) #define MP_XY 20 //モーターの強さは20に固定 #define MP_Z 10 //ただしペンの昇降は弱めにする sub X_ML(int time) //X軸左方向き { OnRev(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub X_MR(int time) //X軸右方向 { OnFwd(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_UP(int time) //Y軸上方向 { OnRev(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_DWN(int time) //Y軸下方向 { OnFwd(OUT_A,MP_XY);Wait(time); Off(OUT_ABC);Wait(1000); } sub Z_UP() //ペンをあげる { RotateMotor(OUT_C,MP_Z,-20); Off(OUT_ABC);Wait(1000); } sub Z_DWN() //ペンをおろす { RotateMotor(OUT_C,MP_Z,20); Off(OUT_ABC);Wait(1000); } task main() { /* "Z_DWN()"から"Z_UP()"までの動作で線を描画する. それ以外の動作は移動のみで,実際に描画はしない. ペン先を紙面に当てた状態で実行すること. */ Z_UP(); Z_DWN();X_MR(1000);Z_UP(); Y_DWN(300);X_ML(300); Z_DWN();X_ML(700);Y_DWN(400);X_MR(800);Z_UP(); Y_UP(400); Z_DWN();Y_DWN(400);Z_UP(); X_ML(600);Y_DWN(200); Z_DWN();Y_DWN(400);Z_UP(); X_MR(500);Y_UP(400); Z_DWN();Y_DWN(400);Z_UP(); X_ML(800);Y_DWN(200); Z_DWN();X_MR(1200);Z_UP(); } * プリンターのような動作も [#l8b06310] インクジェットプリンターのように動作させ,文字(やイラスト)を描画することもできる.この場合,Y軸制御ロボットがプリンターの紙送り機能を,X軸制御/ペン昇降ロボットがプリンターのヘッドの役割をなす. 下図のように,文字を20x20のドットに分割して考える. #ref(2016b/Member/ta.kashi/Mission1/Fig_006.JPG,80%,デザイン) 以下がそのコードである. //OUT_A _> Y軸移動(縦) //OUT_B _> X軸移動(横) //OUT_C _> Z軸移動(ペン) #define MP_XY 20 //モーターの強さは20に固定 #define MP_Z 10 //ただしペンの昇降は弱めにする sub X_ML(int time) //X軸左方向に走査 { OnRev(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub X_MR(int time) //X軸右方向に走査 { OnFwd(OUT_B,MP_XY); Wait(time); Off(OUT_ABC);Wait(1000); } sub Y_DWN() //Y軸方向に1単位進む { OnFwd(OUT_A,MP_XY);Wait(200); Off(OUT_ABC);Wait(1000); } sub Z_UP() //ペンをあげる { RotateMotor(OUT_C,MP_Z,-20); Off(OUT_ABC);Wait(1000); } sub Z_DWN() //ペンをおろす { RotateMotor(OUT_C,MP_Z,20); Off(OUT_ABC);Wait(1000); } task main() { Z_UP(); //1行目 X_ML(2050); Y_DWN(); //2行目 X_MR(150); Z_DWN();X_MR(1800);Z_UP(); X_MR(100); Y_DWN(); //3行目 X_ML(150); Z_DWN();X_ML(1800);Z_UP(); X_ML(100); Y_DWN(); //4行目 X_MR(50); Z_DWN();X_MR(200);Z_UP(); X_MR(1400); Z_DWN();X_MR(200);Z_UP(); X_MR(100); Y_DWN(); //5行目 X_ML(1350); Z_DWN();X_ML(200);Z_UP(); X_ML(500); Y_DWN(); //6行目 X_MR(450); Z_DWN();X_MR(1100);Z_UP(); X_MR(400); Y_DWN(); //7行目 X_ML(450); Z_DWN();X_ML(1100);Z_UP(); X_ML(400); Y_DWN(); //8行目 X_MR(450); Z_DWN();X_MR(200);Z_UP(); X_MR(1300); Y_DWN(); //9行目 X_ML(1350); Z_DWN();X_ML(200);Z_UP(); X_ML(500); Y_DWN(); //10行目 X_MR(450); Z_DWN();X_MR(200);Z_UP(); X_MR(1300); Y_DWN(); //11行目 X_ML(450); Z_DWN();X_ML(1100);Z_UP(); X_ML(400); Y_DWN(); //12行目 X_MR(450); Z_DWN();X_MR(1100);Z_UP(); X_MR(400); Y_DWN(); //13行目 X_ML(450); Z_DWN();X_ML(200);Z_UP(); X_ML(700); Z_DWN();X_ML(200);Z_UP(); X_ML(500); Y_DWN(); //14行目 X_MR(1350); Z_DWN();X_MR(200);Z_UP(); X_MR(400); Y_DWN(); //15行目 X_ML(250); Z_DWN(),X_ML(1600);Z_UP(); X_ML(200); Y_DWN(); //16行目 X_MR(150); Z_DWN(),X_MR(1600);Z_UP(); X_MR(200); Y_DWN(); //17行目 X_ML(450); Z_DWN(),X_ML(200);Z_UP(); X_ML(1400); Y_DWN(); //18行目 X_MR(750); Z_DWN(),X_MR(800);Z_UP(); X_MR(400); Y_DWN(); //19行目 X_ML(450); Z_DWN(),X_ML(800);Z_UP(); X_ML(800); Y_DWN(); //20行目 X_MR(1950); }