在樂高機器人上執行Java程式(二)
文∕趙英傑
josx.platform.rcx套件,包含連結與控制感測器、馬達、揚聲器…等等的類別和介面(interface),本文將介紹其中的Motor(馬達)和Sensor(感測器)類別,以及相關的範例程式。完整的類別和介面說明,請參閱leJOS網站的API文件。
控制馬達的Motor類別
位於josx.platform.rcx套件裡的Motor類別(完整合法名稱為:josx.platform.rcx.Motor)包含控制RCX上,標示為A, B, C的三個馬達接頭的方法。Motor類別的三個代表A, B, C接頭的靜態變數,就取名叫做A, B, C(相信沒有比這更好的名稱了吧)。
每個馬達接頭都可以控制前進(forward)、後退(backward)、停止(stop)和浮動(float)。「浮動」這項功能,若用汽車的排檔來比喻,相當於「打空(N)檔」,代表不輸出電力給馬達,但是讓它能自由轉動。它們的對應方法名稱正是:forward()、backward()、stop()和flt()。馬達的輸出功率可以透過setPower()方法來調整,分成0~7八個等級,數字越大,輸出功率也就越高。相對地,getPower()方法可傳回馬達目前的輸出功率值。
底下的範例程式會讓A和C接頭的馬達不斷前進(直到Run按鈕被按下為止):
import josx.platform.rcx.*; public class MotorTest1 { public static void main (String[] arg) throws InterruptedException { // 設定輸出功率 Motor.A.setPower(1); Motor.C.setPower(1); // 往前行駛… Motor.A.forward(); Motor.C.forward(); // 直到RCX上的"Run"按鈕被按下為止 Button.RUN.waitForPressAndRelease(); } }
馬達本身並沒有所謂「前進」方向和「後退」方向的區別。執行上述程式後,如果機器人是向後行駛,那只要把馬達的連結線拔起來,反相180度接回去(也就是調換電源的正、負極)就可以了。
底下是另一個Motor類別的測試程式,讓A和C接頭的馬達前進3秒鐘後停止:
import josx.platform.rcx.*; public class MotorTest2 { public static void main (String[] args) throws InterruptedException { // 設定輸出功率 Motor.A.setPower(7); Motor.C.setPower(7); // 往前行駛 Motor.A.forward(); Motor.C.forward(); // 持續三秒鐘 Thread.currentThread ().sleep (3000); // 停止馬達 Motor.A.stop(); Motor.C.stop(); } }
如果機器人的馬達直接銜接輪胎的話,您可以把上面兩個停止馬達的stop()敘述,換成flt()讓它在前進三秒鐘之後打空檔,機器人將往前滑行並逐漸減速停止。
讀取感測器狀態的Sensor類別
josx.platform.rcx.Sensor包含讀取RCX上,標示為1, 2, 3的三個感測器接頭狀態的方法,分別用S1, S2和S3常數來代表這三個接頭。
樂高機器人有多種感測器介面,RCX可程式積木無法自動偵測,哪個接頭接上了哪一種感測器。因此,我們必須透過setTypeAndMode()方法,設定感測器的類型和模式。josx.platform.rcx.SensorConstants介面,則提供了代表感測器類型與模式的名字(常數),請參閱下表:
SensorConstants類別的「感測器類型」常數與對應的感測器:
- SENSOR_TYPE_LIGHT:光線感測器
- SENSOR_TYPE_TOUCH:碰撞(touch)感測器
- SENSOR_TYPE_TEMP:溫度感測器
- SENSOR_TYPE_ROT:角度感測器
- SENSOR_TYPE_RAW:代表所有的感測器
底下則是SensorConstants類別的「感測器模式」常數:
- SENSOR_MODE_ANGLE:用於角度感測器的角度測量
- SENSOR_MODE_BOOL:讀取true或false的布林值
- SENSOR_MODE_DEGC:攝氏溫度值,有效範圍介於-20到70度之間。
- SENSOR_MODE_DEGF:華氏溫度值,相當於:攝氏溫度 * 9/5 + 32
- SENSOR_MODE_EDGE:計數脈衝的edge(邊緣),每當脈衝信號改變時,例如,從0變成1或者從1變成0,計數值就加1。
- SENSOR_MODE_PCT:百分比值,它採用這個公式計算:146-raw/7
- SENSOR_MODE_PULSE:計數脈衝,只有當脈衝信號從0變成1時,才會計數。
- SENSOR_TYPE_RAW:傳回0~1023之間的原始(raw),實際上,上面的數值全都是由此「原始」值轉換而成的。
請注意,銜接需要接通電源的感測器時(例如:光線感測器),必須要呼叫activate()方法,代表開啟RCX接頭上的電源。相對地,無須電源的感測器(如:碰撞感測器),要不要呼叫passivate()(代表不通電)都沒關係,因為這是預設值。
下表列出Sensor類別的一些方法,完整的方法請參閱leJOS網站的API文件。
- setTypeAndMode(類型, 模式):設定使用的感測器類型及模式
- readValue():讀取已經從raw(原始值)轉換成對應的感測器的數值
- readRawValue ():讀取原始資料值(傳回介於0~1023之間的數值)
- readBooleanValue():讀取布林值(true或false)
- activate():使用需要通電的「主動式」感測器
- passivate():使用不需要通電的「被動式」感測器
- setPreviousValue (數值):設定感測器的預設值,例如,替某個使用「碰撞感測」的計數器設定10,讓它從第11次碰撞開始算起。
- addSensorListener(偵聽器):替指定的感測器接頭,添加一個SensorListener(感測器偵聽器),參閱下文說明。
感測器程式的寫法有兩種,一種叫做「輪詢(poll)」,讓程式不斷地檢測每個感測器是否產生變化;另一種比較好的方式稱為「事件偵聽器(event listener)」,程式無須主動偵測每個感測器的狀態:檢測器會在發生變化時自行回報狀態。
底下是「輪詢」類型的程式範例,採用while迴圈不停地偵測接頭1的「碰撞感測器」,一旦它碰到東西(亦即,感測器的值為true),RCX就會發出「嗶(beep)」聲:
import josx.platform.rcx.*; public class TouchTest1 { public static void main (String[] args) throws InterruptedException { // 設定S1接頭,採用碰撞感測器,其傳回值類型為「布林」。 Sensor.S1.setTypeAndMode( SensorConstants.SENSOR_TYPE_TOUCH, SensorConstants.SENSOR_MODE_BOOL); // 代表是否碰撞的變數 boolean hit = false; while (true) { // 讀取S1接頭的布林值 hit = Sensor.S1.readBooleanValue(); // 如果碰到了(傳回true)… if (hit) { hit = false; // 發出「嗶」聲 Sound.beep(); } // 暫停3毫秒, // 避免此迴圈持續佔用CPU資源。 Thread.currentThread ().sleep(3); } } }
leJOS沿用了Java的「事件偵聽程式(event listener)」技術,如需聆聽感測器的狀態,請在事件處理程序類別中實作(implements)SensorListener介面,並且實作public void stateChanged(Sensor aSource, int aOldValue, int aNewValue)方法。SensorListener介面負責接收和處理感測器所產生的事件,而SensorListener類別物件的stateChanged()方法,將會在指定的感測器狀態改變時收到通知。
底下是改用「事件偵聽器」寫法,但是作用和上面的程式一樣的範例:
import josx.platform.rcx.*; public class TouchTest2 { // 處理碰撞感測器事件的類別 // 實作SensorListener介面 public static class TouchHandler implements SensorListener { // 實作stateChange()方法 public void stateChanged(Sensor source, int oldValue, int newValue) { boolean hit = source.readBooleanValue(); if (hit) { Sound.beep(); } } } // 主程式 public static void main(String[] args) throws InterruptedException { // 設定接頭1使用的感測器類型和模式 Sensor.S1.setTypeAndMode( SensorConstants.SENSOR_TYPE_TOUCH, SensorConstants.SENSOR_MODE_BOOL); // 設定接頭1的事件偵聽程式 Sensor.S1.addSensorListener(new TouchHandler()); // 當"Run"按鈕被按下時,結束程式。 Button.RUN.waitForPressAndRelease(); } }
请问能举一个感光头的例子么?
最近在摸索这个~
如果您方便的话请给我发个邮件~
有些问题想咨询您~
谢谢