置頂文字

歡迎來訪 !! 如有任何問題歡迎一起討論或是 eMail 給我 legorunmail@Gmail.com

2015年4月27日 星期一

兩台樂高機器人NXT 分工合作



 這次要做的題目是2台樂高機器人NXT 分工合作,而且盡可能把感應器都裝在NXT上這樣在程式設計時就能互相運用做出多樣化的動作與目的。



第一台樂高NXT機器人是「履帶車輛」,前面安裝超音波感應器(UltraSonic Sensor)與光線感應器(Light Sensor),後面安裝爪子與顏色感應器(Color Sensor)。


超音波感應器:負責偵測前方式否有物體存在。
光線感應器:負責車輛行駛在黑色路線軌跡上。


顏色感應器:負責偵測地面上特定顏色做出對應事件動作。






第二台樂高NXT機器人是「機械手臂」,安裝超音波感應器(UltraSonic Sensor)或碰觸感應器(Touch Sensor)負責偵測是否有物品需要抓取。


三顆樂高馬達(Motor)使用內建的角度偵測,所以抓取與放置都能固定在特定位置上。
第二顆升降馬達與第三顆抓取馬達構造圖



第一顆轉向馬達與第二顆升降馬達齒輪連結






當第三顆馬達抓取物品時齒輪經過轉向帶動下個齒輪與傳動軸會產生其它方向應力讓零件慢慢位移是甚至解體,因此在組裝上必須考量結構強度與預防零組件位移措施。


int iTch=Motor.A.getTachoCount();     //讀取A馬達角度值
Motor.A.resetTachoCount();                 //重設A馬達角度值


測試步驟:

1. Android手機與第一台樂高機器人藍芽連結並啟動NXT程式(非必要性只是測試Android手機與樂高機器人的互動)

2.光線感應器先偵測黑色與白色的反射數值當作黑色軌跡路線的判斷標準

3.當第一台NXT在軌跡路線上(PID)運行時,如果超音波感應器遇到前方12公分內有物品時,車輛迴轉180度讓後方的爪子去抓取物品,抓取物品後車輛再迴轉180度繼續在軌跡路線上前進,此時顏色感應器啟動偵測路面上是否有顏色標示。

4.當顏色感應器偵測到特定顏色時開始執行「放置物品」的對應動作


綠色:「右轉」直行一段距離放在固定位置,此時第二台樂高機器人的超音波感應器或碰觸感應器被觸動執行抓取物品然後放在固定位置(預計放在黑色路線上讓第一台樂機器人繼續抓取),如此一直無線迴圈讓兩台樂高機器人一直運作。(外加動作是BT向手機傳遞訊息如顏色)

藍色:不動作

紅色:不動作

黃色:不動作


5.當遇到「綠色」標示時第一台NXT會右轉後前進一段距離放置物品再回到黑色軌跡路線上,此時第二台NXT機器手臂的超音波感應器或碰觸感應器會偵測到有物品需要抓取,因此啟用抓取程式讓NXT機器手臂移動物品放置。


硬體與軟體

硬體組裝:用樂高零件組裝結構性與強度足夠的「履帶車輛」與「機器手臂」。

軟體設計:按照測試流程來設計程式,第一台樂高NXT動作多程式比較複雜,第二台樂高機器手臂動作固定程式碼較少。

Lejos 是架構在 Java語言下因此程式結構與延伸完整,本次程式設計運用行為模式(Behavior)因此可以把軌跡車、超音波偵測、顏色偵測等各自撰寫獨立的Class測試,最後再定義各種動作(軌蹟車、超音波、顏色感應)的優先順序一起整合運用,擺除以往把3顆馬達、所有Sensor動作與變數檢查都寫在一起有時候都不知道是那個環節出錯( Debug花費時間比寫程式還多)。



DIY場地:各色膠帶 + 白色真珠板.....準備測試。




YouTube影片資料














2015年4月14日 星期二

