買書捐殘盟

2011年12月16日 星期五

走迷宮找最短路徑至終點

前一篇的文章[篩選簡化路徑的Line Follower]談到:
『電腦鼠從起點到終點所經過的所有途徑,記錄所經過的座標之後,自動篩選及簡化路徑,進而尋找最短途逕從起點直至終點。』

本篇據此,經由nxt分析 最短途徑後,操作者將nxt電腦鼠再次放到起點處,預備讓電腦鼠可以依據最短途逕直至終點。不過程式需要增加一段,也是本篇的精華所在。



#define blue_tap1 3
#define blue_tap2 4
#define red_tap 9
#define factor 2


#define LMotor OUT_A
#define RMotor OUT_C
#define Motors OUT_AC

#define LightSensor1Port IN_1
#define LightSensor2Port IN_2
#define ColorSensorPort IN_4

int x[10];
int y[10];
int x_fix[10];
int y_fix[10];

int i,j,k=0;
int D;
int colornum;
int black,white,light1_value,light2_value;
int Lpower=0,Rpower=0;

long tick;
string code;


void WaitSEC3()
{
tick=CurrentTick();
while(CurrentTick()<(tick+3000))
{
PlayTone(880,50);
Wait(950);
}
Wait(1000);
}

void InitialLightSensor()
{
while(!ButtonPressed(BTNCENTER,false))
{
TextOut(0,LCD_LINE3,"Light at black..");
black=Sensor(LightSensor1Port);
}
ClearScreen();
Wait(1000);
while(!ButtonPressed(BTNCENTER,false))
{
TextOut(0,LCD_LINE3,"Light at white..");
white=Sensor(LightSensor1Port);
}
ClearScreen();
WaitSEC3();
}


void LoopAdjust()
{
for(i=0; i<ArrayLen(x); i++)
{
x_fix[k]=x[i];
y_fix[k]=y[i];
for(j=i; j<ArrayLen(x); j++)
{
if((x[j]==x[i])&&(y[j]==y[i]))
{
x_fix[i]=x[j];
y_fix[i]=y[j];
i=j;
}
}
k++;
}
}

void BotWalking()
{
light1_value=Sensor(LightSensor1Port);
Rpower=(light1_value-black)*factor;
Lpower=(white-light1_value)*factor;
OnFwd(LMotor,Lpower);
OnFwd(RMotor,Rpower);
}

void LightSensorTouchLine() //讓bot的兩個light往前推進一小步,直至壓線
{
tick=CurrentTick();
while(CurrentTick()<tick+800)
{
OnFwdSync(Motors,25,0);
}
}

void TireTouchLine(int n) //讓bot往前移動,直至前輪壓線
{
tick=CurrentTick();
while(CurrentTick()<tick+n)
{
OnFwd(LMotor,25);
OnFwd(RMotor,25);
}
}

void BotRightTurn() //讓bot右轉
{
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(500);
tick=CurrentTick();
while(CurrentTick()<tick+2050)
{
OnFwd(LMotor,25);
OnFwd(RMotor,-25);
}
}

void BotLeftTurn() //讓bot左轉
{
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(1400);
tick=CurrentTick();
while(CurrentTick()<tick+1600)
{
OnFwd(LMotor,-25);
OnFwd(RMotor,25);
}
}




void ShortLoopGo() //Result: (0,1)-->(-1,1)-->(-1,0)-->(-2,0)
{
string prev_axis="Y" ;
i=1;

while(i<=k)
{
colornum=SensorHTColorNum(ColorSensorPort);
//NumOut(0,LCD_LINE1,D);
if((colornum!=blue_tap1)||(colornum!=blue_tap2))
{
if(colornum==red_tap)
{
StopAllTasks();
}
BotWalking();
}

if((colornum==blue_tap1)||(colornum==blue_tap2))
{
Off(Motors);
Wait(SEC_1);
if((x_fix[i]-x_fix[i-1])!=0)
{
if(((x_fix[i]-x_fix[i-1])>0)&&(prev_axis=="Y"))
{
D=1;
}
if(((x_fix[i]-x_fix[i-1])>0)&&(prev_axis=="-Y"))
{
D=2;
}

if(((x_fix[i]-x_fix[i-1])<0)&&(prev_axis=="Y"))
{
D=3;
}
if(((x_fix[i]-x_fix[i-1])<0)&&(prev_axis=="-Y"))
{
D=4;
}

if(((x_fix[i]-x_fix[i-1])>0)&&(prev_axis=="-X"))
{
D=5;
}
if(((x_fix[i]-x_fix[i-1])<0)&&(prev_axis=="X"))
{
D=6;
}
}

if((y_fix[i]-y_fix[i-1])!=0)
{
if(((y_fix[i]-y_fix[i-1])>0)&&(prev_axis=="X"))
{
D=7;
}
if(((y_fix[i]-y_fix[i-1])>0)&&(prev_axis=="-X"))
{
D=8;
}

if(((y_fix[i]-y_fix[i-1])<0)&&(prev_axis=="X"))
{
D=9;
}
if(((y_fix[i]-y_fix[i-1])<0)&&(prev_axis=="-X"))
{
D=10;
}

if(((y_fix[i]-y_fix[i-1])>0)&&(prev_axis=="Y"))
{
D=11;
}

if(((y_fix[i]-y_fix[i-1])<0)&&(prev_axis=="-Y"))
{
D=12;
}
}
NumOut(0,LCD_LINE1,D);
switch(D)
{
case 1:
BotRightTurn();
prev_axis="-X";
break;
case 2:
BotLeftTurn();
prev_axis="-X";
break;
case 3:
BotLeftTurn();
prev_axis="X";
break;
case 4:
BotRightTurn();
prev_axis="X";
break;
case 5:
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(500);
BotWalking();
prev_axis="-X";
break;
case 6:
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(1400);
BotWalking();
prev_axis="X";
break;
case 7:
BotRightTurn();
prev_axis="Y";
break;
case 8:
BotLeftTurn();
prev_axis="Y";
break;
case 9:
BotLeftTurn();
prev_axis="-Y";
break;
case 10:
BotRightTurn();
prev_axis="-Y";
break;
case 11:
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(500);
BotWalking();
prev_axis="Y";
break;
case 12:
LightSensorTouchLine();
Off(Motors);
Wait(SEC_1);
TireTouchLine(1400);
BotWalking();
prev_axis="-Y";
break;
}
i++;
}
}
Off(Motors);
}

task main()
{
SetSensorLowspeed(ColorSensorPort);
SetSensorLight(LightSensor1Port,true);
SetSensorLight(LightSensor2Port,true);
ArrayBuild(x,0,-1,-1,-1,-2,-2,-2,-1,-1,-2);
ArrayBuild(y,1, 1, 2, 3, 3, 2, 1, 1, 0, 0);
LoopAdjust();
InitialLightSensor();
while(!ButtonPressed(BTNCENTER,false));
ClearScreen();
ShortLoopGo();
}


●★●★●★●★●★●★●★●★●★●★
程式說明
1.LoopAdjust()函式-用以調整簡化最短路徑
2.BotWalking()函式-電腦鼠走黑線的副函式
3.LightSensorTouchLine()、TireTouchLine()-讓電腦鼠光感、輪胎在轉彎處修正其位置
4.BotRightTurn()-電腦鼠右轉副函式
5.BotLeftTurn()-電腦鼠左轉副函式
6.ShortLoopGo()-最短路徑副函式-本篇文章核心函式(Kernel Sub-Function)
(1)根據電腦鼠座標位置,於叉路轉彎,共有12個條件式
(2)依現行軌道,從起點處向上(或向前)Y為正,向下(或向後)Y為負;
(3)從起點處向左X為正,向右為負。
(4)前述2,3根據電腦鼠依現在座標、下一個駐點座標的二者地理位置座標,決定左右轉或前行,方向由使用者定義方向,於程式中設定之。

