目次
#contents
* 書道ロボット [#kc6aef55]
「各自の出身地または氏名の中の一文字(7画以上)をA4用紙の中に書く」ロボットを製作した。

完成した字

(左:練習時、紙を固定したとき) (右:発表時、紙を軽く押さえたとき)

&ref(2015b/Member/rmsun/Mission1/IMG_1057.JPG,270*360,高(固定));
&ref(2015b/Member/rmsun/Mission1/IMG_1171.JPG,270*360,高(本番));
* ロボット本体 [#cec79a4d]
初期ロボットの後ろにペンをつけた構造。ペンは輪ゴムで固定してある。

モータAはペンを持つアームに、モータBは右のタイヤに、モータCは左のタイヤに取り付けた。

ペンの位置は、モータBCの中間と一直線上にある。これが後々役に立った。
#ref(2015b/Member/rmsun/Mission1/IMG_1060.JPG,360*270,書道ロボット正面)
*プログラム [#m32e30bc]
サーボモータと、回転数を計算する関数を使って、「移動距離」や「方向変換する角度」を引数にとるサブルーチンを作った。それらを用いてロボットを移動させるため、モータを回す秒数は考えなくて良くなった。モータの回転角度を指定して動かすので、電池の残量による動きの変化も抑えられた(ある程度)
**メイン関数以外のプログラム [#wb572dc0]
***マクロの定義 [#ca3c85e6]
方向変換時のスピードは、タイヤの摩擦を減らすことばかり考えた結果、100になった(せざるを得なかった)。このせいで紙をおさえなければ字が書けなくなってしまった。
 #define SPEED 30       //前転後転時のスピード
 #define WRITE_SPEED 20 //字を書くときのスピード
 #define TURN_SPEED 100 //方向変換時のスピード
 #define PUT_ANGLE 91   //ペンを上げ下げする時、モータが回転する角度
 
ペンの上げ下げを行うマクロ「down」と「up」。ペンを二段階に分けて下ろす。一段階目(down1)で「残り20度で接地する」角度まで素早く下ろし、二段階目(down2)で「残りの20度」ゆっくり下ろす。上げるとき(up)は、素早く、一気に一段階で。
 #define down1() RotateMotor(OUT_A,-30,PUT_ANGLE-20) 
 //ペンを素早く下げる(地面にはつけない)
 
 #define down2() RotateMotor(OUT_A,-5,20)
 //ペンをゆっくり地面につける
 
 #define up() RotateMotor(OUT_A,70,PUT_ANGLE); 
 //ペンを上げる
***文字の倍率を設定するための変数「magni」 [#y2c1419e]
「magni×移動距離」移動する、とプログラムすることで、好きな大きさで文字を書けるようにする。

移動にはサブルーチンを使っているため、magniをあちこちにつける必要は無く、3つのサブルーチンにつけるだけで済んだ。


 float magni=1.0;          //倍率を設定  例:magni=2.0 → 文字の大きさが2.0倍
&ref(2015b/Member/rmsun/Mission1/IMG_1056.JPG,270*360,文字をmagni倍);
←文字の倍率を0.5→1.0→1.5→2.0→2.5にした時の大きさの比較
***タイヤの回転数を計算する関数「GetAngle_d」「GetAngle_s」 [#rbfc594a]
タイヤの回転数を計算するために使う変数
 float pen_to_center=17.0; //回転の中心(左右のタイヤの中間)からペンまでの距離
 float diameter=5.45;	     //タイヤの直径(cm)
 float pi=3.1415;          //円周率
 float tire_ang;	     //タイヤの回転する角度(返り値用)


「GetAngle_d」... 進みたい距離(cm)から、タイヤの回転数を計算する関数(http://yakushi.shinshu-u.ac.jp/robotics-jr/robotics-jr-2013.pdfの31ページのプログラムを参考にしました)
 float GetAngle_d(float distance) {
         //進みたい距離(cm)「distance」から、タイヤの回転数を計算する関数
 
 	tire_ang=(distance)/(diameter*pi)*360.0;  //角度を計算する	
 	return tire_ang;	                     //角度を返す(float型)
 }

「GetAngle_s」... 方向変換したい角度から、タイヤの回転数を計算する関数(http://yakushi.shinshu-u.ac.jp/robotics-jr/robotics-jr-2013.pdfの31ページにある練習問題62を参考にしました)
 float GetAngle_s(float spin_ang) {
 	//方向変換したい角度「spin_ang」から、タイヤの回転数を計算する関数
 
 	const float spin_dia=11.4;
 	//ロボットがその場で1周回転した時に、タイヤが地面に描く円の直径spin_dia(cm)
 	//spin_dia...左右のタイヤの間隔
 
  	tire_ang=(spin_dia*spin_ang)/(diameter);
 	//方向変換したい角度から、タイヤの回転する角度を計算する
         //約分前の式は tire_ang=(spin_dia*pi*(spin_ang/360.0)*360.0)/(diameter*pi)
 				                   
 	return tire_ang;	//角度を返す(float型)
 } 
***ロボットを前後に動かすサブルーチン「forward」「reverse」 [#k7fa5963]
・サブルーチン「forward」...指定した距離(cm)だけ前転する
 sub forward(float fwd){
 	RotateMotorEx(OUT_BC,SPEED,GetAngle_d(fwd)*magni,0,true,true);
 	//fwd(cm)*magni(倍率)前転  
 }
・サブルーチン「reverse」...指定した距離(cm)だけ後転する
 sub reverse(float rev) {
 	RotateMotorEx(OUT_BC,-SPEED,GetAngle_d(rev)*magni,0,true,true);
 	//rev(cm)*magni(倍率)後転
 }
GetAngle_dを用いることで、サブルーチン「forward」「reverse」は「前転後転する距離(cm)」をそのまま引数にとることができる。

使用例:3.0cm進みたいとき → forward(3.0)

5.0cm下がりたいとき → reverse(5.0)
***ロボットを回転させるサブルーチン「turn」 [#q2d85e44]
・サブルーチン「turn」...指定した角度ロボットが回転する
 sub turn(float tn,int pl_mi) {	
 	//tn(度)方向変換
引数pl_miは、ロボットが右回転するか左回転するかを決める。
 	/*pl_mi=1 → 右まわりに回転
 	  pl_mi=-1 → 左まわりに回転*/
左右のタイヤを逆向きに回すことで、その場で回転する。
 	RotateMotorEx(OUT_BC,TURN_SPEED,GetAngle_s(tn),100*pl_mi,true,true);
 	//左右のタイヤの逆向きに回すことで方向変換
 	Off(OUT_BC);
 	Wait(100);
 }
GetAngle_sを用いることで、サブルーチン「turn」は「回転する角度」をそのまま引数にとることができる。

使用例:90度右回転させたいとき → turn(90,1);


***ペンの位置を変えずに方向変換するサブルーチン「spin_re」 [#d4c2619e]
サブルーチン「spin_re」... ロボットの向きを変えるが、ペンの位置は変えない

引数pl_miは、ロボットが右回転するか左回転するかを決める。

引数spin_angは、方向変換したい角度を決める。
 sub spin_re(float spin_ang,int pl_mi) { 
 	//「ペンを中心に、spin_ang度回転する」(ときと同じ結果になる)関数
 	/*右に回転するとき...pl_mi=1
 	  左に回転するとき...pl_mi=-1*/	

「ロボットが回転する時に生じる誤差」を修正するための「gosa1」「gosa2」
 	float gosa1=0;	//右に回転するときの誤差「gosa1」
 	float gosa2=0;	//左に回転するときの誤差「gosa2」
 	if (pl_mi>0) {	//pl_mi>0(右への方向変換のとき)
 		gosa1=0.5;	//右へ回転するときの誤差を修正する
 	}
 	else {			//左への方向変換のとき
 		gosa2=0.5;	//左へ回転するときの誤差を修正する
 	}

実際に方向変換する動作
        RotateMotorEx(OUT_BC,-50,GetAngle_d(pen_to_center-gosa1),0,true,true);	
  	//pen_to_center-gosa1(cm)後転(gosaは回転の時のズレを調節するため)
  	turn(spin_ang,pl_mi);	//spin_ang(度)回転
 	RotateMotorEx(OUT_BC,20,GetAngle_d(pen_to_center-gosa2-gosa1),0,true,true);
 	//pen_to_center-gosa2-gosa1(cm)前転
 }
・方向変換のとき、ロボットはその場で回転するので、左右のタイヤの間に回転の中心がある。

・回転の中心では、いくら回転しても位置が変わらない。

→ペンを動かさずにロボットの向きを変えたいならば

� 峅鹽召涼羶�(今回は左右のタイヤの中間)」を「ペンの位置」まで移動させる

�▲蹈椒奪箸鮖慊蠅粒囘找鹽召気擦�

��ペンを、�,痢屮撻鵑琉銘屐廚泙念榮阿気擦�    

とすればよい

� 銑�を行うと、「ペンを中心に指定の角度回転させた」ときと同じ結果が得られる。

使用例:ペンを中心に右に90度方向変換  →  spin_re(90,1);

「spin_re」の説明は、少し分かりづらいと思われる。なので「方向変換の動作を詳しく」の項目で、写真とともに説明する。
***字を書くときに使うサブルーチン「write」 [#x81c3f00]
サブルーチン「write」... ペンが地面についた状態で、ロボットを前進させることで字を書く

ペンを二段階に分けて下ろすのが特徴。一段階目(down1)では「あと20度で接地する角度」まで素早く下ろし、そこで0.1秒停止する。二段階目(down2)では、「残りの20度」だけゆっくりペンを下ろす。ペンが接地するときに発生する力は、二段階目の「20度分ゆっくり下ろした」ときの力だけなので、ペンは安定した接地が可能。

ペン接地後、指定された距離だけ進む(このとき字を書く)。その後、ペンを上げる。このとき、ペンを素早く、一気に(一段階で)上げることで、無駄なインクが紙に付くのを防ぐ。
 sub write(float kaku) //ペンで線を引く
 {
 	down1();	//ペンを素早く下げる(地面にはつけない)
 	Wait(100);
 	down2();	//ペンをゆっくり地面につける
 	Wait(10);
 	RotateMotorEx(OUT_BC,WRITE_SPEED,GetAngle_d(kaku)*magni,0,true,true);
 			//WRITE_SPEEDの速さでkaku(cm)進む → kaku(cm)の線を書く
 	up();		//ペンを上げる
 }
使用例:6.0cmの線を引きたいとき → write(6.0);
***「口」を書くためのサブルーチン「kuti」 [#k0ad77bc]
「高」では「口」を2回書くので、一応サブルーチンにした。

 sub kuti(float tate, float yoko) //口の字を書く
 	//口の縦の長さ(tate)と、横の長さ(yoko)を引数にとる
 {
 	write(tate);		//tate(cm)書く
 	spin_re(90.0,-1);    //ペンを中心に90度左回転
 	write(yoko);	       //yoko(cm)書く
 	spin_re(90.0,-1);	//ペンを中心に90度左回転
 	write(tate);         // ↓ここから上の繰り返し(repeatを使ったら誤差が出た)
 	spin_re(90.0,-1);
 	write(yoko);
 	spin_re(90.0,-1);
 }
使用例:縦の長さ5.0cm、横の長さ10.0cmの「口」を書きたいとき  →  kuti(5.0,10.0);
**方向変換の動作を詳しく [#f0fe2ef5]
サブルーチン「spin_re」を使ったとき、ロボットが実際にどのような動作をするのかの説明

今回は「90度左回転の方向変換をする」ときを例に説明する。

「spin_re」は、ロボットを回転させつつ、ペンの位置を最初と同じにするのが目的

(左写真:回転の中心の位置、モータBとモータCの間にある)

(右写真:ペンの初期位置)

&ref(2015b/Member/rmsun/Mission1/IMG_1078.JPG,360*480,回転の中心);
&ref(2015b/Member/rmsun/Mission1/IMG_1082.JPG,360*480,ペンの初期位置);
***� 峅鹽召涼羶粥廚函屮撻鵑琉銘屐廚竜�離分だけ後転する [#v6e2c55b]
回転の中心を、最初ペンのあった位置まで持っていく。

このとき実行されたプログラム↓↓
 	RotateMotorEx(OUT_BC,-50,GetAngle_d(pen_to_center-gosa1),0,true,true);	
 	//pen_to_center-gosa1(cm)後転(gosaは回転の時のズレを修正するため)

#ref(2015b/Member/rmsun/Mission1/IMG_1085.JPG,360*480,後退)
***�▲蹈椒奪箸鮖慊蠅粒囘找鹽召気擦� [#b0a71654]
実際に方向変換したい角度分、回転させる。

このとき実行されたプログラム↓↓
 	turn(spin_ang,pl_mi);	//spin_ang(度)回転
&ref(2015b/Member/rmsun/Mission1/IMG_1086.JPG,360*480,後退時のペンの位置);
回転→→
&ref(2015b/Member/rmsun/Mission1/IMG_1089.JPG,360*480,回転);
***��「回転の中心」と「ペンの位置」の距離分だけ前転する [#a584e47f]
ペンを元の位置に戻すため、�,撚爾�った分の距離を進む。

このときのペンの位置は、最初のペンの位置と同じ。

このとき実行されたプログラム↓↓
 	RotateMotorEx(OUT_BC,20,GetAngle_d(pen_to_center-gosa2-gosa1),0,true,true);
 	//pen_to_center-gosa2-gosa1(cm)前転(gosaは回転の時のズレを修正するため)
***�し覯� [#h5e0c8c4]
「ペンを中心にして、指定の角度回転させたとき」と同じ結果が得られる。
#ref(2015b/Member/rmsun/Mission1/IMG_1095.JPG,270*360,結果)
**メイン関数(実際に字を書くプログラム) [#m0a26da1]
 task main() 
 {
 	up();             //ペンを地面につけたままスタート
 	write(1.0);       //1画目(「亠」の「|」の部分)

 			     //2画目の位置(「亠」の「―」の部分)まで移動
 	spin_re(90.0,-1);	 	//ペンを中心に90度左回転
 	reverse(2.0);	   	 	//2.0(cm)後転
 	write(4.0);	    //2画目(「亠」の「―」の部分)

 			     //3画目(口の部分)まで移動
 	reverse(3.0);    		 //3.0(cm)後転
 	spin_re(90.0,1); 		 //ペンを中心に90度右回転
 	forward(0.5);     		 //0.5(cm)前転
 	kuti(1.0, 2.25);   //「口」の字を書く(4,5,6画目)

 			      //7画目(「高」の左下の「|」の部分)まで移動
 	forward(1.5);     		//1.5(cm)前転
 	spin_re(90.0,-1); 		//ペンを中心に90度左回転
 	reverse(1.25);    		//1.25(cm)後転
 	spin_re(90.0,1);  	 	//ペンを中心に90度右回転
 	write(3.0);        //7画目(「高」の左下の「|」の部分)

 		       	      //8画目(真ん中を通る「一」の部分)まで移動
 	reverse(3.0);	    		//3.0(cm)後転
 	spin_re(90.0,-1);		//ペンを中心に90度左回転
 	write(4.75);       //8画目(真ん中を通る「一」の部分)

 		              //9画目(右下の「|」の部分)まで移動
 	spin_re(90.0,1); 		//ペンを中心に90度右回転
 	write(3.0);        //9画目(右下の「|」の部分)

 	spin_re(90.0,1);   //ロボットの向きを7画目の「ー」と平行にする

 			      //最後の「口」の部分まで移動
 	forward(3.5);    		//3.5(cm)前転
 	spin_re(90.0,-1);		//ペンを中心に90度左回転
 	reverse(2.5);   		//2.5(cm)後転
  	kuti(1.0, 2.25);   //最後の「口」を書く
		
}
*使うと思っていたサブルーチン「harai」 [#i93f8fa2]
今回書いた字の「高」には、曲線がなかった。そのため、曲線の移動をする「harai」は出番が無かった。なので、今回のプログラムには含まれていないが、これがあった方が書ける字の幅は広がる。

「harai」... 左右のタイヤ(モータ)をどれだけ同期させるかで、ロボットの曲がり具合を決めて線を引く(字を書く)
 sub harai(float ha, int angl) {
 				/*angl>0...右斜めへの「はらい」
 				  angl<0...左斜めへの「はらい」*/
 	down1();	        //ペンを素早く下げる(地面にはつけない)
 	Wait(100);
 	down2();	        //ペンをゆっくり地面につける
 	Wait(10); 
 	RotateMotorEx(OUT_BC,SPEED,GetAngle_d(ha),angl,true,true); 
 				//anglは「左右のタイヤの同期」の調整のため
 				//ha(cm)前転
 	up();		//ペンを上げる
        RotateMotorEx(OUT_BC,-SPEED,GetAngle_d(ha),angl,true,true); 
                                //「字を書いた距離」分戻って、ロボットの向きを元に戻す
 }

*工夫点 [#k7ba3fac]
・サーボモータや、回転角度を計算する関数を使ったことで、「xセンチ進む」「yセンチ下がる」「z度向きを変える」のように長さや角度を指定してロボットを動かせるようになった。サブルーチンの引数に進む距離や回転する角度が書いてあるので、プログラムを見れば、ロボットがどんな動きをしているのかがわかる。

・文字の倍率を設定できるようにした。そのおかげで、文字が紙からはみ出る心配が消えた。

*反省点 [#y108e50d]
・紙をおさえないと字が書けなくなってしまった。タイヤの摩擦を減らすためとはいえ、回転スピード100%は流石にやり過ぎだった。

・プログラムがかなり長くなってしまった。必要以上にサブルーチンを使っているので、サブルーチン同士をまとめたりして、数を減らすべきだった(特に、「turn」と「spin_re」は、まとめておくべきだった)

・ロボット製作では全く貢献できなかったので、次回からは貢献できるように頑張りたい。

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS