#author("2019-11-18T14:04:18+09:00","yuu","yuu")
#author("2019-11-18T14:05:22+09:00","yuu","yuu")
目次
#contents

* 課題1:お絵描きロボット [#we1fcdeb]
チームメイトの似顔絵を描くロボットを製作しなさい。
* ロボットについて [#z6259212]
xyプロッタ型のロボットを作りました。ロボットの前面と後面です。 (下写真) 2つのモータを使いx方向、y方向にペンを動かします。NXT本体とモータをコードでゆとりをもってつなげられるようNXT本体は中央に取り付け、また重さに耐えられるように下に車輪を付けました。

&ref(IMG_5646comp.jpg,nolink,90%, );
&ref(IMG_5645comp.jpg,nolink,90%, );

** 台車部分 [#p61cd538]
台車部分はy方向に動きます。台車部分には上側部分を乗せるためのレールが付けられていて、レールに沿って上側部分が移動します。

&ref(IMG_5644comp.jpg,nolink,60%, );

** 上側部分 [#vf84d15e]
右下写真の右モータはy方向に動かすためのタイヤを回転させるためのもの、左モータはペンを上下させるためのものです。前輪と後輪を歯車を使い同期させ空回りしないようしたのですが、それでも滑ってしまうこともあります。タイヤの下には、レールの間にはまるように出っ張った部分があります。またペンを持つところはペンをはさめるように薄いプレートで厚みを調整し、ペンの上側はブロックで固定しています。

&ref(IMG_5643comp.jpg,nolink,90%, );
&ref(IMG_5660comp.jpg,nolink,110%, );

* プログラムについて [#o2332a14]
** マクロおよび定数 [#j2cc36a5]

 #define PenUp RotateMotor(OUT_A, 50, 16)          //ペンを上げる
 #define PenDown RotateMotor(OUT_A, -50, 16)       //ペンを下げる
 const float RADIAN = 3.14/180;

PenUpはペンを上げる、PenDownはペンを下げるためのものです。また、RADIANは角度の単位を(°)から(rad)に変えるためのものです。(2π rad/360 °)

** 曲線(楕円弧)をかくサブルーチン [#h992b2da]

 void WriteEllipse(float Xradius, float Yradius, float angle1, float angle2)
 {
     float theta1 = angle1*RADIAN;          //単位をラジアンに変更
     float theta2 = angle2*RADIAN;          //単位をラジアンに変更
     const float d_theta = 0.01;
     for (float theta = theta1 ; theta < theta2 ; theta = theta+d_theta){
         int Xa = Xradius * cos(theta);
         int Xb = Xradius * cos(theta+d_theta);
         int Ya = Yradius * sin(theta);
         int Yb = Yradius * sin(theta+d_theta);
         RotateMotor(OUT_B, 60, Xb-Xa);
         RotateMotor(OUT_C, 20, Yb-Ya);
     }
 }


図のようにx半径rx、y半径ryの楕円を書く時、微小部分AからBまで動かすことを考えます。x方向にrxcos(θ+dθ)-rxcosθ、y方向にrysin(θ+dθ)-rysinθ動かせばよいことがわかります。2つのモータを一度に動かすのは難しいので、x方向に動かしてからy方向に動かしています。これをθ=0°からθ=360°まで繰り返せば楕円になり、一部分のみにすると弧がかけます。モータの回転角度は整数のため途中で移動距離をintに代入し整数に直します。

プログラムでは、~
  X_radias、Y_radiasはそれぞれx半径[rx]、y半径 [ry]~
  X_radias、Y_radiasはそれぞれx半径 [rx]、y半径 [ry]~
  angle1とtheta1は弧の始めの角度~
  angle2とtheta2は弧の終わりの角度(angleの単位は度、thetaの単位はラジアン)~
  d_thetaは角度の変化量 [dθ]、thetaは角度 [θ]~
としています。

この方法で書くため、なめらかな曲線はかけず、ぎざぎざになってしまいます。

&ref(IMG_E5667comp.jpg,nolink,50%, );


** ペンを移動させるサブルーチン [#x21ee2a6]
 void MovePen(int X, int Y)
 {
     RotateMotor(OUT_B, 20, X);
     RotateMotor(OUT_C, 20, Y);
 }
x方向にXだけ動かしたのち、y方向にYだけ動かします。

** ペン上下+線をかくサブルーチン [#oef1466a]

 void MDEU(int X, int Y, float Xradius, float Yradius, float angle1, float angle2)
 { 
     MovePen(X, Y);
     PenDown;
     WriteEllipse(Xradius, Yradius, angle1, angle2);
     PenUp;
 } 

 void MDSU(int X, int Y)
 {
     MovePen(X, Y+10);
     MovePen(0, -10);
     PenDown;
     MovePen(25, 7);
     MovePen(-25, -7);
     PenUp;
 }

かき始めるところにペン持っていき、ペンを下ろし、線をかき、ペンを上げるプログラムです。~
MDEU(''M''ovePen Pen''D''own Write''E''llipse Pen''U''p)は曲線、MDSU(''M''ovePen Pen''D''own Write''S''quare Pen''U''p)は四角をかくものです。
** メインプログラム [#a3e6132e]
 task main()
 {
     Wait(1000);                            //すぐに動かないように少し待つ
     MDEU(-210, 212, 78, 100, 100, 240);    //A
     MDEU(0, 36, 144, 250, 240, 302);       //B
     MDEU(0, -34, 78, 100, 300, 440);       //C
     MDEU(0, -54, 24, 150, 40, 80);         //D
     MDEU(-20, 0, 12, 64, 90, 180);         //E
     MDEU(-18, 57, 40, 63, 90, 180);        //F
     MDEU(115, -5, 70, 20, 90, 160);        //G
     MDEU(-20, 0, 70, 20, 20, 90);          //H
     MDSU(28, -20);                         //I
     MDSU(58, 0);                           //J
     MovePen(-20, -15);
     PenDown;
     MovePen(0, -10);
     MovePen(0, 10);                        //K
     PenUp;
     MovePen(-50, 0);
     MDEU(10, -20, 50, 30, 200, 340);       //L
 }
サブルーチンを利用してメインプログラムを書いています。左は書き順(プログラムのコメント部分に対応)、右は完成した似顔絵です。

&ref(IMG_56542comp.jpg,nolink,60%, );
&ref(IMG_5654comp.jpg,nolink,60%, );

* 感想 [#d228ca2d]
うまくかけたほうだと思いますが、ペンが高すぎると線をかけず、低すぎるとタイヤが空回りしてx方向にうまく動かないという問題がありました。そのためx方向に動かすときは速度を落としたり、初期状態を調節していましたが、この問題を解決するためにはペンを上下させる機構にペンを下げようとしても一定以上は下がらないようにする仕組みが必要だと感じました。

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