買書捐殘盟

顯示具有 設計 標籤的文章。 顯示所有文章
顯示具有 設計 標籤的文章。 顯示所有文章

2014年12月12日 星期五

利用matlab,一秒鐘讀取一筆資料

影片結果:https://www.youtube.com/watch?v=JZhv-OIad-8&feature=youtu.be

程式碼:
clear all
clc

data=[100 90 80 60 80 90 75 70 80 100];
n=size(data,2);
sum=0;
average_value=0;

for i=1 :10
 if i==1
  max_value=data(i); 
  min_value=data(i);
 end
 
 sum=sum + data(i);

 if data(i)>max_value
   max_value= data(i);
 end

 if data(i)  min_value=data(i);
 end
 fprintf('第%d筆資料=%d,讀取完畢\n',i,data(i))
 pause(1);
end
fprintf('-------------\n')
average_value=sum/n;
fprintf('平均值=%3.2f\n',average_value)
fprintf('最大值=%d\n',max_value)
fprintf('最大值=%d\n',min_value)

2012年7月8日 星期日

撲克牌洗牌機

久違了,好久沒有新的idea,今天寫一篇關於利用NXT做撲克牌發牌機。



基本上,把撲克牌放兩邊,模擬我們人的手洗牌的樣子。
而機構本身利用輪胎來推進撲克牌至中間的凹槽,馬達轉動的速度無需過快,以免撲克牌卡紙而破壞了牌紙。

且程式也止不過幾行,其中主要用以啟動馬達轉動的程式碼:

OnFwd(OUT_AB,50);

以下是洗牌機的影片片段:

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根據電腦鼠依現在座標、下一個駐點座標的二者地理位置座標,決定左右轉或前行,方向由使用者定義方向,於程式中設定之。

[執行結果]

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();

}
}
}


三、執行結果:

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日 星期日

手機藍芽遙控挖土機



這個遙控的原理承襲前述藍芽遙控的方式,技術上大同小異。
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月10日 星期三

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狀態


[執行結果]

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,馬達正轉與反轉測試

將馬達接在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月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);

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秒之內,效果卓著。