Android手機方向感應器與樂高NXT配合應用

當手機有方向感應器(ORIENTATION)能分辨東西南北時便可以把資訊分享給樂高NXT機器人作為判斷方向的用途

首先要了解Android方向感應器的三個數值的定義eventvalue

方位角 Value[0]:0代表北方、90代表東方、270代表西方、180代表南方 .............共360度

南北向 Value[1]:南北旋轉向...手機水平螢幕朝上=(0)、手機直立=(-90)、手機倒立=(90)、手機水平螢幕朝下=(正負180)

東西向 Value[2]:東西向旋轉...手機水平螢幕朝上=(0)、手機向右翻(-90)、手機向左翻(90)、手機水平螢幕朝下=(0)

方向感應器與3軸加速器的不同處在於方向感應器有「方位角」因此能分辨東西南北。

2015年4月10日 星期五

Android 三軸加速感應器與 Lego NXT 應用

Android 手機裡面有個Accelerometer 加速感應器,它的作用就是能即時知道手機的移動或翻轉位置狀態如下圖所示

X軸為「左右翻轉」的加速度
Y軸為「前後翻轉」的加速度
Z軸為「上下移動」的加速度




利用這個特性我們就可控制 Lego NXT 在行走時能左右轉或是加速、煞車、前進、後退
X軸往左翻負值、往右翻正直--->控制車子左右轉                      value:-10<=X軸<=10
Y軸往前翻負值、往後翻正直--->控制車子往前加速 往後煞車 value:-10<=Y軸<=10
Z軸目前用不著






App執行後的步驟
1.把樂高NXT啟動、藍芽開啟並執行程式等待手機App的藍芽連結此時顯示「Waiting」。
2.按下App「藍芽連結」鈕如果找到樂高NXT並連結成功後會顯示「Connected」,樂高會把爪子打開後關閉一次代表連結成功。
3.啟動App讀取手機上的三軸加速器資料,並把資料傳給樂高NXT接收與執行對應動作
A.X軸在正負2的數值內保值前進或後退,超過正負2代表要樂高要左轉或右轉
B.Y軸在直立超過9時代表樂高NXT要停止不動,其餘時候是前進或後退













2015年4月9日 星期四

Android 手機透過藍芽與 NXT Lejos 應用

 NXT主機上有藍芽BlueTooth因此能傳送與接收其他裝置的藍芽資料,本篇要實做用一般常見的Android手機與Lego NXT主機透過藍芽做資料交換。

準備的物件
1.Android手機一隻新舊款沒關係但要有藍芽功能。
2.Lego Mindstorms NXT 或EV3,馬達3顆接 A、B、C Port
3.先將手機與NXT 的藍芽都打開,讓手機尋找NXT主機內定密碼為1234,連結成功後手機上就有這台NXT的資訊與名字作為手機App要連結的目標。

由於Android手機有很多Sensor如重力感應器、方向感應器、光線感應器、GPS等如能加以應用等於幫NXT省下不少外購Sensor費用。

如果大家有淘汰的Android手機可以試試看本篇的應用。

一部份開發 Android App程式:

我在App主畫面上放了 9個 Button以方便未來擴充應用

執行 App 程式的主畫面


顯示手機上 Sensor 資訊




原始碼如下:開發環境 Eclipse 與 Android、Lejos套件

package com.example.lego_android;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.graphics.Color;
import android.view.Window;
import android.widget.*;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.text.TextPaint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import java.util.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import android.content.*;


public class MainActivity extends Activity {

private  int MP=LinearLayout.LayoutParams.MATCH_PARENT;
private  int WC=LinearLayout.LayoutParams.WRAP_CONTENT;
private TableRow.LayoutParams view_layout2;
 private TextView txtID;
 private TextView txtSync;
 private TextView txtTitle01;
 private TextView txtTrending;
 private TextView txtTitle02;
 private String strTimeCnt="";
 private TextView txtVer;
 private boolean chkvername=false;
 private String strVer="";
 private int ifisze=18;
 private int iChnl=0;  
 private String strTitleH="LegoRun 資 訊 管 理";
 
 
   private BluetoothAdapter adapter;
private BluetoothSocket nxtSocket;
public DataInputStream nxtDataIn;
public DataOutputStream nxtDataOut;
   public final int MODE_CONTROL = 1;
   private int mode;
 
