2010年8月7日 星期六

機器人程式設計與實作─使用JAVA

http://4rdp.blogspot.com/2010/08/java.html

一本書的誕生不是一件容易的事,作者阿吉與祥瑞不知歷經多少日夜努力,終於完成這本經典著作,它是指導讀者使用 Java 應用於 NXT 的第一本中文書,無論對 CAVE 或是 NXT 玩家都是重要的里程碑,有了之前機器人新世界 NXC 與 NXT 的發行經驗,讓他們不敢輕忽校稿的重要,把文件一次又一次的 review,甚至部份內容全部改寫,連書名、書皮都改版多次(請詳見 CAVE's Blog),由此可見他們用心程度。

今年三月二十日,春分的前一天,阿吉來信附上 Ch10"多執行緒與事件"、Ch14"藍芽遙控車"、 Ch15"遠端遙控" 三個章節,因為有許多專業術語,深怕誤導讀者,請求提供意見,從此開始受邀參與這個歷程,我很榮幸能夠幫他們校稿以及內容更正,前後將近一週的時間 e-mail 飛來飛去,才搞定這三章。接著五月十日開始第一校,六月十一日第二校,我回覆意見後,他們這些天與出版社的人,應該是忙的人仰馬翻,很高興七月二十五日終於正式出版,付出努力都會有收穫的!

機器人程式設計與實作-使用Java碁峰資訊出版/CAVE 教育團

昨天晚上與兩位作者碰面聚餐,
祥瑞是建中的機器人研究社社長,對程式設計有濃厚興趣,在阿吉老師的帶領下,高一升高二的暑假前,完成這本巨著真是厲害,現在希望能吸收其它同學加入,如果有建中同學看到這裡,並對機器人有興趣就趕快加入吧。從我開立研發養成所部落格之後,因為 NXT 關係有幸認識阿吉,從他構想 CAVE 到現在出版書籍,以及尋找志同道合之士正式成立公司,看他克服萬難逐步完成自己的理想真是令人欽佩,現在是事業草創初期有很多事要做,接下來的營運計畫,將是穩健中求發展,朝向中高階應用教育方向努力。

接下來我要好好介紹 Java ,讓大家了解 Java 與其它程式語言的差異以及應用的時機。我第一次接觸 Java 這個語言差不多是在 2002 年,那時候工作需要寫一些網頁,但是這些不是簡單的網頁,而是動態變化的,也就是點選網頁上的小圖塊後,畫面會展開或是收合,有點像是 Windows 檔案總管左邊的樹狀目錄結構,主要為了資料分類瀏覽。因為它不是靜態網頁,所以需要在網頁內嵌入程式控制,JavaScript 是一種選擇,可以讓網頁設計師設計出動態的互動網頁。

之所以會有 Java 系統被設計出來,主要是希望讓程式設計師只要寫一次程式,就可以在各種不同的計算機系統執行,不必再更動改寫程式。為了達到這樣的目的,不同的作業系統,就需要有專業的系統設計師開發出專屬的 Java 系統平台給大家用,沒有這些專業高手在前面開山闢路,我們這些後輩就沒有這些簡單易用的系統平台,來實踐自己的專案構想,簡單講,現代科技就是建構在前人的知識技術之上,再發展下一代更方便的科技,在此感謝 leJOS 的創始人 Juan Antonio 及其它工程師們提供免費的 Java 系統給 NXT 玩家們使用。

一個計算機系統被設計出來後,通常會有 ABC 三種程式語言也同時被開發出來給其它應用程式工程師使用,Assembly (組合語言)BASICC 語言,組合語言一定會有,它是給計算機系統第一批系統設計工程師使用的語言,所有的基礎程式庫都是由組合語言建出來的,高階程式設計師應該沒什麼機會摸。B、C 語言系統算是二線系統,它們都是利用組合語言再架構起來,經過編譯後論程式大小以及執行效能,都不如直接使用組合語言設計來的好,即使程式編譯設定了最佳化,也沒辦法完完全全最佳化,那為什麼不使用組合語言就好呢?因為組合語言非常難懂,像是古文的文言文不夠白話,有在玩 NXT 的玩家,有幾個人用 Next Byte Codes (NBC) 開發程式呢?我認為屈指可數,甚至沒有人想用,不諱言我就是其中之一,可是它是 NXC 重要的基石,少了組合語言 C 也不會存在,日後為各位解釋編譯程式(Compiler)原理,到時候您就會明白。

Java 的 JRE (Java Runtine Environment) 開發,一般都是先有 C 語言之後再利用它開發,才會節省人力時間,因此它會是三線系統,程式大小沒特別比較過,不敢斷言,但是執行效率應該比 C 差一點,因為你寫的 Java 程式必須透過 JRE 執行,而不是直接在 CPU 上跑。談到這裡,並不是要各位不要用 Java,相反的,聰明的讀者應該看出一個趨勢:如果你是一般的應用程式設計師,學好 Java 無論任何系統平台,你都可以很快設計出程式,因為現在 CPU 速度很快、記憶體空間超足夠,幾乎感覺不到程式執行速度不夠快,這樣子電腦程式會寫,手機程式也會寫,很有成就感,未來找一般應用軟體設計工作很好求職。通常 C 語言對需要設計電子硬體驅動程式的工程師,比較實用,甚至還要懂組合語言!

最後,很感謝阿吉與祥瑞分享出這麼棒的著作,我也透過他們學到 leJOS 精髓,如果你有不錯的著作,請不吝拿出來與大家分享。

31 則留言:

  1. 以前java剛流行時,就因為專案需要,寫了好幾年的java,為了說服各路人馬,那時常掛在嘴邊的就是,「寫一次,到處都可執行」“Write once,run anywhere”,不過直到接觸LLVM後,發覺現今compiler技術的進步,不只讓各程式語言可以“Write once,run anywhere”而且更小效率更好,
    期待nxt的程式開發也引進LLVM

    回覆刪除
  2. Jimmy 爸爸您好,

    謝謝您提供資訊,LLVM 需要專家開路,您的網誌設計不錯,歡迎您加入 LEGO NXT 行列。

    回覆刪除
  3. 請問一個問題,http://lejos.sourceforge.net/forum/viewtopic.php?f=6&t=1457&start=15&hilit=Wii+Nunchuk+Sensor
    在這個網頁中使用了Wii的雙截棍製作了一個感應器,我也製作了一個,但我使用原作者的NXC程式碼可以成功顯示數值在螢幕上NXC: http://www.mindstormsforum.de/viewtopic.php?t=3828
    (圖片已遺失,程式碼在中間)
    但我不知道該如何把(下)網頁中的兩個代碼做結合。 http://lejos.sourceforge.net/forum/viewtopic.php?f=6&t=1457&start=15&hilit=Wii+Nunchuk+Sensor
    請求您的協助,謝謝!

    回覆刪除
  4. 翔您好,

    恭喜你已經成功改製 Wii 雙截棍感應器可以連接NXT感應器接口,聽你描述目前NXC程式可以正常執行,只是不明白為什麼,你還要LeJos Java做什麼?

    NXC程式只能在NXC改裝的OS下執行,NXT Java程式只能在LeJos下執行,兩者程式碼無法混用。

    回覆刪除
  5. 因為我已經會寫lejos 傳數值到pc 但我不知道該如何使用nxc回傳數值到pc
    (偷偷告訴您,其實我要把雙截棍的數值透過藍牙回傳到pc然後再用pc端的程式控制滑鼠的座標。也就是無線藍牙雙截棍滑鼠的拉,哈!哈!哈!)
    可是我不會使用nxc回傳數值到pc,所以...才來請求您的協助,拜託拉>.< 教教我拉 (用vb唷^^)
    非常感謝! (成功之後,我在將超詳細的製作過程放在網至上給您閱讀唷~ ^()^)

    回覆刪除
  6. 翔您好,

    現在明瞭你想做甚麼,我會與你討論,幫你釐清問題提供方向,但不會直接給你程式碼,就當你自己是研究所學生,自己找論文專題研究,老師從旁協助,如果你可以這樣解決問題,未來必是厲害的研發工程師。

    假設你有兩隻 NXT,一隻裝有 LeJos 另一隻為 NXC OS,這樣才方便同時比較。你說已經會寫 LeJos 傳數值到 PC,應該是指會寫 NXT Java 在 LeJos 上執行,PC 端也是用 Java 設計藍牙讀取程式,那你可以從這個基礎開始變更。

    藍牙資料傳遞格式維持不變,PC 端使用原來 Java 程式接收數值,
    (1)NXT 端嘗試改成 NXC 藍牙傳值,
    (2)PC 端再改成 VB 藍牙接收數值,
    (3)PC 端程式接收後,控制滑鼠座標。

    看你哪一個步驟有問題,我們再討論。

    回覆刪除
  7. 是的!我會在電腦上使用JAVA接收資料,但Lejos沒辦法連接到 雙截棍 。
    所以現在只能使用nxc了,我上網找了很久,nxc傳資料到電腦的範例幾乎沒有,我知道天底下沒有白吃得午餐,但可以請您指導一下方向嗎?
    現在的狀況是:
    1.lejos 沒辦法連接到 雙截棍,所以不能使用。
    2.nxc如何傳送資料,可以請您給個範例嗎?
    (是用SendRemoteString(BT_CONN,OUTBOX,"Lego NXT"); 這樣嗎? 還是用其他的?)
    謝謝!!

    回覆刪除
  8. 翔您好,

    你所列的NXC指令 SendRemoteString(BT_CONN,OUTBOX ,"Lego NXT") 可以使用,
    請注意,BT_CONN = 0,因為與 PC 相連,
    想傳送字串,有兩個方案選擇其一,
    —、固定長度字串
    二、不固定長度字串,在字串後面再加 LineFeed 字元 0x0A。VB可以使用ReadLine方法讀取。

    回覆刪除
  9. 您好
    那 SendRemoteString(0,OUTBOX ,"Lego NXT") 要怎麼用vb接收呢?(ReadLine?)

    我要傳送的資料是固定的字串長度,也可以用數字代替,我稍微測試過,我覺得搭配 http://www.mindstormsforum.de/viewtopic.php?t=3828
    (圖片已遺失,程式碼在中間) 中的nxc程式碼好像會lag...

    用java當作電腦接收端的程式也可以,只是我不知道該如何使用java接收nxc的資料...
    請求您的幫忙!
    謝謝!

    回覆刪除
  10. 翔您好,

    今天正好有空測驗 NXT,我使用 NXC SendRemoteString(0,OUTBOX2 ,"Lego NXT\n") ,用 VB ReadLine 接收資料,原先認為應該可以發現不行,原因為字串中包含特殊字元,而無法正常讀取,只能一個一個字元讀取,因此改建議用 ReadByte 指令,如果你要用 PC Java 程式接收資料也一樣。字串中 \n 為 0x0A 結尾碼。

    NXC 資料回傳 PC 格式與 PC 傳資料給 NXT 格式是—致的,可參考 http://4rdp.blogspot.com/2009/05/vb-communication-with-nxt-over.html 一文,
    以上例說明

    byteIn(0) = 9 + 5 '9 bytes in input message
    byteIn(1) = &H0 'should be 0 for NXT
    byteIn(2) = &H80 'Direct Command no reply expected
    byteIn(3) = &H9 'MESSAGEWRITE
    byteIn(4) = &H1 'Box Number - 1 (BOX2)
    byteIn(5) = 9 + 1 '9-byte message size with null terminator
    byteIn(6) = Asc("L") ' L ASCII code
    byteIn(7) = Asc("e") ' e ASCII code
    byteIn(8) = Asc("g") ' g ASCII code
    byteIn(9) = Asc("o") ' o ASCII code
    byteIn(10) = Asc(" ") ' space ASCII code
    byteIn(11) = Asc("N") ' N ASCII code
    byteIn(12) = Asc("X") ' X ASCII code
    byteIn(13) = Asc("T") ' T ASCII code
    byteIn(14) = &HA ' Line Feed
    byteIn(15) = &H0 'null terminator

    只要搞清楚資料格式,無論哪一種語言寫,都可以與 NXT 互相通訊。

    回覆刪除
  11. 您好! 我已經成功了,我用nxc 傳送byte給java接收,現在已經可以控制滑鼠囉! 真是非常非常感謝您最近的指導,現在功課比較忙,等假日我在PO製作過程和程式碼在我的痞客邦!(http://lucky861223.pixnet.net/blog)
    非常感謝您!!

    回覆刪除
  12. 翔您好,

    很高興你已經試驗出 NXT 傳出資料,PC 接收資料的方法,我沒幫甚麼忙,重要的是自己的努力,非常期待你的作品分享。^_^

    回覆刪除
  13. 想請問一下
    要怎麼把NXT的中的char用USB傳到PC中??
    試了很久都沒辦法接收到從NXT傳出的數值
    可否幫我解答
    謝謝

    回覆刪除
  14. Yeh 您好,

    由於對你所使用系統一無所知,無法精確答覆你的提問,請你進一步說明NXT端是使用NXC、NXT-G或LeJOS?PC端使用LabView、Java或其它語言?

    不過有一點可以確知PC與NXT之間透過USB通訊,所使用Protocol是Direct Command,你的資料格式符合嗎?

    回覆刪除
  15. 我使用的是leJos跟JAVA來寫
    PC端的程式
    DataInputStream dis = new DataInputStream(connector.getInputStream());
    int b;
    try {
    b=dis.readInt();
    System.out.println(b);
    dis.close();
    }
    catch (IOException e) {
    catch block
    e.printStackTrace();
    System.out.println(e);
    }
    NXT端的程式
    NXTConnection connection = null;
    connection=USB.waitForConnection();
    DataOutputStream dataOut = connection.openDataOutputStream();
    int a=123;
    try {
    dataOut.write(a);
    LCD.drawString("writing....", 0,2 );
    dataOut.flush();
    dataOut.close();
    }
    catch (IOException e ) {
    LCD.drawString(" write error "+e, 0,5 );
    }
    把int 123 用USB傳到PC接收
    但在PC端會出現
    at java.io.DataInputStream.readInt(Unknown Source)


    找不出問題
    求解答謝謝!!

    回覆刪除
  16. Dear Yeh,

    問題應該在PC端的程式,如果你有CAVE這本JAVA的書,那就可以使用它的範例程式。

    你需要
    import lejos.pc.comm.*;
    程式中需要建立物件
    NXTConnector conn = new NXTConnector();
    連線到NXT
    conn.connectTo("","", NXTCommFactory.USB);
    取得輸入串流
    DataInputStream dataIn = conn. getDataIn();
    最後就可以讀入
    b = dataIn.readInt();

    有問題再討論。

    回覆刪除
  17. import java.io.*;
    import lejos.pc.comm.*;
    import lejos.util.Delay;
    public class BT_TEST {

    public static void main(String[] args)
    {
    NXTConnector connector = new NXTConnector();
    connector.connectTo("","", NXTCommFactory.USB);

    DataInputStream dis = new DataInputStream(connector.getInputStream());

    DataInputStream dataIn = connector. getDataIn();
    int b;


    try {

    b=dis.readInt();
    System.out.println(b);
    dis.close();
    }
    catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    System.out.println(e);
    }


    }
    }
    這是我的程式
    但是getDataIn這個指令錯誤
    顯示錯誤說
    The method getDataIn() is undefined for the type NXTConnector
    幫忙解答一下
    謝謝

    回覆刪除
  18. Dear Yeh,

    問題在空格
    DataInputStream dataIn = connector._getDataIn();

    以底線代表空格,請將空格去除

    回覆刪除
  19. Dear Yeh,

    很高興你的問題解決了,最近忙沒時間寫程式幫你測試,希望你能從這個專題學習解決問題的方法,只是好奇多數人採用藍牙連線,為何你需要USB?

    回覆刪除
  20. 因為需要用道雙向傳輸
    藍芽只能單向
    所以放棄使用藍芽來做傳輸

    回覆刪除
  21. Dear Yeh,

    藍牙傳輸是可以雙向傳輸的,這方面值得你進一步研究。

    回覆刪除
  22. 不好意思...想要請問一下
    因為目前沒辦法將VISUAL 2010與BCC一起同步執行
    所以我用BCC裡面的CreateFile來創建一個TXT檔案
    然後想要再使用VISUAL從檔案裡面讀取COMPASS的資料

    但是我所創建出來的檔案都必須透過NXT EXPLORER 才可以把檔案抓到自己的PC上面

    我想問有沒有辦法可以讓檔案直接創建再PC上面

    回覆刪除
  23. 彥丞您好,

    BCC (Bricx Command Center)是 一個整合式程式開發介面應用程 式,主要是用來撰寫及編譯NXC之 類程式碼於NXT上執行。

    因此NXC中的 CreateFile 創建出 來的檔案是被儲存於NXT中,只 有透過NXT EXPLORER才能將檔 案從NXT複製到PC。

    如果你想直接於PC創建資料檔 案,必須寫兩個程式,分別 在NXT及PC上執行,透過有線或 無線通訊,把NXT的資料傳送 到PC,然後由PC端儲存。

    回覆刪除
  24. 謝謝老師

    不過我這幾天在網路上還有書上面翻到的指令都是針對藍芽以及兩台NXT互相通訊

    可不可麻煩提供有線傳輸的資料給我
    謝謝

    回覆刪除
  25. 彥丞您好,

    目前採用USB解決方案的例子很少見,我見過的案例是 JAVA (NXT及PC),如果你有專案時間壓力,建議你採用JAVA (USB解決方案)或是幾百元買一隻藍牙傳輸器(藍牙無線方案)。

    倘若另有其它考量非採USB不可,並且NXT端必須用NXC,或是PC端必須用VISUAL,那你就需要花費較多時間研究研究。

    現在手邊沒有現成的程式,平日工作繁忙,假日抽空研究。

    回覆刪除
  26. 您好,我有購買您 "機器人程式設計與實作使用JAVA",在實作第8章:通訊 使用Sample 8_3時,檢查到
    DataInputStream dataIn = conn.getDataIn(); //取得輸入串流
    DataOutputStream dataOut = conn.getDataOut(); //取得輸出串流

    這兩行 沒有getDataIn()/Out方法,我查看了NXTConnector.class 發現裡面已經沒有getDataIn/Out,只剩下getDataIn/Out

    使用無Data開頭的方法,沒有辦取對讀進來或輸出的資料 賦予型態? EX:dataOut.writeInt(123); 使用getDataIn() 得到的值是65,不曉得他是如何decode的
    我的LeJOS版本是 0.9.1 beta ,感謝回答

    回覆刪除
    回覆
    1. 上面筆誤,(只剩下getDataIn/Out ) -> (只剩下getInputStream/OutputStream )

      刪除
  27. YERorNO 您好,

    可能你誤會了,"機器人程式設計與實作使用JAVA",我並非原書作者只是協助 CAVE 校稿幾個章節。

    關於 Sample8_3,這程式是 PC 端的程式,不能被用於 NXT,這方面你需要再仔細查看書本內容。

    至於 getDataIn() 得到 65,可能是你的程式只取得 dataOut.writeInt(123) 串流中的一個位元組,這要同時看你發射端與接收端的程式才有辦法判斷。

    如果有其它問題,也可以向 CAVE 反應,http://blog.cavedu.com/contact/ 。

    回覆刪除
    回覆
    1. 抱歉,誤會您了

      自問自答:

      在 http://www.lejos.org/forum/viewtopic.php?f=7&t=4483 討論串找到解答

      將原本PC端程式Sample8_3:

      class Sample8_3
      {
      public static void main(String args[])
      {
      NXTConnector conn = new NXTConnector(); //建立NXTConnector物件
      if(!conn.connectTo("", "", NXTCommFactory.BLUETOOTH)) //連線到NXT
      {
      System.out.println("Cannot connect to NXT");
      System.exit(1);
      }
      //DataInputStream dataIn = conn.getDataIn(); //取得輸入串流 <-- 這是原本書上的
      //DataOutputStream dataOut = conn.getDataOut(); //取得輸出串流 <-- 這是原本書上的

      //換成下面這樣
      DataInputStream dataIn = new DataInputStream(conn.getInputStream());
      DataOutputStream dataOut = new DataOutputStream(conn.getOutputStream());

      try{
      dataOut.writeInt(123); //輸出int值123
      dataOut.flush(); //清除內存
      System.out.println(dataIn.readInt()); //讀取int值並顯示到螢幕上
      } catch(Exception e) {System.exit(1);}
      }//main
      }//Sample8_3

      成功work

      刪除
    2. 謝謝分享,留下記錄有助後人 try and error 及解決問題。

      刪除