[執行結果]

video

2011年12月9日 星期五

篩選簡化路徑的Line Follower

續前一篇[具路徑規劃的Line Follower],當電腦鼠(nxt car)從起點走到終點後,根據前述的程式,我們是將所經過的路徑之x-y座標,存成檔案紀錄在nxt檔案夾中,可供我們事後觀測座標路徑正確與否。

其次,我們打算根據所走的x-y所有點,計算較短路徑。


(圖1 電腦鼠軌道)

程式的規劃,電腦鼠在路徑行走時,採右手法則(即遇到叉路時,以右轉優先,其次為直行,最後為左轉)。
從圖1,可以知道:
當電腦鼠走到(-1,1)時,以右手法則轉向右邊,途經(-1,2)、(-1,3)、(-2,3)、(-2,2)、(-2,1),最後又會到(-1,1)的點,之後右轉至(-1,0),再轉至終點(-2,0)。
這過程中,電腦鼠途經(-1,2)、(-1,3)、(-2,3)、(-2,2)、(-2,1)這些座標的點,對我們直觀的判斷,這些中途繞道的點,應該是多餘的。所以之後的程式,應該根據所有走過的座標,去篩選哪些是不必要再走的。

本篇文章,先設計一測試程式,將電腦鼠走過的座標,存成陣列,並逐步篩選以簡化路徑。

[程式]

(圖2 簡化路徑之程式)

圖2程式:
x[ ],y[ ]為原始數據,而x_fix[ ], y_fix[ ]為簡化後記錄的座標數據
程式從第一組元素(element)開始往後比對,i為參考組,j為比對組。
後面各組(j)若有與參考組(i)的座標雷同,則記錄該點至x_fix及y_fix。
(基本上,當電腦鼠會走回原來的地方,代表剛剛所經過的路徑必不是終點存在的區域,既然回到原來的地方,所以剛剛繞過的地方可捨去不用)


(圖3 電腦鼠走過的座標示意圖)

從圖3,籃框處中間的其他各點,為非必要的途徑,所以可捨去。
所以經過篩選後,最後的座標為:
(0,1)-->(-1,1)-->(-1,0)-->(-2,0)

2011年12月3日 星期六

具路徑紀錄之Line Follower

前些日子,採購了一顆具備辨色能力之hitechnic color sensor,並應用在Line Follower實務,在等距之處貼上色貼,刻劃橫坐標x以及縱座標y。


(圖1 硬體地圖配置)

一、硬體介紹
1.圖1每一個色貼間隔,盡可能讓bot有足夠空間可以迴轉。

2.起點設置在照片中車子停止的位置,硬體裝置使用三個感應器(light sensor x2、color sensor x1)。color sensor負責偵測前端是否為[色貼]標籤,做為後續判斷是否前進或左右轉彎的依據。

3.light sensor分置兩側,左邊light sensor(緊鄰color sensor),負責做沿線前進的功能;右邊的light sensor,負責判斷是否做轉彎的動作…

二、軟體程式

#define LMotor OUT_A
#define RMotor OUT_C
#define Motors OUT_AC

#define LightSensor1Port IN_1
#define LightSensor2Port IN_2
#define ColorSensorPort IN_4
#define factor 2

#define black_tap 0
#define blue_tap1 3
#define blue_tap2 4
#define red_tap 9


int colornum; //彩色感應器讀值
int black,white,light1_value,light2_value; //光感器讀值
int Lpower=0,Rpower=0;
long tick=0;

//紀錄車子移動方位

int x=0,y=0;
int D=1; //初始放在y軸上

byte fileHandle;
short bytesWritten;
string str_log;
string str_x;
string str_y;

void InitialLightSensor()
{
while(!ButtonPressed(BTNCENTER,false))
{
TextOut(0,LCD_LINE3,"Light at black..");
black=Sensor(LightSensor1Port);
}
ClearScreen();
Wait(1000);
while(!ButtonPressed(BTNCENTER,false))
{
TextOut(0,LCD_LINE3,"Light at white..");
white=Sensor(LightSensor1Port);
}
ClearScreen();
tick=CurrentTick();
while(CurrentTick()<(tick+3000))
{
PlayTone(880,50);
Wait(950);
}
Wait(1000);
}

void BotWalking()
{
light1_value=Sensor(LightSensor1Port);
Rpower=(light1_value-black)*factor;
Lpower=(white-light1_value)*factor;
OnFwd(LMotor,Lpower);
OnFwd(RMotor,Rpower);
}