   private boolean bConnect_NXT=false;
   private String NXTUUID = "00001101-0000-1000-8000-00805F9B34FB";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

LinearLayout layout=new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);    
        layout.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
        layout.setBackgroundColor(Color.rgb(19,92,160));

        //標題隱藏
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(layout);
     
     
        TableLayout tl= new TableLayout(this);
        tl.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
        tl.setBackgroundColor(Color.parseColor("#333333"));
        tl.setStretchAllColumns(true);
        layout.addView(tl);  
     
 
        TableLayout.LayoutParams row_layout_mid = new TableLayout.LayoutParams();
        row_layout_mid.weight=1f;
        row_layout_mid.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);

        TableLayout.LayoutParams row_layout_max = new TableLayout.LayoutParams();
        row_layout_max.weight=8f;
        row_layout_max.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);

        TableLayout.LayoutParams row_layout_min = new TableLayout.LayoutParams();
        row_layout_min.weight=0.2f;
        row_layout_min.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);

        TableRow.LayoutParams view_layout1 = new TableRow.LayoutParams(MP,MP);
        view_layout1.span=2;

        view_layout2 = new TableRow.LayoutParams(getScrWH(MainActivity.this)[0]/2,MP);
        view_layout2.setMargins(1,0,0,0);

        TableRow tr1 = new TableRow(this);
        tr1.setLayoutParams(row_layout_mid);
        tr1.setGravity(Gravity.CENTER_HORIZONTAL);
        txtTitle01 = new TextView(this);
        txtTitle01.setLayoutParams(view_layout1);
        txtTitle01.setText(strTitleH);
        txtTitle01.setBackgroundColor(Color.parseColor("#00C9C9"));  //#46C7C7
        txtTitle01.setGravity(Gravity.CENTER);
        TextPaint tp = txtTitle01.getPaint();
        tp.setFakeBoldText(true);
        txtTitle01.setTextColor(Color.parseColor("#EEEEEE"));
        txtTitle01.setOnClickListener(new ButtonClickListener());
        txtTitle01.setTextSize(ifisze);
        txtTitle01.setTag("txtTitle01");
        tr1.addView(txtTitle01);
        tl.addView(tr1);

        TableRow tr2 = new TableRow(this);
        tr2.setLayoutParams(row_layout_mid);
        tr2.setGravity(Gravity.CENTER_HORIZONTAL);
        txtTitle02 = new TextView(this);
     
     
        txtTitle02.setText("尚未連結Lego NXT...");
     
        txtTitle02.setTextSize(18);
        txtTitle02.setLayoutParams(view_layout1);
        txtTitle02.setGravity(Gravity.CENTER);
        txtTitle02.setBackgroundColor(Color.WHITE);
        tr2.addView(txtTitle02);
        tl.addView(tr2);


        TableRow tr3 = new TableRow(this);
        tr3.setLayoutParams(row_layout_max);
        tr3.setGravity(Gravity.CENTER_HORIZONTAL);
        tl.addView(tr3);

        LinearLayout layoutBrick=new LinearLayout(this);
        layoutBrick.setOrientation(LinearLayout.VERTICAL);
        layoutBrick.setLayoutParams(view_layout1);
        layoutBrick.setBackgroundColor(Color.parseColor("#95B9C7"));
        tr3.addView(layoutBrick);


        TableLayout tl_Brick= new TableLayout(this);
        tl_Brick.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
        tl_Brick.setBackgroundColor(Color.parseColor("#FFFFFF"));
        tl_Brick.setStretchAllColumns(true);
        layoutBrick.addView(tl_Brick);

        TableLayout.LayoutParams row_layout_Brick = new TableLayout.LayoutParams();
        row_layout_Brick.weight=1f;
   
        TableRow.LayoutParams view_layout_Brick = new TableRow.LayoutParams(MP,MP);
        view_layout_Brick.weight=0.7f;
 
     
        int ibtn=0;
        for(int i=0;i<=2;i++){

            TableRow tr_Brick = new TableRow(this);
            tr_Brick.setLayoutParams(row_layout_Brick);
            tr_Brick.setGravity(Gravity.CENTER_HORIZONTAL);
            tl_Brick.addView(tr_Brick);

            for(int j=0;j<=2;j++){
                Button btn_Brick = new Button(this);
                btn_Brick.setLayoutParams(view_layout_Brick);              
                btn_setup(btn_Brick,ibtn); //設定按鈕外觀圖形
                btn_Brick.setOnClickListener(new ButtonClickListener());
             
                tr_Brick.addView(btn_Brick);
                ibtn++;
            }

        }



        TableRow tr4 = new TableRow(this);
        tr4.setLayoutParams(row_layout_min);
        tr4.setGravity(Gravity.CENTER_HORIZONTAL);

        txtTrending = new TextView(this);
        txtTrending.setText("資料設定");
        txtTrending.setTextSize(18);
        txtTrending.setLayoutParams(view_layout1);
        txtTrending.setGravity(Gravity.CENTER);
        txtTrending.setTag("txtTrending");
        txtTrending.setOnClickListener(new ButtonClickListener());
        txtTrending.setBackgroundColor(Color.parseColor("#E5E4E2"));
        tr4.addView(txtTrending);
        tl.addView(tr4);


        //-------------------------------------------
        TableRow tr5 = new TableRow(this);
        tr5.setLayoutParams(row_layout_mid);
        tr5.setGravity(Gravity.CENTER_HORIZONTAL);

        txtID = new TextView(this);
        setTxtView(txtID,"帳 號 設 定","txtID");
        tr5.addView(txtID);

        txtSync = new TextView(this);
        setTxtView(txtSync,"資 料 更 新","txtSync");
        tr5.addView(txtSync);

        tl.addView(tr5);

        //----------------------------------------------
        TableRow tr6 = new TableRow(this);
        tr6.setLayoutParams(row_layout_mid);
        tr6.setGravity(Gravity.CENTER_HORIZONTAL);

        TextView txtHelp = new TextView(this);
        setTxtView(txtHelp,"使 用 說 明","txtHelp");
        tr6.addView(txtHelp);

        txtVer = new TextView(this);
        setTxtView(txtVer,"版 本 資 訊","txtVer");
        tr6.addView(txtVer);
        tl.addView(tr6);
     
     
     
        //藍芽啟用
        adapter = BluetoothAdapter.getDefaultAdapter();
        if(adapter==null)
        {
        Toast.makeText(getApplication(), "No Bluetooth adapter found", Toast.LENGTH_SHORT).show();
        this.finish();
        }
     
    if(!adapter.isEnabled())
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
   
   


}




