足球機器人 – 射門 Soccer Robot - Shoot
球門以障礙物作為守門員,所以是一個比較簡單的守門員,一臺機器人使用超音波感應器掃描,找到沒有守門員的地方後射門。
機器人機結構
機器人必須能夠掃描到沒有守門員的地方,所以裝超音波的地方必須可以旋轉或移動,射門的時候必須是與超音波同方向,所以機器人可以是一台車子再加上一個射門的裝置或直接用機器人撞球。
機器人程式
機器人的動作必須先向一個方向掃描,找到沒有守門員的地方,然後射門。
主程式
每一個機器人的程式內都必須有一個主工作(程式),這是啟動後第一個執行的程式,就像樹的主幹一樣主導整個程式。機器人可以多工處理(多個程式幾乎同時執行),「task」表示這是一個工作,「main()」表示為主工作,其他工作自定名稱,例如「second()」的second是這個工作的名稱,如同樹的其中一個分枝,大括弧{}內是這個工作的內容。
task main()
{
程式碼
}
等待Wait
程式會卡在「等待」這個指令,直到設定的毫秒數過去
Wait(毫秒);
範例:十秒後程式結束
task main()
{
Wait(10000);
}
資料型態 Data Type
機器人可以處理的資料型態,也就是資料的種類,一般有數值、字元等,數值又有可以放不同大小資料的型態。
型態
|
內容
|
關聯性
|
int
|
整數
|
-32768~32767
|
long
|
長整數
|
-2147483648~2147483647
|
float
|
浮點數(有小數點的數)
|
32bits
|
bool
|
布林值(邏輯值)
|
是/否
|
char
|
字元
|
8bits
|
string
|
字串
|
一串字元
|
範例:十秒後程式結束
task main()
{
Wait(10000);
}
運算
運算子(符號)
|
內容
|
關聯性
|
限制
|
範例
|
絕對值
|
n/a
|
abs(x)
| ||
回傳正負號
|
n/a
|
sign(x)
| ||
++, --
|
後置增/減
|
左
|
只有變數
|
x++
|
++, --
|
前置增/減
|
右
|
只有變數
|
++x
|
-
|
負
|
右
|
-x
| |
~
|
位元 否
|
右
|
~123
| |
!
|
邏輯 否
|
右
|
!x
| |
*, /, %
|
乘、除、係數
|
左
|
x * y
| |
+, -
|
加、減
|
左
|
x + y
| |
<<, >>
|
位元移動左與右
|
左
|
x << 4
| |
<, >, <=, >=
|
關係運算子
|
左
|
x < y
| |
==, !=
|
等於、不等於(比較)
|
左
|
x == 1
| |
&
|
位元 且
|
左
|
x & y
| |
^
|
位元 互斥或
|
左
|
x ^ y
| |
|
|
位元 或
|
左
|
x | y
| |
&&
|
邏輯 且
|
左
|
x && y
| |
||
|
邏輯 或
|
左
|
x || y
| |
?:
|
三元值
|
右
|
x==1 ? y : z
|
條件迴圈 while
當「條件」成立時重覆執行大括弧{}內的程式碼,例如條件是「還沒走十公分」而程式碼是「走」,機器人就會一直走,直到走了十公分,也就是「還沒走十公分」已經不對了。
while(條件)
{
程式碼
}
範例:走十秒後結束程式
task main()
{
Int a;
a = 0;
while(a < 10)
{
Wait(1000);
a = a + 1;
}
}
感應器
運用感應器時必須先設定哪個感應器接口連什麼感應器,這裡我們使用超音波感應器及角度感應器,而這次使用馬達內建的角度感應器,不用另外設定,可以直接使用讀取指令。
設定超音波感應器
設定一、二、三或四號為超音波感應器(Ultrasonic Sensor)
SetSensorUltrasonic(S1/S2/S3/S4);
範例:設定一號為超音為感應器(S1/S2/S3/S4=一號/二號/三號/四號)
因為S1是一號,所以在括弧中打上S1
SetSensrUltrasonic(S1);
其他還有觸碰感應器、光學感應器及聲音感應器等…
SetSensorTouch("port");/SetSensor("port", SENSOR_TOUCH);
SetSensorLight("port");
SetSensorSound("port");
讀取超音波感應器的值
讀取「感應器」一、二、三或四號的值
Sensor(S1/S2/S3/S4)
讀取「超音波感應器」一、二、三或四號的值
SensorUS(S1/S2/S3/S4)
範例:讀取在「一號」的超音波感應器的值
因為我們設定一號是超音波感應器,所以讀取一號的值就是讀超音波感應器的值。
SetSensrUltrasonic(S1);
SensorUS(S1);
讀取角度感應器的值
因為角度感應器的值從一啟動程式後就開始累計,不想使用累計值或為求保險就必須歸零。
ResetRotationCount(OUT_A/OUT_B/OUT_C);
讀取A、B或C的角度感應器值
MotorRotationCount(OUT_A/OUT_B/OUT_C);
範例:對 A 歸零後讀取 A 的值
若無錯誤,應是讀到「0」
ResetRotationCount(OUT_A);
MotorRotationCount(OUT_A);
感應器應用範例 - 擊棒球
利用超音波感應器偵測球是否進入擊球區,當超音波感應器讀而小於六公分時表示進入擊球區,此時馬達轉動超過一百八十度後停止。
task main()
{
SetSensrUltrasonic(S1);
while(SensorUS(S1) > 6)
{
}
OnFwd(OUT_A,100);
ResetRotationCount(OUT_A);
while(MotorRotationCount(OUT_A) < 180)
{
}
}
*前後值比較
要找到障礙物,就必須不斷的比較「之前」測到的超音波值及「之後」測到的沼音波值,如果前比後小,就表示超音波的距離值由大到小,也就是從球門轉到障礙物的那一瞬間,這時就找到了障礙物的左邊界(此處是由左向右),而後比前小就代表從障礙物轉到球門的那一瞬間,此時找到了障礙物的右邊界。
dist0代表「前」值,dist1代表「後」值,deg0記錄「左邊界」,deg1記錄「右邊界」。
//找障礙物
while(MotorRotationCount(OUT_A) < 180)//轉完這個角度後即是描完球門
{
//轉彎
OnFwd(OUT_A,40); //A前
OnRev(OUT_C,40); //C後
//取得距離
dist0 = SensorUS(S1); //取得第一個值
Wait(2); //等 2 毫秒
dist1 = SensorUS(S1); //第二個值
//取得障礙物開始及結束位置
if(dist1 < dist0) //後比前小的話,就是找到障礙物的開始
{
deg0 = MotorRotationCount(OUT_A);//記錄障礙物的開始
}
if(dist1 > dist0)
{
deg1 = MotorRotationCount(OUT_A);//記錄障礙物的結束
}
}
Off(OUT_AC); //停止
射門
這時轉(走)回起點,此時因為已記錄了有障礙物的位置範圍,我們只要在程式寫「當超音波還在障礙物的位置範圍內就不射門」就可以達到在空處射門。
while(MotorRotationCount(OUT_A) < deg1 && MotorRotationCount(OUT_A) > deg0)//如果在障礙物範圍內就繼續轉
{
//反轉彎
OnRev(OUT_A,40); //C前
OnFwd(OUT_C,40); //A後
}
NXC 足球機器人射門範例
task main()
{
SetSensorUltrasonic(S1); //設定 Port 1 是超音波感應器
int deg0,deg1,dist0,dist1; //找到障礙物的開始角度,找到障礙物的結束角度,前一個距離,後一個距離
ResetRotationCount(OUT_A);//Port A 角度感應器歸零
//找障礙物
while(MotorRotationCount(OUT_A) < 180)//轉完這個角度後即是描完球門
{
//轉彎
OnFwd(OUT_A,40); //A前
OnRev(OUT_C,40); //C後
//取得距離
dist0 = SensorUS(S1); //取得第一個值
Wait(2); //等 2 毫秒
dist1 = SensorUS(S1); //第二個值
//取得障礙物開始及結束位置
if(dist1 < dist0) //後比前小的話,就是找到障礙物的開始
{
deg0 = MotorRotationCount(OUT_A);//記錄障礙物的開始
}
if(dist1 > dist0)
{
deg1 = MotorRotationCount(OUT_A);//記錄障礙物的結束
}
}
Off(OUT_AC); //停止
while(MotorRotationCount(OUT_A) < deg1 && MotorRotationCount(OUT_A) > deg0)//如果在障礙物範圍內就繼續轉
{
//反轉彎
OnRev(OUT_A,40); //C前
OnFwd(OUT_C,40); //A後
}
Off(OUT_AC); //停止
//射門
OnFwd(OUT_B,100); //射門機構轉
Wait(200);
Off(OUT_B);
}
|