void LightSensorTouchLine() //讓bot的兩個light往前推進一小步,直至壓線
{
tick=CurrentTick();
while(CurrentTick() {
OnFwdSync(Motors,25,0);
}
}

void TireTouchLine(int n) //讓bot往前移動,直至前輪壓線
{
tick=CurrentTick();
while(CurrentTick() {
OnFwd(LMotor,25);
OnFwd(RMotor,25);
}
}
void BotRightTurn() //讓bot右轉
{
tick=CurrentTick();
while(CurrentTick() {
OnFwd(LMotor,25);
OnFwd(RMotor,-25);
}
}

void BotLeftTurn() //讓bot左轉
{
tick=CurrentTick();
while(CurrentTick() {
OnFwd(LMotor,-25);
OnFwd(RMotor,25);
}
}


void BotTurning()
{
Off(Motors);
Wait(1000);
LightSensorTouchLine();
Off(Motors);
Wait(1000);

light1_value=Sensor(LightSensor1Port);
light2_value=Sensor(LightSensor2Port);
colornum=SensorHTColorNum(ColorSensorPort);

if(light1_value>light2_value) //可右轉
{
TireTouchLine(500);
BotRightTurn();
D=D+1;
if(D>4) D=1;
}
if(light1_value<=light2_value) //可左轉
{
if(colornum==black_tap) //彩色感應器偵測到前方有黑線,可前行
{
BotWalking();
D=D;
}
if(colornum!=black_tap)
{
TireTouchLine(1400);
BotLeftTurn();
D=D-1;
if(D<1) D=4;
}
}
}

void AxisRecord()
{
if(D==1)
{
y=y+1;
}

if(D==2)
{
x=x+1;
}

if(D==3)
{
y=y-1;
}

if(D==4)
{
x=x-1;
}
str_x=NumToStr(x);
str_y=NumToStr(y);
str_log=str_x+" "+str_y;
WriteLnString(fileHandle,str_log,bytesWritten);
}

void IfEndTask()
{
Off(Motors);
tick=CurrentTick();
while(CurrentTick() {
PlayTone(880,20);
Wait(450);
}
AxisRecord();
StopAllTasks();
}


task main()
{
DeleteFile("DataLog.txt");
CreateFile("DataLog.txt",10000,fileHandle);

SetSensorLowspeed(ColorSensorPort);
SetSensorLight(LightSensor1Port,true);
SetSensorLight(LightSensor2Port,true);
InitialLightSensor();
}

task BotWalkTask()
{
Follows(main);

while(true)
{
colornum=SensorHTColorNum(ColorSensorPort);
if((colornum!=blue_tap1)||(colornum!=blue_tap2)) //判斷是否遇到叉路
{
if(colornum==red_tap)
{
IfEndTask();
}
BotWalking();
}
if((colornum==blue_tap1)||(colornum==blue_tap2))
{
AxisRecord();
BotTurning();

}
}
}


三、執行結果:

video

2011年11月24日 星期四

Google App Inventor: 亂數產生器

亂數產生器,隨機產生一個或一組數字,在教室情境中很受歡迎。
當老師想要點名,卻又不想流於形式,亂數產生器得到的號碼,讓學生無法得知下一刻是誰被點到,那種急速上升刺激與快感,難以形容。

曾在Market找相關的軟體,卻未盡滿足我的需要,所以心中浮現用app inventor來開發,相信難度不高,且能客製化自己的需要來安排各種功能。

首先,我的軟體介面如下:

(圖1 軟體介面)

圖1中:
1.[亂數範圍]可由使用者自行決定起訖範圍。
2.[剔除不要的號碼]欄位,由使用者自行決定亂數產生時,以不出現此欄位指定的號碼為主。
3.當按下[隨機產生號碼]按鈕,即產生斗大的一個號碼於畫面中。

程式碼:

上述程式中:
1.StartNum及EndNum變數,分別指定為主畫面使用者輸入起訖號碼
2.RndNo變數,由[random integer函式],隨機產生一數字並指定回RndNo,範圍如上述1.
3.RemoveElementNum變數,使用者指定[剔除號碼]後,系統自動統計其個數,並指定給RemoveElementNum
4.for range迴圈:
檢視[隨機產生的數字]是否與[欲剔除的數字]相等?若相等,則重新產生一個新的數字。直至不重複為止。

2011年11月19日 星期六

Hitechnic Color Sensor 測試

一、硬體方面
color sensor 接在nxt主機上的IN_4。
二、軟體方面
根據[Third-party sensors: a review of some Hitechnic’s sensors]一文提到:
The new and totally redesigned HiTechnic Color Sensor Version 2 (V2) operates by using a single white LED (light emitting diode) to illuminate the target and analyses the color components of the light reflected by the target’s surface and calculates a Color Number that is returned to the NXT program.

Sensor Register Layout

AdressTypeContents
42HbyteColor number
43HbyteRed reading
44HbyteGreen reading
45HbyteBlue reading

Procedures are already available in Bricxcc to use the sensor:

  • SetSensorLowspeed() initializes the I2C communication with the sensor.
  • SensorHTColorNum(port) returns the value of Color number.
  • ReadSensorHTRawColor(port, rouge, vert, bleu) returns the RGB readings.
  • ReadSensorHTColor(port, index, rouge, vert, bleu) returns the value of Color number, and the RGB readings. See the code example at the end of the article ...
所以在NXC的程式基本採用的指令,包含二個:
1. SetSensorLowspeed(IN_4)
2. ReadSensorHTColor(IN_4,colorvalue,r,g,b)
其中,IN_4是彩色感應器接向IN_4,而colorvalue是經由color sensor回應的讀值,r,g,b分別為red,green,blue三原色的數值。

三、程式碼
#define ColorSensorPort IN_4

task main()
{

byte r,g,b;
byte colorvalue;
SetSensorLowspeed(ColorSensorPort);

while(true)
{
ReadSensorHTColor(ColorSensorPort, colorvalue,r,g,b);
NumOut(0,LCD_LINE3,colorvalue);
if (colorvalue==0) PlayTone(880,25);
Wait(25);
ClearScreen();
}
}

上述程式是說:
當color sensor讀值(colorvalue)=0,則PlayTone一個短音。
colorvalue=0-->黑色

2011年10月22日 星期六

Google App Inventor - 使用手機的語音控制

如果你的手機支援語音搜尋,那麼應該可以啟用語音辨識的操作。
這個功能可以替代手寫,英語的辨識率還算不錯,但中文辨識率不太高就是了。
(假如假設你發音正確,那麼應該可以顯示將你想說的語音轉成文字)

首先,我在手機介面上,設計如下的簡易畫面來進行測試:

(圖1)手機操作畫面

圖1中,我引用了App inventor的Other stuff中的[Speech recognizer]元件作語音控制。
而[按下後說話]的按鈕,是用來啟用speech recognizer,當你按下後即可說話。
簡單說了一段英文或中文語句,文字將顯示在圖中的Voice2TextResult文字欄位。

-------------------------------
其次,開啟Block Editor,進行程式編輯。
程式設計如下:

(圖2)程式內容

圖2中:
1. SpeechAfterPressedBtn-即按鈕,按下按鈕後,開始說話。
 call SpeechRecognizer1.GetText,呼叫語音辨識,取得文字。
2. When...SpeechRecognizer1.BeforeGettingText-
 當操作者還在說話時,介面上Voice2TextResult文字欄位的內容為空白
3. When...SpeechRecognizer1.AfterGettingText-
 將Voice2TextResult文字欄位指定為SpeechRecognizer1.Result
 即所說的內容,經過語音辨識後將文字呈現在Voice2TextResult文字欄位

--------------------------
有興趣的玩家,可以利用這樣的功能,實現在NXT的藍芽控制,當你說:
Go或是向前、Back或是向後、Left或是向左、Right或是向右,經過語音辨識,再結合
前述[Google App Inventor -手機藍芽遙控Segway]的方法,程式稍微修改一下就可
讓你的機器人用語音控制前進,後退、向左及向右了。

2011年10月15日 星期六

利用手機的指北針感應器,來做方位感測

前幾天利用手機的digital compass sensor (orientation sensor),拿來做為方位的度量。
設計的機構如下:


結果發現,這樣的機構,因為太靠近nxt motor,會產生磁場嚴重的干擾而使度量發生錯誤。
今天再改裝加高鷹架,機構如圖:


尚未測試,也許可以改善。測試的結果待續。

2011年9月20日 星期二

Google App Inventor - 使用手機的LocationSensor讀取經緯資料

現在的手機只要具備GPS的功能,就可以使用Google App Inventor裡的LocationSensor 來讀取
所在位置的經緯度、高度等資料。


(圖1-軟體介面外型)

部分程式:

其中:
1. [value latitude] -->由Location Sensor傳回的緯度數據
2. [value longitude]-->由Location Sensor 傳回的經度數據
3. LatitudeReport.Text --> 將[value latitude]顯示在(圖1)的緯度欄位
4. LongitudeReport.Text-->將[value longitude]顯示在(圖1)的經度欄位
5. AddressReport.Text-->將[LocationSensor1.CurrentAddress]的結果顯示在位址欄位

至於已知兩處AB的經緯度,如何得知A-B間最短的距離?
我利用:

其中:
其中 為大圓距離, 是地球半徑(6371km),而點 1 經度、緯度分別為 ,點 2 的經度、緯度分別為
其實這只是球面三角餘弦公式的推廣,注意經緯度的度數計算時要轉成徑度,才會計算正確。
d算出來的單位為km.

2011年9月14日 星期三

Google App Inventor- 製作找方位軟體

坊間有一些support Compass的軟體,以圖像的方式做出指北針,操作者可由指針朝北定出方位。

筆者試著以Google App inventor製作一簡易型的指北針,只要您的手機有Orientation Sensor就具備數位指北針的支援。


(圖1)

介面說明:
1. [Compass Test]-軟體標題
2. [CurrentDirection]-標籤文字
3. [TextBox]-存放方位感測器(Orientation Sensor)的方位
4. [CheckDirection]- 按鈕每點一次,可搜尋當前方位角
5. 引用OrientationSensor,並設定為[enable]


※使用AppInventor,介面可使用中文字,但Block Editor僅能使用英文字,設計時須注意。

(圖2)

圖2即Block Edit的程式碼。
由appinventor手冊提供的orientation sensor,偵測到的為數值,介於0~359。
其中:
0: 北方
90: 東方
180: 南方
270: 西方

筆者將數值轉化成文字,使操作的時候較易識別。
-------------------------------
※測試時,操作者將手機任意指向一方位,按下[CheckDirection]按鈕,即馬上顯示該方位的方位角,但無角度顯示,僅以16方位之結果顯示之。
即:
東、西、南、北
東南、東北、東南東、南東南、北北東、東北東
西南、西北、西南西、南南西、西北西、北北西

2011年9月4日 星期日

手機藍芽遙控挖土機

video

這個遙控的原理承襲前述藍芽遙控的方式,技術上大同小異。
bot裝置參考[Lego.com Mindstorms]部分的接法。
機身略有修改。

2011年9月1日 星期四

VB設計:讓電腦自動偵測SerialPort,並自動與nxt連線

在上一篇[利用藍芽技術,讓電腦可控制NXT主機],當時談到:

在設計時,使用者必須先知道自己的電腦對外連線的輸出埠(Com port)並連結,才能讓電腦透過藍芽連線裝置。

然而,如果能夠設計一程式,讓電腦在開啟軟體後,能夠自動偵測與連結電腦現有的輸出埠,而無須使用者去指定,便能增加其便利性。

換句話說,電腦到底選中哪一個輸出埠,交由軟體自己去尋找與連線,而不再經由使用者去確認與指定。

我的作法是:
ˋ
上圖即VB建構的一個畫面,畫面上方做一個MenuStrip,裡面的item包含Connect 、Disconnect
以及結束。

當按下[connect]的item,程式碼相對應的內容如下:

   Private Sub ComPortConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComPortConnect.Click

Dim com_port As String = ""

For Each sp As String In My.Computer.Ports.SerialPortNames
com_port = sp
Next

SerialPort1.PortName = com_port
SerialPort1.Open()
ConnStatus.Text = com_port + " is Connected"
End Sub
------
其中:
1.ComPortConnect_Click() 代表按下該item後即將完成的動作行為
2. Dim com_port As String ="": 宣告com_port為字串變數,其初始值為空字串
3. For Each sp.....:這一段的敘述,讓電腦自動尋找已建立的com port name有哪些,其結果回傳給sp變數,並指定給com_port
4. SerialPort1.PortName = com_port: 指定SerialPort的PortName為被找到的com_port
5. SerialPort1.Open( ): 開啟該com_port的序列埠
6. ConnStatus是畫面裏頭的文字方塊(TextBox),當連線成功,則在該TextBox顯示COM(x) is connected!

當按下[Disconnect]的item,相對應程式碼如下:


SerialPort1.Close()
ConnStatus.Text = "Wait to connect!"

上述意思是:
把SerialPort開啟的序列埠關閉,並於文字方塊顯示Wait to connect!

2011年8月23日 星期二

Kinect技術,值得研究!

今天在大潤發特別停駐在Kinect看人打電玩的時候,不是因為電玩好玩到甚麼程度,而是Kinect感應器的技術吸引我。

-----------------
目前Kinect的技術有三大主流:Kinect一次可擷取三種東西,分別是彩色影像、3D深度影像、以及聲音訊號。

Kinect主要就是靠3D深度感應器偵測玩家的動作。中間視訊鏡頭則是用來辨識玩家身分(靠著人臉辨識和身體特徵)、以及辨識基本的臉部表情。
-----------------

這可以提供給做影像辨識的研究一個方向,傳統的影像辨識各家學派技術推陳出新,技術艱深難懂,如果能夠統一規格,當然對間接使用這項技術應用的人來說,他也許不需要再學辨識技術,只要接續這項技術的後端,繼續開發更多的應用,這真的很不錯喔。

當然,以遊戲的角度來思考,Kinect的技術優於Wii或PS,在於使用者無須背負(手持)感應器(如Wii要手持遙控器),Kinect完全拋棄這些雜七雜八的東西,讓使用者更輕盈。

寒假再來好好研究這一項技術。

2011年8月20日 星期六

難度很高的NXT bike

NXT bike難度很高,感覺比segway更難。剛剛看youtube連結NXT_Bike(http://www.youtube.com/watch?v=VxiOy4QzD7I),酷!但看起來就難~

機構與程式在http://www.rjmcnamara.com/2010/08/05/nxt-based-bicycle-robot/

2011年8月19日 星期五

Google App Inventor -手機藍芽遙控Segway

這兩天使用Google App Inventor 將VB版的操作平台,實現在App Inventor上,操作時可以直接在手機上使用。

Google App Inventor 設計區安排的介面如下:


操作面板上,點選藍芽連線的按鈕,選擇與之匹配的NXT,即完成連線。

程式區,結構如下:

解釋:軟體啟動後,將預備顯示的區塊設定顯示(visible true)、不想出現的先隱藏(visible false)
ListPicker.BeforePicking等待使用者回應按鈕,使用者按下後,則進入挑選清單。
清單裡頭包含已被手機偵測到的藍芽裝置都會出現在畫面中。


解釋:
當ListPicker.BeforePicking按下按鈕後,被選到的清單藍芽裝置便會與手機連線。
如果連線成功,則出現Connecting...字樣
如果連線失敗,則出現can not be connected!



解釋:
當藍芽離線後,畫面出現Wait之字樣(回到初始狀態)。



解釋:
本例僅作ForwardBtn的操作。
當使用者按下ForwardBtn按鈕,程式指定ControlMessage之變數值為F,且透過
NxtDirectCommands1.MessageWrite的方法,指定mailbox為1、message為ControlMessage,
將變數的文字傳出給NXT。(NXT為接收端,程式須設計接收訊息)

附帶一提,一個很簡單卻又很重要的一個設定,少了這個操作,你的手機還是無法與nxt連結,那就是點選NxtDirectCommands1,右邊的Properties,須指定為BluetoothClient1


最後,Connect to Device,選擇你的手機,就可以開始操作了。

-----------------
至於NXT端的程式,程式碼如下:
#include "NXCDefs.h"

#define Touch IN_1

void BTCommCheck(int conn)
{
if (BluetoothStatus(conn)!=NO_ERR) //如果nxt未與主機連線
{
TextOut(5,LCD_LINE2,"Error");
Wait(1000);
Stop(true);
}
}

//-----------------------------------------
task main()
{
 string ControlMessage;
 int power=20,power_left,power_right;
 BTCommCheck(0);
 SetSensorTouch(Touch);
 ClearScreen();
 while(SENSOR_1==0)
 {
   ReceiveRemoteString(MAILBOX1,true,ControlMessage);
   TextOut(0,LCD_LINE1,ControlMessage);
 }
}

video

2011年8月15日 星期一

segway-改裝後的猴子騎兩輪車

video

brick結構概念參考NXT Segway with Rider

今早想採用上述的結構,但因為該組裝結構沿用我前面寫的bot balance程式,似乎會一面傾倒,懶得修改程式,畢竟修改程式調整參數,耗費的工夫相較於改裝,當然是改裝比較簡單,因此動了一下腦筋,以原來的NXT Segway with Rider的概念,以及原有的兩輪平衡車子為基礎,簡單的說就是原來的兩輪平衡車上頭,加上一隻猴子。

這一隻猴子有另一個用途,猴子身體加裝一馬達,可以調整猴子身體前傾或後仰,來改變機身的重心位置,後續的設計將朝此製作。

2011年8月13日 星期六

利用藍芽技術,讓電腦可控制NXT主機

NXT Brick本身已內建藍芽裝置,對於想透過藍芽技術對NXT予以控制的玩家早已躍躍欲試。
目前已有相當齊備的文獻:
1.有些玩家透過兩台NXT,一台當作Master、一台當作Slave,由Master主機對Slave進行直接控制。
2.透過電腦PC與NXT,由電腦端(主控端)傳送控制指令(Send Message),並由NXT回饋數值給主控端。
3.利用Android 手機與NXT,傳送控制指令(Send Message)對NXT下達操作指令。


本文旨在建立Visual Basic 介面,利用藍芽技術,由電腦操控NXT

一、首先,利用Visual Basic Express ,建立一個操控界面

     (圖一)

上圖,通訊埠預設COM1~4,依使用者需要自行增加,通訊埠請選擇[VB工具箱/元件/SerialPort]物件,把該物件拖曳至介面中。

二、介面中的[通訊埠],每一個選項(COM1~4),假設使用者選擇COM1,程式會先判斷目前電腦是否與NXT連線,如早已連線,會先中斷連線,並再次連線,以確保不重複連線的問題。
    COM1ToolStripMenuItem.Checked = True

    COM2ToolStripMenuItem.Checked = False
    COM3ToolStripMenuItem.Checked = False
    COM4ToolStripMenuItem.Checked = False
    If SerialPort1.IsOpen() Then
      SerialPort1.Close()
    End If
    SerialPort1.PortName = COM1ToolStripMenuItem.Text
    SerialPort1.Open()
    ConnText.Text = "已連線"
上述部分的程式碼,其中
1.COM1ToolStripMenuItem是介面中COM1的選項Name,COM1ToolStripMenuItem.Checked用以告知使用者當前選擇哪一個通訊埠,會在該選項出現打勾
2.使用者該選擇哪一個通訊埠?請打開電腦藍芽選項,先讓電腦搜尋可與電腦藍芽連線的裝置,當然,你一定要記得先打開NXT的電源以及藍芽(ON),才能讓電腦搜尋的到。當使用者找到NXT後,會要求使用者輸入密碼,預設為1234,因此在電腦輸入1234的密碼後,便與NXT匹配連線了
3.此時,點選可與電腦連線的裝置兩下,可看到設定的選項,其中[輸出序列埠]就是預備和NXT連線的對外通訊埠。這個通訊埠即我們要選擇的。


三、介面中有[前進]、[後退]、[向左]、[向右]四個按鈕,使用者按下按鈕後,會傳出指令給NXT,因此相關設定的程式碼如下:
  Dim byteOut(9) As Byte
  byteOut(0) = 2 + 5 '2 bytes in output message
  byteOut(1) = &H0 'should be 0 for NXT
  byteOut(2) = &H80 'Direct Command no reply expected
  byteOut(3) = &H9 'MESSAGEWRITE
  byteOut(4) = &H0 'Box Number - 1
  byteOut(5) = 2 + 1 '2-byte message size with null terminator
  byteOut(6) = Asc("F")
  byteOut(7) = Asc(" ") ' space ASCII code
  byteOut(8) = &H0 'null terminator
  SerialPort1.Write(byteOut, 0, 9)

1.上述程式碼是[前進]按鈕的功能。其他[後退]、[向左]、[向右]程式碼均相同,可複製貼上至其他按鈕。
2.唯一需要修改的,在於byteOut(6)。茲設定的[前進],當按下按鈕時,送出字元F;
[後退]送出字元B;[向左]送出字元L;[向右]送出字元R
3.byteOut為電腦與NXT之間通訊的方式,相關文獻可參考[NXTreme]、[研發養成所]及[VB and Connecting the NXT with Bluetooth]
4.電腦傳輸命令至NXT,是送到指定的信箱(MAILBOX),所以byteOut(4)=&H0是MAILBOX1,由使用者自行定義要傳到哪一個信箱,以便NXT接收。當然,NXT的程式碼,其收件信箱也要指定相同的MAILBOX,投遞與接收要相同。

至此,VB的通訊介面業已底定。
●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●
其次,NXT的接收程式碼,茲以Brixcc的NXC碼編寫,程式碼如下:

#include "NXCDefs.h"


#define MAILBOX1 0
#define Touch IN_1

void BTCommCheck(int conn)
{
if (BluetoothStatus(conn)!=NO_ERR) //如果nxt未與主機連線
{
TextOut(5,LCD_LINE2,"Error");
Wait(1000);
Stop(true);
}
}

//-----------------------------------------
task main()
{
string ControlMessage;
BTCommCheck(0);
SetSensorTouch(Touch);
ClearScreen();
while(SENSOR_1==0)
{
ReceiveRemoteString(MAILBOX1,true,ControlMessage);
TextOut(0,LCD_LINE1,ControlMessage);
}
}
1.程式開啟後,NXT會先檢查是否與PC連線,如果未連線,則出現ERROR畫面並結束程式,使用者必須先啟動前述建立的VB介面軟體,點選通訊埠連線
2.連線後,當使用者在VB介面軟體按下[前進]、[後退]、[向左]、[向右]任一按鈕,則於NXT畫面顯示相對應的字元(F、B、L、R)
3.BTCommCheck(0)之引數0,代表NXT設定為slave端。
4.從電腦發送的命令,直至NXT接收,本身為字元,所以設定一個變數ControlMessage接收之
video

2011年8月10日 星期三

實現Segway 兩輪平衡車

Segway是一個非常不穩定的非線性模型,很多研究多半會選擇​segway作為鑑定其控制器設計的良窳。

Segway是一台兩輪式、自由度高、且易於往兩側傾倒的極不平​衡車子,為了維持車子能立直中立,而不易傾倒,則便是研究者致力​研究的終極目的。

現階段研究,能使機身維持筆直站立且穩定,下階段想透過手機藍芽​裝置,來遠端遙控Segway。

[執行結果]

video

video
卡丁車與segway對決

Matlab for NXT,測試光感知器每秒鐘亮滅一次設計

[說明]使用Matlab 讓光感知器每秒鐘做亮或滅的動作

[程式碼]

clear all

clc

%Define Sensor Parameters
SwitchPort = SENSOR_1;
LightPort =SENSOR_2;
Mode = 0;

COM_CloseNXT('all');

hNXT=COM_OpenNXT();
COM_SetDefaultNXT(hNXT);

OpenSwitch(SwitchPort);

while(GetSwitch(SwitchPort)==false)
  if(Mode == 0)
    ModeType = 'INACTIVE';
    OpenLight(LightPort,ModeType);
    pause(1);
  end
  if(Mode == 1)
    ModeType = 'ACTIVE';
    OpenLight(LightPort,ModeType);
    pause(1);
  end
  Mode = not(Mode);
end

COM_CloseNXT(hNXT);
使用not函數,用以改變mode狀態


[執行結果]

video

2011年8月9日 星期二

Matlab for NXT,經卡爾門濾波前後,陀螺儀數據觀測

經卡爾門濾波前後,陀螺儀數據觀測,發現有很明顯的濾波效果,由記錄的結果可證實。

[程式碼]
% 測試陀螺儀元件- GetGyro()

clear all
clc

%IN Port 設定
touch_port = SENSOR_1;
gyroport = SENSOR_3;

%設定Kalman Filter 參數
P=10;
Q=1e-4;
R=0.05;
prev_filter_value=0;

COM_CloseNXT('all');
hNXT=COM_OpenNXT();
COM_SetDefaultNXT(hNXT);

OpenSwitch(touch_port); %開啟觸碰感應器

OpenGyro(gyroport); %開啟陀螺儀感知器
offset = CalibrateGyro(gyroport,'AUTO'); %計算陀螺儀偏移值(offset)

cnt=1;
while(GetSwitch(touch_port)==false)
 GyroValue= GetGyro(gyroport); %陀螺儀原始數據
 [P,curr_filter_value]=Kalman_filter(P,Q,R,prev_filter_value,GyroValue); %陀螺儀數據經卡爾門濾波後的數據
 disp(sprintf('GyroValue=%2.2f',curr_filter_value));
 pause(0.01);
 clc
 data(1,cnt)=GyroValue; %紀錄原始陀螺儀數據
 data(2,cnt)=curr_filter_value; %紀錄卡爾門濾波後的數據
 prev_filter_value=curr_filter_value;
 cnt=cnt+1;
end

CloseSensor(touch_port); %關閉感知器
CloseSensor(gyroport);
COM_CloseNXT(hNXT);

plot(data(1,:))
hold on
plot(data(2,:),'r-')
hold off


[副程式Kalman_Filter.m]

function [P,curr_filter_value]=Kalman_filter(P,Q,R,prev_filter_value,measurement_value)
 P=P+Q; %第k-1時刻與第k時刻前測之間的協方差P
 Kgain=P/(P+R); %卡爾曼增益
 curr_filter_value=prev_filter_value+Kgain*(measurement_value-prev_filter_value);
 P=(1-Kgain)*P; %第k時刻後測與前測之間的協方差
end



[執行結果]
offset=610.2152

從上圖中,藍色線代表未濾波前的數據、紅色線為濾波後的數據,前後結果非常明顯。

2011年8月8日 星期一

Matlab for NXT,陀螺儀測試偏移值

本程式碼用以測試陀螺儀(Hitechnic Gyro)之偏移值(offset),將機器人扶正靜待1秒鐘,即可完成檢測。
程式碼如下:
% 測試陀螺儀元件

clear all
clc
% 設定陀螺儀參數
port = SENSOR_3;

COM_CloseNXT('all');
hNXT=COM_OpenNXT();
COM_SetDefaultNXT(hNXT);

OpenGyro(port);
offset = CalibrateGyro(port,'AUTO');

disp(sprintf('Gyro Sensor offset = %d ',offset));

COM_CloseNXT(hNXT);

Matlab For NXT,本程式測試讀取MotorA轉角,並回傳數值至電腦

[程式碼]
clear all

clc

COM_CloseNXT('all');

hNXT=COM_OpenNXT(); %此指令會尋找使用 USB 線與電腦連接的 NXT,若是要使用藍芽的話則只要在括號內加上 'bluetooth.ini' 即可
COM_SetDefaultNXT(hNXT); %將 hNXT 設為全域的預設代碼,之後就不需要在指令後再輸入代碼了

force=50;
Dist=360;

MyMotor = NXTMotor(MOTOR_A,'Power',force, 'TachoLimit', Dist);
MyMotor.ResetPosition();

MyMotor.SendToNXT();
data=MyMotor.ReadFromNXT();

while(data.Position < Dist)
data=MyMotor.ReadFromNXT();
disp(sprintf('Current Position of Motor A: %d', data.Position));
end

COM_CloseNXT(hNXT);

Matlab for NXT,馬達正轉與反轉測試

將馬達接在port A,C,進行正反轉測試
[程式碼]
%測試A,C馬達正轉與反轉
clear all

clc

COM_CloseNXT('all');

hNXT=COM_OpenNXT(); %此指令會尋找使用 USB 線與電腦連接的 NXT,若是要使用藍芽的話則只要在括號內加上 'bluetooth.ini' 即可
COM_SetDefaultNXT(hNXT); %將 hNXT 設為全域的預設代碼,之後就不需要在指令後再輸入代碼了

LeftWheel=MOTOR_A;
RightWheel=MOTOR_C;
BothWheel=[LeftWheel;RightWheel];
Force=50; %馬達動力
Dist=360; %馬達轉動的角度

MotorForward = NXTMotor(BothWheel,'Power',Force, 'TachoLimit', Dist);
MotorReverse = MotorForward; %設定MotorReverse結構與MotorForward相同
MotorReverse.Power= -Force; %個別修改Power屬性

MotorForward.SendToNXT(); %送出正轉指令
MotorForward.WaitFor(); %等待執行完畢

MotorReverse.SendToNXT(); %送出反轉指令
MotorReverse.WaitFor();

COM_CloseNXT(hNXT);

2011年8月7日 星期日

RWTH- MindStorms NXT Toolbox Documents

這個網站完整記錄Matlab 編輯NXT 控制,對於感測器、馬達等元件的語法說明。
http://www.mindstorms.rwth-aachen.de/trac/wiki/Documentation

2011年8月6日 星期六

Matlab for NXT,搭配LightSensor與TouchSensor之測試實驗

以前接觸Brixcc或lejos,TouchSensor就叫做TouchSensor,但Matlab的Toolbox命名為Switch。

以程式碼解說實際的簡單應用:
[程式碼]

%觸碰感知器(TouchSensor)、光感知器(LightSensor)測試

clear all
clc

COM_CloseNXT('all');

hNXT=COM_OpenNXT(); %此指令會尋找使用 USB 線與電腦連接的 NXT,若是要使用藍芽的話則只要在括號內加上 'bluetooth.ini' 即可
COM_SetDefaultNXT(hNXT); %將 hNXT 設為全域的預設代碼,之後就不需要在指令後再輸入代碼了

OpenSwitch(Sensor_1);
OpenLight(Sensor_2,'ACTIVE');

cnt=1;
while (GetSwitch(Sensor_1)==false)
LightValue(cnt)=GetLight(Sensor_2)*0.09775;
cnt=cnt+1;
end

plot(LightValue)
COM_CloseNXT(hNXT);


解說:
1.這個程式利用OpenLight()指令、OpenSwitch()指令分別初始光感知器、觸碰感知器,指令的應用,請在Matlab command提示字元下,輸入help OpenLight、help OpenSwitch查閱
2.OpenLight()指令,參數使用ACTIVE,告知初始即點亮LightSensor
3.GetSwitch()用以取得觸碰感知器觸碰與否,有則回傳true,反之則以false
4.GetLight()指令用以取得光感知器的讀值,乘以0.09775轉換為我們較為了解的光感值
5.程式判斷觸碰感知器未被按下時,記錄光感知器的讀值於LightValue陣列,並於觸碰感知器被按下後,將結果plot出來。

Matlab for NXT,程式碼的外殼template

紅色文字顏色套用自程式碼中。

[程式碼]
clear all
clc
COM_CloseNXT('all');

%此指令會尋找使用 USB 線與電腦連接的 NXT,若是要使用藍芽的話則只要在括號內加上 'bluetooth.ini' 即可
hNXT=COM_OpenNXT();

%將 hNXT 設為全域的預設代碼,之後就不需要在指令後再輸入代碼了
COM_SetDefaultNXT(hNXT);

:
:
程式區
:
:



COM_CloseNXT(hNXT);

Matlab for NXT,安裝ToolBox套件

筆者過去接觸Matlab多半用於自動控制設計,對於NXT的操作算是頭一遭。如有謬誤,請指教。

環境說明:
1.使用Matlab版本為R2008b
2.作業系統OS為win7
3.NXT_Driver for win7
(1) 32bit: http://www.robotc.net/files/NXT_32bit_driver.zip
(2) 64bit: http://www.robotc.net/files/NXT_64bit_driver.zip
4. NXT Firmware: LEGO_MINDSTORMS_NXT_Firrmware_V1.29.rfw
5. RWTH Mindstorms NXT toolbox

安裝toolbox步驟說明
1.到RWTH網站下載最新toolbox

2.更新NXT firmware韌體程式至最新

3.打開matlab,點選[File/Set Path/Add Folder],並指定[RWTHMindstormsNXT]資料夾所在位置,選擇後按[save]

4.再次選擇[Add Folder],並指定[RWTHMindstormsNXT/tools]之資料夾加入,按[save]

5.打開RWTHMindstormsNXT 資料夾,進入子資料夾tools->MotorControl,裡面會有一個檔案MotorControl22.rxe,利用NXT-G或Brixcc將該檔案傳到NXT主機上
(所有傳呼控制都會執行此檔案,務必上傳)

6.
接上NXT,進入MATLAB,在command window上輸入指令: COM_OpenNXT
如果畫面正常,將出現NXT連線後的相關資訊。

2011年7月28日 星期四

粒子群演算法尋找比例控制器P最佳參數

為了驗證粒子群演算法在定位控制的應用之可行性,所有應用到粒子群演算法的相關參數、係數與作法同前述。

茲僅以比例控制器(P-controller)做驗證,至於比例-積分控制(Proportional-Integrate control, PI control)、比例-微分控制(Proportional-Differential control, PD control)有興趣的研究者可以參考。

茲選擇受控對象的轉移函數為:
y(s)/u(s)=(s+1)/(s^2+2s+1)...................(1)

此受控對象的步階響應(Step response)為下圖所示:

(圖一)

由圖一中可得知受控對象的響應,上升時間大約5秒(0~100% level),安定時間約6秒。
換句話說,此受控對象加入一個穩定的電源,從開始到穩定,大概需要6秒的時間。

為了使受控對象的輸出響應可以獲得比較滿意的結果(例如:上升時間低於1秒,或安定時間能
縮短),因此設計一個良好的控制系統是非常重要的。
PID控制器對於受控對象系統模型不明確仍有極佳的控制效能,因此廣泛使用於工業控制。然而PID雖然結構簡單,但如果參數設定不佳,仍有可能使系統產生不佳的控制效果,甚至造成系統的不穩定。

廣泛地從相關文獻可略而得知:
比例控制器(P-Controller):有效縮短響應時間、存在穩態誤差
積分控制器(I-Controller):增加系統的型式、改善響應的穩態誤差、但易使系統產生不穩定
微分控制器(D-Controller):改善響應時間、增加系統穩定能力

模擬實驗中,我們初期先撒出200個particles,每一個particle代表kp值(數值介於5~20之間),其參數設定請參酌程式碼。每一個kp帶入副函式(PSO_position_control.m),求得每一個kp所產生的響應結果,將適應函數的值(本例採error平方的加總結果)記錄之,並比較每一組的優劣,最終找出最佳的結果。

[程式碼]
clear all
clc

%初始化
N=200; %partical 個數
Period=10; % 迭代次數
Vmax=1;

kp=15*rand(1,N)+5; %產生N個數的kp隨機值,範圍介於+5~+20
P_best=kp; %個體最優值,初始狀態取kp相同值
G_best=25;
V=zeros(1,N);
C1=2;
C2=2;
W=0.8;

for i=1:Period
i
for j=1:N
V(2,j)=W*V(1,j)+C1*rand(1)*(P_best(1,j)-kp(1,j))+C2*rand(1)*(G_best-kp(1,j));
if(abs(V(2,j)>Vmax))
V(2,j)=sign(V(2,j))*Vmax;
end
kp(2,j)=kp(1,j)+V(2,j);
P_best_fitness=PSO_position_control(P_best(1,j));
current_particle_fitness=PSO_position_control(kp(2,j));
if(current_particle_fitness<P_best_fitness)
P_best(1,j)=kp(2,j);
end
end
V(1,:)=V(2,:);
kp(1,:)=kp(2,:);
for j=1:N
P_best_fitness=PSO_position_control(P_best(1,j));
G_best_fitness=PSO_position_control(G_best);
if(P_best_fitness<G_best_fitness)
G_best=P_best(1,j);
end
end
end

G_best

[副函式PSO_position_control.m]
function fitness=PSO_position_control(kp)
%系統轉移函數 sys_open(s)=(s+1)/(s^2+2s+1)
num=[1 1];
den=[1 2 1];
sys_open=tf(num,den);

%轉移函數轉換為狀態空間方程式
[a,b,c,d]=tf2ss(num,den);
sys_open=ss(a,b,c,d);

%初始設定
Ts=0.01; %sampling time
x=zeros(2,1); %state space variables is set to zero
u=0;
R=10; %reference value
y=0; %current state space output value, assume as velocity
position=0; %current position
cnt=1;
err=0; %error between reference position value and current one.
sum_err=0;

for t=0:Ts:10
dot_x=a*x+b*u;
y=c*x+d*u;

position=position+y*Ts;

err=R-position;
sum_err=sum_err+0.5*(err)^2;
u=kp*err;

x=dot_x;
plot_position(cnt)=position;
cnt=cnt+1;
end

fitness=sum_err;



[執行結果]
經由最佳化搜尋,找到kp=29.0621可有良好的效能,因此將此參數帶入觀察,其響應結果如下圖所示:

(圖二)

其上升時間、安定時間都在1秒之內,效果卓著。

2011年7月25日 星期一

求解最佳化的問題-使用粒子群演算法,part4

最佳化問題,求解一維函數F(x)=x^2的極值

粒子群演算法相關參數初值定義如下:

particle 個數N=200

迭代次數period=1000次

Vmax=5粒子飛行速度最大值

X=10*rand(1,N)-5,隨機產生N個-5~+5之間的數

V=zeros(1,N),所有粒子飛行速度初值均為0

C1=C2=2

W=0.8

G_best=1000,寫大一點,否則會一下子就收斂

P_best=X ,一開始,個體的最佳值即一開始當前的位置

[程式碼]


%一維函數F(x)=x^2,給定範圍找最小值

clear all
clc

%初始化
N=200; %partical 個數
Period=1000; % 迭代次數
Vmax=5;

X=10*rand(1,N)-5; %產生N個數的隨機值,範圍介於-5~+5
P_best=X; %個體最優值,初始狀態取X相同值
G_best=1000;
V=zeros(1,N);
C1=2;
C2=2;
W=0.8;

cnt=1;
for i=1:Period
for j=1:N
V(2,j)=W*V(1,j)+C1*rand(1)*(P_best(1,j)-X(1,j))+C2*rand(1)*(G_best-X(1,j));
if(abs(V(2,j)>Vmax))
V(2,j)=sign(V(2,j))*Vmax;
end
X(2,j)=X(1,j)+V(2,j);
P_best_fitness=fitness(P_best(1,j));
current_particle_fitness=fitness(X(2,j));
if(current_particle_fitness<P_best_fitness)
P_best(1,j)=X(2,j);
end
end
V(1,:)=V(2,:);
X(1,:)=X(2,:);
for j=1:N
P_best_fitness=fitness(P_best(1,j));
G_best_fitness=fitness(G_best);
if(P_best_fitness<G_best_fitness)
G_best=P_best(1,j);
end
end
G_best_dot(cnt)=G_best;
cnt=cnt+1;
end

plot(G_best_dot)
G_best


副函數fitness:

function fit=fitness(X)

fit=X^2;

end

[執行結果]

從圖中可看出最佳值收斂,且經計算得到G_best=-3.9680e-021

換句話說,電腦分析當-3.9680e-021可得函數F(-3.9680e-021)的最小值。當然,迭代的分析,必然存在誤差,但其結果仍令人滿意,與期望值0極為接近。

求解最佳化的問題-使用粒子群演算法,part3

接續前一篇討論…

粒子群演算法,在運算上非常的簡單,首先先初始撒下粒子之個數、粒子飛行速度。

演算法:

1.每一粒子飛行速度與方向遷就於粒子本身及群體,定義飛行速度:

Vi(k+1)=W*Vi(k)+C1*rand()*(P_best_i-Xi(k))+C2*rand()*(G_best-Xi(k))............(1)

其中:

W是權重常數,考慮前一刻飛行速度,對下一刻的飛行速度的影響程度

Vi(k+1):第i個粒子,第k+1時刻的飛行速度

Vi(k):第i個粒子,第k時刻的飛行速度

C1,C2:學習係數,當前位置與最佳位置,影響下一時刻飛行速度的權重

rand():隨機函數,產生0~1之間的小數

P_best_i:第i個粒子,所走路徑中自始至今之最佳值

G_best:群體粒子之最佳值

Xi(k):第i個粒子,當前所在位置

2.定義粒子下一時刻的位置:

Xi(k+1)=Xi(k)+Vi(k+1)...........................(2)

其中:

Xi(k+1):第i個粒子,在第k+1時刻的位置

Xi(k):第i個粒子,在第k時刻的位置

(2)式,速度‧時間=Vi(k+1)△t=位置

3.第i粒子最佳位置取代原則:

需定義適應函數(fitness function),若fit(Xi(k+1))優於fit(P_best_i),則P_best_i=Xi(k+1)

4.群體最佳值的修正時機:

若fit(P_best_i)優於fit(G_best),則取代當前G_best地位

求解最佳化的問題-使用粒子群演算法,part2

粒子群演算法仿效鳥類飛行追求卓越的生命歷程,很像「雁行理論」。

粒子群演算法(Particle Swarm Optimization, PSO),其精神為「一群隨機粒子分佈於一個區域範圍,每一個體粒子搜尋鄰近範圍的最優,個體與個體之間相互比較(取適合度fitness function)並擇其優,經過反覆迭代的過程,找出總體的最佳值。」

以前述的例子來解釋,假設一個一維函數F(x)=x^2..............(1)

首先,先散佈一群(N個)隨機的粒子,每一個粒子都代表此函數的X,例如X1,X2,X3....,XN

每一個X帶入(1)都會有一個相對應的函數值,我們的目的是:

找出X等於多少時,函數有最小值?

在每一次迭代的過程,個體(particle)在一個環境探索(exploit) 的歷程中,反覆與舊有的經驗(個體自始至今的最佳)比較,如果當前的位置優於舊有的經驗,最佳位置則以新值取代。

簡單的說,一隻鳥覓食,假設一個環境,甲處有一隻蟲、乙處有兩隻蟲、丙處有五隻蟲,以鳥的觀點來說,這三處最好的位置,應該是丙處。而鳥在飛翔的過程,假設會先經過甲、再到乙,最終到達丙處,鳥在觀察的過程中會發現乙優於甲、丙又優於乙,所以最佳的位置會不斷被取代。

而一群個體,在一個環境探索的歷程,每一個個體都會找到當前最好的位置,但以一個群體來說,整體最優的位置在哪?

因此,在粒子群演算法中討論到,如果一群個體相互比較,發現某一個體找到的位置是群體中所有的最佳,那麼所有的個體,將追隨這個總體最佳的位置前進。

簡單地說,如果一群鳥在一個環境中,每一隻鳥都會找到不錯的覓食點,假設其中有一隻鳥挖到寶,發現在某一個地方有無限量供應的蟲(酷斃了!),那麼所有的鳥將追隨這個位置並往這個方向飛,而放棄他自己找到還不錯的覓食點。

所以,當你有空停下腳步觀看天上飛翔的鳥,你會發現:一群鳥會跟著其中一隻往某個方向飛…

(待續)

求解最佳化的問題-使用粒子群演算法,part1

研究所時,跟隨恩師學習各種控制技術,印象中最為深刻地莫過於探討最佳化的問題。

有關最佳化的研究,有許多相關文獻運用不同的技術或方法求解最佳化,而最終的目的無非是獲得最佳的結果。至於最佳化的目的,主要為克服人為的經驗誤差,因此求助於微電腦控制,找尋參數的最佳值。

在暴力搜尋法中,遺傳演算法(Genetic Algorithm) 是最經典的技術,另外,仿生物型態的,還包含有蟻行演算法(Ants Algorithm)、及粒子群演算法(Particle Swarm Optimization,仿鳥類生態)。

再談最佳化的問題前,我們先瞭解一個最簡單的問題:

已知一個一維變數的函數F(x)=X^2 (X平方)..............(1)

若X的範圍從-5到+5,我們如果用畫圖的方式,可得以下的圖形:

從圖可以看出,此函數為拋物線函數,且最小值為F(0)=0,即:當X=0時有最小值0

當然,如果用微分來計算,我們可知:

dF(x)/dx=2x....................(2)

令dF(x)/dx=0 可得極值,由(2)可知2x=0,所以x=0............(3)

把(3)的結果帶回(1),所以F(x)=0

2011年7月15日 星期五

2011年6月30日 星期四

PLC-E5.自動化橘子裝箱

題目功能說明:
1.啟動X24開關後,輸送帶Y1正轉開始
2.操作者按下X20開關,即刻下達供給指令(Y0),此時會於輸送帶左側出現一個空箱子。
3.當空箱子經輸送帶至橘子供給機正下方,箱子檢測感測器(X1)感應後,即刻停止輸送帶運轉。
4.供給5個橘子到箱子,並把裝滿五個橘子的箱子輸送到右側托盤。
5.橘子的供給是依據橘子的供給指令(Y2),而橘子數量的檢測則依據橘子通過檢測之感應器(X2)來計數。
[程式碼]

2011年6月29日 星期三

微分方程式-以Matlab ODE45求解

這幾天對於系統識別(System Identification)感興趣,得知Matlab的ode45( )函數,可將高階線性或非線性的微分方程式,例如:

y(n)+a1y(n-1)+a2y(n-2)+….+an-1y’+any=0.......................(1)

求得其解,因此非常了得。當然,所得的數值對照於原函數的真實解,完全吻合。
為說明方便,假設一RLC電路如下:


其電路方程式為:

i’’(t)+3i’(t)+2i(t)=0...................(2)

假設其初值為i(0)=1(A) 且V(0)=2(V)
從(2)式及初值,可得

i(t)=-3e-t+4e-2t(A)...................(3)

上述(3)式即(2)之解。

茲以Matlab 0de45( )函數來解:
[fun.m]
function dydt=fun(t,y)
%線性二階微分方程
dydt(1)=y(2);
dydt(2)=-3*y(2)-2*y(1);
dydt=dydt';


[ode.m]
clear all
clc
[t,y]=ode45(@fun,[0 10],[1 -5]);
y1=y(:,1);
y2=y(:,2);
Z=-3*exp(-t)+4*exp(-2*t);
plot(t,y1,'r',t,y2,'g',t,Z,'b*')

[ode.m]程式碼中,Z為微分方程式的解,在本例中與ode所求得的y1做一比較。

[執行結果]