@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

public int[] getScrWH(Activity activity){
        int scrWH[]={0,0};
        DisplayMetrics dm = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
        scrWH[0] = dm.widthPixels;
        scrWH[1] = dm.heightPixels;
        return scrWH;
    }


//按鈕事件
class ButtonClickListener implements OnClickListener  {
        // onClick 方法(觸碰按鈕時的事件處理器)
        public void onClick(View v){
            //取得 tag
            String tag = (String)v.getTag();

            if (tag.equals("btn0")){
           
            if(((Button)v).getText().equals("藍芽連結")){
           
            txtTitle02.setText("尋找藍芽中...");            
            //開啟藍芽
            if(connectNxt())
            ((Button)v).setText("關閉藍芽");
            }else{
            try {
            txtTitle02.setText("傳送功能 = 關閉藍芽連結");
    nxtDataOut.writeInt(999);
    nxtDataOut.flush();            
            nxtSocket.close();
            bConnect_NXT=false;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            ((Button)v).setText("藍芽連結");
            }
           
            }
         
         
            //傳送資料
            if (tag.equals("btn1")){
            try {
           
            if(((Button)v).getText().equals("啟動馬達A")){            
            txtTitle02.setText("傳送功能 = 啟動馬達A");
            nxtDataOut.writeInt(0);
            nxtDataOut.flush();
            ((Button)v).setText("關閉馬達A");
            }else{
            txtTitle02.setText("傳送功能 = 關閉馬達A");
            nxtDataOut.writeInt(1);
            nxtDataOut.flush();
            ((Button)v).setText("啟動馬達A");
            }
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}        
           
            }
         
            if (tag.equals("btn2")){
            try {
            if(((Button)v).getText().equals("啟動馬達B")){            
            txtTitle02.setText("傳送功能 = 啟動馬達B");
            nxtDataOut.writeInt(2);
            nxtDataOut.flush();
            ((Button)v).setText("關閉馬達B");
            }else{
            txtTitle02.setText("傳送功能 = 關閉馬達B");
            nxtDataOut.writeInt(3);
            nxtDataOut.flush();
            ((Button)v).setText("啟動馬達B");
            }
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}        
           
            }
         
         
            if (tag.equals("btn3")){
            try {
            if(((Button)v).getText().equals("啟動馬達C")){            
            txtTitle02.setText("傳送功能 = 啟動馬達C");
            nxtDataOut.writeInt(4);
            nxtDataOut.flush();
            ((Button)v).setText("關閉馬達C");
            }else{
            txtTitle02.setText("傳送功能 = 關閉馬達C");
            nxtDataOut.writeInt(5);
            nxtDataOut.flush();
            ((Button)v).setText("啟動馬達C");
            }

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}        
           
            }
         
         
        }

    }

private boolean connectNxt()
    {
    if(bConnect_NXT){
    txtTitle02.setText("手機已經連結成功不須再次連結");    
    return false;
    }
   
    String name="NXT8547";
    BluetoothDevice nxt = null;
   
   
   
        Set devicesSet = adapter.getBondedDevices(); 
     
        if(devicesSet.size()==0)
        {        
        txtTitle02.setText("手機上無任何藍芽連結裝置");
        return false;
        }
     
        for (BluetoothDevice device : devicesSet)
        {
            if (device.getName().equals(name))
            {
            nxt = device;
                break;
            }
        }
     
        if(nxt==null)
        {        
        txtTitle02.setText("找不到Lego NXT name:"+name);
        return false;
        }
     
        try
        {        
nxtSocket = nxt.createRfcommSocketToServiceRecord(UUID.fromString(NXTUUID));
nxtSocket.connect();
nxtDataOut = new DataOutputStream(nxtSocket.getOutputStream());
nxtDataIn = new DataInputStream(nxtSocket.getInputStream());
}
        catch(IOException e)
{
       
        txtTitle02.setText("連結此裝置:"+name+" 失敗!!");
        return false;
}
   
        txtTitle02.setText("連結此裝置:"+name+" 成功!!");
    bConnect_NXT=true;
    return true;


    }

private void setTxtView(TextView v,String strText,String strTag){
        v.setText(strText);
        v.setTextSize(ifisze);
        v.setLayoutParams(view_layout2);
        v.setGravity(Gravity.CENTER);
        v.setBackgroundColor(Color.parseColor("#F3F3F3"));
        v.setTag(strTag);
        v.setOnClickListener(new ButtonClickListener());
    }

private void btn_setup(Button btn_Brick,int ibtn){
        Context context=getApplicationContext();    


        Drawable[][] image=new Drawable[9][2];
        image[0][0]=context.getResources().getDrawable(R.drawable.net_42_128);
        image[0][1]=context.getResources().getDrawable(R.drawable.net_42_72);

        image[1][0]=context.getResources().getDrawable(R.drawable.net_24_128);
        image[1][1]=context.getResources().getDrawable(R.drawable.net_24_72);

        image[2][0]=context.getResources().getDrawable(R.drawable.net_18_128);
        image[2][1]=context.getResources().getDrawable(R.drawable.net_18_72);

        image[3][0]=context.getResources().getDrawable(R.drawable.net_08_128);
        image[3][1]=context.getResources().getDrawable(R.drawable.net_08_72);

        image[4][0]=context.getResources().getDrawable(R.drawable.net_21_128);
        image[4][1]=context.getResources().getDrawable(R.drawable.net_21_72);

        image[5][0]=context.getResources().getDrawable(R.drawable.net_27_128);
        image[5][1]=context.getResources().getDrawable(R.drawable.net_27_72);

        image[6][0]=context.getResources().getDrawable(R.drawable.net_22_128);
        image[6][1]=context.getResources().getDrawable(R.drawable.net_22_72);

        image[7][0]=context.getResources().getDrawable(R.drawable.net_01);
        image[7][1]=context.getResources().getDrawable(R.drawable.net_23_72);

        image[8][0]=context.getResources().getDrawable(R.drawable.net_26_128);
        image[8][1]=context.getResources().getDrawable(R.drawable.net_26_72);





        String[][] strBtn=new String[9][2];
        strBtn[0][0]="藍芽連結";
        strBtn[0][1]="btn0";
        strBtn[1][0]="啟動馬達A";
        strBtn[1][1]="btn1";
        strBtn[2][0]="啟動馬達B";
        strBtn[2][1]="btn2";
        strBtn[3][0]="啟動馬達C";
        strBtn[3][1]="btn3";

        strBtn[4][0]="啟動光線";
        strBtn[4][1]="btn6";
        strBtn[5][0]="Fun05";
        strBtn[5][1]="btn7";

        strBtn[6][0]="Fun06";
        strBtn[6][1]="btn4";
        strBtn[7][0]="Fun07";
        strBtn[7][1]="btn5";
        strBtn[8][0]="Fun08";
        strBtn[8][1]="btn8";
     
     


     

        btn_Brick.setText(strBtn[ibtn][0]);
        btn_Brick.setTextSize(ifisze);
        btn_Brick.setTag(strBtn[ibtn][1]);
        //-------------
        int spacing=0;
        int width=btn_Brick.getMeasuredWidth();
        int height=btn_Brick.getMeasuredHeight();
        int txt_width=(int)(btn_Brick.getTextSize()*btn_Brick.getText().length());
        int txt_height=(int)(btn_Brick.getLineCount()*btn_Brick.getLineHeight());

        int img_width=0;
        int img_height=0;
        int content_height=0;
        int content_width=0;
        int padding_w=0;
        int padding_h=0;
        int isel=0;

        if(getScrWH(this)[0]>=720){
            img_width=image[ibtn][0].getIntrinsicWidth();
            img_height=image[ibtn][0].getIntrinsicHeight();
            isel=0;
        }else{
            img_width=image[ibtn][1].getIntrinsicWidth();
            img_height=image[ibtn][1].getIntrinsicHeight();
            isel=1;
        }
        content_height=txt_height+img_height+spacing;
        content_width=txt_width+img_width+spacing;
        padding_w=width/2-content_width/2;
        padding_h=height/2-content_height/2;
        btn_Brick.setCompoundDrawablesWithIntrinsicBounds( null,image[ibtn][isel], null, null);
        ////setMargins(left, top, right, bottom);
        //btn_Brick.setPadding(0,padding_h,0,0);
        btn_Brick.setCompoundDrawablePadding(padding_h/2);
        //----------------

    }

}


第二部份開發Lejos NXT 程式

import lejos.nxt.Button;
import lejos.nxt.ButtonListener;
import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.nxt.comm.BTConnection;
import lejos.nxt.comm.Bluetooth;

import java.io.DataInputStream;
import java.io.DataOutputStream;

import lejos.nxt.comm.*;
import lejos.util.Delay;


public class BT_Frm01 {

public static void main(String[] args)throws Exception  {
// TODO Auto-generated method stub

Button.ESCAPE.addButtonListener(new ButtonListener(){
public void buttonPressed(Button b){System.exit(1);}
public void buttonReleased(Button b){}
});


LCD.drawString("Waiting",0,0);
BTConnection btc = Bluetooth.waitForConnection(0, NXTConnection.RAW);
DataInputStream din = btc.openDataInputStream();
DataOutputStream dout = btc.openDataOutputStream();
LCD.drawString("Connected",0,0);

Motor.A.setSpeed(450);
Motor.B.setSpeed(450);
Motor.C.setSpeed(450);

while(true){
int iData=din.readInt();
LCD.clear();
LCD.drawString("value="+String.valueOf(iData),0,2);


if(iData==999){
btc.close();
System.exit(1);
break;
}

switch(iData){
case 0:
Motor.A.forward();
break;
case 1:
Motor.A.stop();
break;
case 2:
Motor.B.forward();
break;
case 3:
Motor.B.stop();
break;
case 4:
Motor.C.forward();
break;
case 5:
Motor.C.stop();
break;

}



Delay.msDelay(1000);

}


}

}


除了手機可跟NTX互動溝通外不同的NXT主機也可以互動,這是我用第一台NXT主機當控制器透過BT遙控第二台NXT車輛的影片,第一隻馬達控制前輪方向動作、第二隻馬達控制後輪前進後退動作




2015年3月31日 星期二

LEGO NXT 網路影片分享

LEGO NXT  網路影片分享


這簡直要膜拜Orz....太強了!!









各式各樣的軌蹟車與跑法值得參考。




WRO 國小組題目....厲害!!

2015年3月23日 星期一

Lejos 重要的行為類別 Lejos.subsumption.Behavior

Lejos.subsumption.Behavior  行為類別

如果NXT接了TouchSensor、LightSensor、UltraSonic偵測設備,而我們必須根據這三種設備回傳的資料做判斷,在傳統程式while(true){...}必須一直做迴圈判斷以避免這三種資料互相衝突。

例如TouchSensor要倒退20公分、UltraSonic測到距離範圍是要前進,這時NXT是要前進還是後退呢???有時候就會看到NXT一下前進一下後退。這是因為這三種回傳資料沒優先順序,通常我們會加入許多判斷旗號True、False來輔助,不過實際會發現有些旗號不會如你預定的流程走該True的時候竟然還是False,這種經驗相信很多人都曾有過。

而Lejos在類別Class已經加入了行為模式Behavior  讓我們應用,因為行為模式讓我們自己定義優先順序因此在程式撰寫時就不會雜亂無章,TouchSensor與UltraSonic一定要定義誰先誰後這樣一來程式就比較好維護,NXT在執行時也不會亂成一團。




Interface Behavior 屬於界面因此有3個必須實做的方法(Method)

1.
boolean takeControl():如果該定義行為被啟動時傳回true
例如TouchSensor被碰觸時就會回傳true

Returns a boolean value to indicate if this behavior should become active. For example, if a touch sensor indicates the robot has bumped into an object, this method should return true. This method should return quickly, not perform a long calculation.

2.
void action() :被啟用時的需要執行動作
例如TouchSensor被碰觸時需要後退Motor.A.backward 並轉向

The code in this method begins performing its task when the behavior becomes active. For example, if takeControl() detects the robot has collided with an object, the action() code could make the robot back up and turn away from the object.

A behavior is active as long as its action() method is running, so the action() method should exit when its task is complete. Also, the action() method should exit promptly when suppress() is called. When it exits, it should leave the robot in a safe state for the next behavior.

3.
void suppress() :
仲裁者(Arbitrator) 呼叫 suppres()時會立刻停止 void action()的執行動作

The code in the suppress() method should immediately terminate the code running in the action() method. It also should exit quickly.

仲裁者(Arbitrator)依行動者(behaviors)陣列索引由大至小來進行,因此那個行為最高優先的要放最後面。
仲裁者(Arbitrator)先檢查boolean takeControl()是否回傳Ture,接著執行void action(),這期間如果有其他更高優先權的行為發生時仲裁者(Arbitrator)會執行現在行為的void suppress() ,因此程式在執行時必須要注意void suppress()的變化來決定是否要停止或繼續執行。


因此有上述三種行為定義時就可以讓我們開發程式時能適當的使用


Lejos官網上的範例程式如下
準備NXT+2顆馬達+LightSensor
LightSensor接Port1
馬達接B、C

package org.lejos.sample.linefollower;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.LightSensor;
import lejos.nxt.SensorPort;
import lejos.robotics.RegulatedMotor;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.navigation.RotateMoveController;
import lejos.robotics.subsumption.Arbitrator;
import lejos.robotics.subsumption.Behavior;
import lejos.util.PilotProps;

/**
 * Demonstration of use of the Behavior and Pilot classes to
 * implement a simple line following robot.
 *
 * Requires a wheeled vehicle with two independently controlled
 * wheels with motors connected to motor ports A and C, and a light
 * sensor mounted forwards and pointing down, connected to sensor port 1.
 *
 * Press ENTER to start the robot.
 *
 * You can run the PilotParams sample to create a property file which
 * sets the parameters of the Pilot to the dimensions
 * and motor connections for your robot.
 *
 * @author Lawrie Griffiths
 *
 */
public class LineFollower {

public static void main (String[] aArg)
throws Exception
{
      PilotProps pp = new PilotProps();
    pp.loadPersistentValues();
    float wheelDiameter = Float.parseFloat(pp.getProperty(PilotProps.KEY_WHEELDIAMETER, "4.96"));
    float trackWidth = Float.parseFloat(pp.getProperty(PilotProps.KEY_TRACKWIDTH, "13.0"));
    RegulatedMotor leftMotor = PilotProps.getMotor(pp.getProperty(PilotProps.KEY_LEFTMOTOR, "B"));
    RegulatedMotor rightMotor = PilotProps.getMotor(pp.getProperty(PilotProps.KEY_RIGHTMOTOR, "C"));
    boolean reverse = Boolean.parseBoolean(pp.getProperty(PilotProps.KEY_REVERSE,"false"));
   
// Change last parameter of Pilot to specify on which
// direction you want to be "forward" for your vehicle.
// The wheel and axle dimension parameters should be
// set for your robot, but are not critical.
final RotateMoveController pilot = new DifferentialPilot(wheelDiameter, trackWidth, leftMotor, rightMotor, reverse);
final LightSensor light = new LightSensor(SensorPort.S1);
        pilot.setRotateSpeed(180);
        /**
         * this behavior wants to take control when the light sensor sees the line
         */
Behavior DriveForward = new Behavior()
{
public boolean takeControl() {
return light.readValue() <= 40;
}

public void suppress() {
pilot.stop();
}
public void action() {
pilot.forward();
                while(light.readValue() <= 40)
                Thread.yield(); //action complete when not on line
}
};

Behavior OffLine = new Behavior()
{
private boolean suppress = false;

public boolean takeControl() {
return light.readValue() > 40;
}

public void suppress() {
suppress = true;
}

public void action() {
int sweep = 10;
while (!suppress) {
pilot.rotate(sweep,true);
while (!suppress && pilot.isMoving())
Thread.yield();
sweep *= -2;
}
pilot.stop();
suppress = false;
}
};

Behavior[] bArray = {OffLine, DriveForward};
        LCD.drawString("Line ", 0, 1);
        Button.waitForAnyPress();
   (new Arbitrator(bArray)).start();
}
}