Java 机器人编程入门手册(四)

在这一章中,你将学习一组传感器,它们被用来执行有根据的动作。

原文:Beginning Robotics Programming in Java with LEGO Mindstorms

协议:CC BY-NC-SA 4.0

十、触摸传感器和超声波传感器简介

正如您在第三章中所看到的,电机是执行运动的最重要组件。在这一章中,你将学习一组传感器,它们被用来执行有根据的动作。

想象一下,一个机器人在你的房子里走来走去,却没有配备任何传感器。这样的行程将是短暂的,因为机器人撞上了墙壁,而保险杠没有激活或被一些低层障碍物阻挡车轮。然而,如果你在机器人身上安装了一个距离传感器,你可能会想象机器人可以通过使用这个距离传感器避开障碍物,在你的房子周围徘徊几个小时。

乐高 MindStorm EV3/NXT 套件带有四个常见的传感器:触摸传感器,颜色传感器,光传感器和超声波传感器。在本章中,您将使用您创建的轮式机器人,并添加这四个传感器中的两个——触摸传感器和超声波传感器——以使它在房间内移动时更加了解周围的环境。在下一章,你将看到如何应用光传感器和颜色传感器来探索你的机器人周围的环境。

特别是,本章将涵盖以下主题:

  • 触摸传感器简介
  • 超声波传感器简介
  • 触摸传感器编程实践
  • 超声波传感器编程实践

传感器类别

莱霍斯 EV3 为所有类型的传感器提供软件抽象。在这种情况下,传感器必须物理连接到 EV3 模块中的端口,并且传感器对象必须知道这是哪个端口。为了识别这些信息,您创建了一个传感器的实例,然后在它的构造函数中传递这些信息,比如SensorPort.S1、SensorPort.S2、SensorPort.S3和SensorPort.S4。在您的代码中,传感器类必须指出它们插入了哪个端口。这可以通过使用端口类lejos.hardware.port.Port来完成。

端口类类似于MotorPort。这个类中有四个静态端口对象。通常,这些在传感器的构造函数中使用,如下所示:

        Port portS1 = ev3brick.getPort("S1");
        EV3TouchSensor touchSensor = new EV3TouchSensor(portS1);

触摸传感器

触摸传感器是 EV3 机器人套件中最基本的传感器。它有一个简单的开关,由前面的红色按钮激活,如图 10-1 所示:

Java 机器人编程入门手册(四)

图 10-1。

The Lego EV3 touch sensor

如图 10-1 所示,触摸按钮有一个轴孔,允许乐高积木轴直接连接到开关上。触摸传感器检测它何时被某物按压,何时被再次释放,它将返回一个简单的浮点值,指示传感器是否被按压。EV3TouchSensor类实现了 Touch 接口,它包含一个简单的方法getTouchMode ()。getTouchMode(方法检测其前端按钮何时被按下,其中样本包含一个元素。值 0 表示按钮未被按下,值 1 表示按钮被按下。

总之,为了使用触摸传感器,您可以使用以下构造函数创建它的一个实例:

EV3TouchSensor touchSensor = new EV3TouchSensor(portS1);
SensorMode toucher = touchSensor.getTouchMode();
float[] sample = new float[toucher.sampleSize()];

要测试触摸传感器是否被按下,您可以使用fetchSample ()方法:

toucher.fetchSample(sample, 0);
if (sample[0]==1) then the button is pressed

以下程序等待触摸传感器被按下。按下触摸传感器后,触摸传感器的值将在 LED 屏幕上显示 50 秒。该计划被终止,直到你按下 EV3 砖上的退出按钮。下面是使用 S1 港的测试程序的源代码。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3 ch10p1.java
//an example for touch sensor testing
//******************************************************************

import lejos.hardware.BrickFinder;
import lejos.hardware.Keys;
import lejos.hardware.ev3.EV3;
import lejos.hardware.lcd.TextLCD;
import lejos.hardware.port.Port;
import lejos.hardware.sensor.EV3TouchSensor;
import lejos.hardware.sensor.SensorMode;

public class ch10p1 {
        public static void main(String[] args) throws Exception {

                // get EV3 brick
                EV3 ev3brick = (EV3) BrickFinder.getLocal();

                // LCD class for displaying and Keys class for buttons
                Keys buttons = ev3brick.getKeys();
                TextLCD lcddisplay = ev3brick.getTextLCD();

                // block the thread until a button is pressed
                buttons.waitForAnyPress();

                // get a port instance
                Port portS1 = ev3brick.getPort("S1");

                // Get an instance of the touch EV3 sensor
                EV3TouchSensor touchSensor = new EV3TouchSensor(portS1);

                // get an instance of this sensor in measurement mode
                SensorMode toucher = touchSensor.getTouchMode();

                // initialize an array of floats for fetching samples
                float[] samplevalue = new float[toucher.sampleSize()];

                lcddisplay.clear();

                while (buttons.getButtons() != Keys.ID_ESCAPE) {

                        // fetch a sample
                        toucher.fetchSample(samplevalue, 0);

                        lcddisplay.drawString("value: " + samplevalue[0], 0, 0);
                        Thread.sleep(50);
                }

                lcddisplay.clear();
                System.out.println("EXIT");
                System.exit(0);
        }

}

超声波传感器

超声波传感器发出人类几乎听不见的声音信号,然后测量反射回来需要多长时间。由于音速是众所周知的,传感器可以计算信号传播的距离。超声波传感器通常用于避开障碍物,在不同位置之间导航,甚至在不同位置之间构建地图。超声波传感器以厘米为单位测量到固体物体的距离,它可以测量高达 255 厘米的距离。超声波传感器的精确度从 6 厘米到 180 厘米。经验实验表明,超过 180 厘米的物体不能被可靠地定位。此外,它的精度为正负 3 厘米,尽管对于更近的物体精度会有所提高。

如图 10-2 所示,超声波传感器往往会产生一个声纳锥体,也就是说它在锥体形状内探测前方的物体。圆锥体以大约 30 度的角度打开,这意味着在 180 厘米的距离处,圆锥体的直径大约为 90 厘米。圆锥形对机器人来说很好,因为它可以更好地扫描机器人前面的更大区域,以发现可能的碰撞。

Java 机器人编程入门手册(四)

图 10-2。

The Lego EV3 ultrasonic sensor

在 leJOS NXJ 中,超声波传感器是实现测距仪接口的距离传感器。以下方法用于通过测距仪接口获得距离。

double distance = rf.getRange();  

这个方法将以厘米为单位返回到一个物体的距离。当单次扫描检测到多个物体时,可使用以下方法获得相应的距离:

float [] distances  = rf.getRanges();  

若要创建实例,可以使用以下构造函数:

UltrasonicSensor(Port SensorPort)  

在 leJOS NXJ 中,超声波传感器以两种模式工作:默认为连续模式和 ping 模式。在 ping 模式下,传感器只发送一次 ping。当在连续模式下运行时,传感器尽可能频繁地发出 pings,最近获得的结果可通过调用以下命令获得:

int getDistance()  

返回值以厘米为单位。如果没有找到回应,返回值将是 255。

以下程序将测试您的超声波传感器是否工作正常;也就是说,它能够识别你的机器人和它前面的障碍物之间的厘米距离。在程序中,我们使用端口号 S1,并在按下退出键后终止程序。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3 ch10p2.java
//an example for ultrasonic sensor testing
//******************************************************************

import lejos.hardware.BrickFinder;
import lejos.hardware.Keys;
import lejos.hardware.ev3.EV3;
import lejos.hardware.lcd.TextLCD;
import lejos.hardware.port.Port;
import lejos.hardware.sensor.EV3UltrasonicSensor;
import lejos.robotics.SampleProvider;

public class ch10p2 {
        public static void main(String[] args) throws Exception {

                // get EV3 brick
                EV3 ev3brick = (EV3) BrickFinder.getLocal();

                // LCD class for displaying and Keys class for buttons
                Keys buttons = ev3brick.getKeys();
                TextLCD lcddisplay = ev3brick.getTextLCD();

                // block the thread until a button is pressed
                buttons.waitForAnyPress();

                // get a port instance
                Port portS1 = ev3brick.getPort("S1");

                // Get an instance of the Ultrasonic EV3 sensor
                EV3UltrasonicSensor sonicSensor = new EV3UltrasonicSensor(portS1);

                // get an instance of this sensor in measurement mode
                SampleProvider sonicdistance = sonicSensor.getDistanceMode();

                // initialize an array of floats for fetching samples
                float[] sample = new float[sonicdistance.sampleSize()];

                // fetch a sample
                sonicdistance.fetchSample(sample, 0);

                while (buttons.getButtons() != Keys.ID_ESCAPE) {
                        lcddisplay.clear();
                        lcddisplay.drawString("distance: " + sample[0], 0, 1);
                        try {
                                //Thread.sleep(10000);
                        } catch (Exception e) {
                        }

                }
        }
}

在上面的代码中,要使用超声波传感器,您必须创建一个UltrasonicSensor的实例:

// get a port instance
Port portS1 = ev3brick.getPort("S1");
// Get an instance of the Ultrasonic EV3 sensor
EV3UltrasonicSensor sonicSensor =
new EV3UltrasonicSensor(portS1);

您必须指出您将传感器插入了哪个端口。在本例中,超声波插入端口 1。如果您的程序需要知道从传感器到任何对象的距离,那么您需要使用 getDistanceMode()方法:

       // get an instance of this sensor in measurement mode
       SampleProvider sonicdistance =
       sonicSensor.getDistanceMode();
       // initialize an array of floats for fetching samples
       float[] sample = new float[sonicdistance.sampleSize()];
       // fetch a sample
       sonicdistance.fetchSample(sample, 0);

总之,超声波传感器可以帮助你的 EV3 机器人测量距离,观察物体的位置。如你所知,超声波传感器使用与蝙蝠相同的科学原理;也就是说,它通过计算声波检测到物体然后返回所需的时间来测量距离。通常,与柔软织物制成的物体或弯曲形状的物体相比,具有坚硬表面的较大物体返回的读数最好,因为后者很难被传感器检测到。

触摸传感器编程实践

到目前为止,你的机器人对他们的环境一无所知。现在你要开始用一些传感器来练习,这样机器人就可以判断如何最好地移动。在这一部分中,你的任务是给你的机器人增加触觉和超声波传感器,这样它就可以在周围环境中移动,根据需要执行避障动作。开始时,机器人应该沿直线前进,直到遇到障碍物。一旦检测到障碍物,你的机器人应该尝试一个回避策略。最简单的操作是后退,旋转远离物体,然后再次前进。你的机器人应该继续移动和躲避物体,直到程序被用户终止(例如,按下退出按钮)。你实际上应该产生两个不同的程序:一个只使用触摸传感器,另一个只使用超声波传感器。

在本练习中,您将开发一个程序,将触摸传感器放置在机器人的前面,并编写一个程序,使机器人能够绕过障碍物。伪代码可能如下,其中有一个循环不断询问传感器是否有问题以及是否应该采取规避措施。在本练习中,我们使用的 leJOS 版本是 leJOS NXJ。

while true
        Move forward
        if (touch sensor is pressed)
                move backward
                appropriate delay
                turn right

下面的示例程序ch10p3.java说明了如何实现这个目标。

//******************************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3 ch10p3.java
//This program has the robot move forward until the touch sensor is //activated.
//When the sensor is activated the robot will back up, rotate 90 degrees and continue moving.
//******************************************************************************************

import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.TouchSensor;
import lejos.robotics.navigation.DifferentialPilot;

public class ch10p3 {
        public static void main(String[] args) {
                // set up differential pilot and nav path controller to // use for
                // navigation
                DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f, Motor.A, Motor.C);
                pilot.setRotateSpeed(60); // rotate speed set slower to // prevent slipping

                // set up touch sensor
                TouchSensor touch = new TouchSensor(SensorPort.S1);

                // wait to begin
                Button.waitForPress();

                // move forward until touch sensor is pressed
                while (!Button.ESCAPE.isPressed()) {
                        pilot.forward();

                        // if sensor is pressed, stop, rotate 90 degrees // then continue
                        if (touch.isPressed()) {
                                pilot.stop();
                                pilot.travel(-10);
                                pilot.rotate(90);
                        }
                }
        }
}

超声波传感器编程实践

在本练习中,您将编写一个程序,让您的机器人在一个区域内漫游,使用超声波传感器避免与墙壁碰撞。它应该在您按下 ENTER 键时开始,在您按下 ESCAPE 键时停止,不断显示其 LCD 上显示的距离。此外,如果测距仪检测到障碍物,该程序的代码应该能够指示机器人后退一定距离,旋转远离物体,然后再次前进。下面是一个示例程序ch10p4.java,向您展示如何实现这个目标。

//**********************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3 ch10p4.java
//This program will move the robot forward until the ultrasonic //sensor detects an
//object within 25cm. When the object is detected the robot will //back up to 30cm,
//rotate 90 degrees, then continue moving.
//**********************************************************************************

import lejos.nxt.Button;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.UltrasonicSensor;
import lejos.robotics.navigation.DifferentialPilot;

public class ch10p4 {

        public static void main(String[] args) {

                int distance;

                // set up differential pilot and nav path controller to // use for
                // navigation
                DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f, Motor.A, Motor.C);

                // rotate speed set slower to prevent slipping
                pilot.setRotateSpeed(180);

                // set up ultrasonic sensor
                UltrasonicSensor ultraSonic = new UltrasonicSensor(SensorPort.S1);

                // wait to begin
                Button.waitForPress();

                // move forward until distance from object is 25 cm

                while (!Button.ESCAPE.isPressed()) {
                        distance = ultraSonic.getDistance();
                        while (distance > 25) {
                                pilot.forward();
                                distance = ultraSonic.getDistance();
                        }

                        // if object is closer than 25 cm backup to 30 cm

                        if (distance <= 25) {
                                while (distance <= 30) {
                                        pilot.backward();
                                        distance = ultraSonic.getDistance();
                                }

                                // rotate 90 degrees and continue loop
                                pilot.rotate(90);
                        }
                }
        }
}

摘要

在本章中,您了解了触摸和超声波传感器的功能及其工作原理。您还熟悉了超声波传感器和触摸传感器的用法,并学习了如何应用 leJOS NXJ Java 编程语言来控制和操作触摸传感器和超声波传感器,以使机器人具有与其环境交互的能力。

在下一章中,您将使用您创建的轮式机器人,并添加光传感器和颜色传感器,以使您的机器人在房间中移动时更加了解周围的环境。

十一、光传感器和颜色传感器简介

乐高 MindStorm EV3/NXT 套件带有四个常见的传感器:触摸传感器,颜色传感器,光传感器和超声波传感器。继第十章中的触摸传感器和超声波传感器之后,在本章中,您将为轮式机器人添加光传感器和颜色传感器,以提高它在房间内移动时对周围环境的感知。

特别是,本章将涵盖以下主题:

  • 光传感器简介
  • 颜色传感器简介
  • 颜色和光线传感器的编程实践

光敏感元件

在 NXT 2.0 中,光传感器和颜色传感器集成到同一个硬件中。如图 11-1 所示,光线传感器由传感器正面的一个小透镜组成。

Java 机器人编程入门手册(四)

图 11-1。

Lego NXT 2.0 light sensor

光传感器测量由镜头捕获的光的强度,然后执行一组功能。通过将光传感器指向下方,机器人可以跟随一条黑线或由任何其他颜色组成的线。通过使用传感器,您还可以防止您的机器人移动到线的边缘,因为当物体远离时,光值会显著降低。换句话说,远处的物体反射的光不如近处的物体多。此外,光传感器可以区分暗物体和亮物体,因为暗物体反射回的光较少。

要使用光线传感器,首先使用以下构造函数创建它的一个实例:

public ColorSensor(SensorPort port);  

然后,一旦有了ColorSensor的实例,使用方法setFloodlight关闭颜色检测,这样ColorSensor通过使用Color.NONE只检测环境光,如下所示。然后使用getLightValue()方法返回一个 0 到 100 之间的数字,其中灯光越亮,灯光值越高。

setFloodlight(Color.NONE)  
int lightvalue = getLightValue()  

下面是一个示例程序,它使用连接到 S3 港的光线传感器读取当前光线值。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch11p1.java
//an example for light sensor testing
//******************************************************************

import lejos.nxt.*;
import lejos.robotics.Color;

public class ch11p1 {
        public static void main(String[] args) throws Exception {

                // Get an instance of the color/light sensor of NXT 2.0
                ColorSensor lightsensor = new ColorSensor(SensorPort.S1);

                // turn off the color detection, detecting light only
                lightsensor.setFloodlight(Color.NONE);

                LCD.clear();

                // keep receiving light value until an Escape button is // pressed
                while (!Button.ESCAPE.isPressed()) {
                        LCD.drawInt(lightsensor.getLightValue(), 4, 0, 0);
                }

                // clean out the LCD screen
                LCD.clear();
        }
}

颜色传感器

如前所述,在 Lego NXT 2.0 中,颜色传感器和光线传感器都集成在同一个硬件中。与光传感器类似,颜色传感器也可以用于检测不同的颜色。如图 11-1 所示,在硬件传感器的侧面有一个内置的从多色发光二极管发出红色、蓝色或绿色光的灯。通过实现ColorDetector接口,颜色传感器可以读取红色、绿色和蓝色值,或者它可以从调色板中识别颜色。要识别简单的预定义颜色,可以使用getColorID()方法,该方法返回一个表示颜色常数的整数。

int getColor()  

这些值在lejos.robotics.Color类中预定义。例如,Color.GREEN表示绿色光谱中的颜色值。您也可以使用以下代码检索 RGB 值:

ColorPick cp = new ColorSensor(SensorPort.S1);
Color colorvalue = cp.getColor();

int green = colorvalue.getGreen();

Color 类可以通过使用Color.getColor()方法进一步识别色谱,该方法从调色板中返回一个颜色常数。

下面是一个示例程序,它通过识别光线中的不同颜色来测试您的颜色传感器是否正常工作。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch11p2.java
//an example for color sensor testing
//******************************************************************

import java.util.ArrayList;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.nxt.SensorPort;
import lejos.robotics.Color;
import lejos.util.Stopwatch;
import sensors.ColorStruct;
import sensors.SensorControl;
import sensors.UnsupportedSensorException;

/**
 * Demonstrates the use of color sensors
 */
public class ch11p2 {

        // The port to use for color sensing
        private static SensorPort _colorPort = SensorPort.S1;

        // Stores the number of characters that fit on one line of the // LCD screen.
        private static int LCD_DISP_WIDTH = 16;

        public static void main(String[] args) {
                try {
                        runColorSenseDemo();
                } catch (UnsupportedOperationException e) {
                        handleError(e, true);
                } catch (Throwable e) {
                        handleError(e, false);
                }
        }

        /**
         * Demonstrate color sensing
         *
         * @throws UnsupportedSensorException
         *             Thrown if _colorPort does not specify a valid   
         *  color sending
         *             port
         */
        private static void runColorSenseDemo() throws UnsupportedSensorException {
                LCD.drawString("Press to begin…", 0, 0);
                Button.waitForPress();
                SensorControl sControl = new SensorControl(null, null, _colorPort, null);
                while (!Button.ESCAPE.isPressed()) {
                        ColorStruct cvalue = sControl.getSensedColor();
                        Color cv = sControl.getRGBColor();
                        LCD.clear();
                        LCD.drawString("Color: " + cvalue, 0, 0);
                        LCD.drawString("Sensed:", 0, 1);
                        LCD.drawString(cv.getRed() + " " + cv.getGreen() + " " + cv.getBlue(), 0,2);
                }
        }

        /**
         * Prints an error on the LCD screen so that it does not run
         * off the screen,
         * then waits for escape to be pressed to continue.
         *
         * @param message
         *            The message to display on the LCD screen.
         * @param expected
         *            Flag indicating if the exception was expected or   
         *        caught as
         *            part of a blanket-catch.
         */
        private static void handleError(Throwable ex, boolean expected) {
                String message;
                if (expected) {
                        message = "ERROR: " + ex.getClass().toString() + ex.getMessage();
                } else {
                        message = "UNEXPECTED ERR: " + ex.getClass().toString() + ex.getMessage();
                }

                LCD.clear();
                ArrayList<String> messageSplit = new ArrayList<String>();

                // split the message in 16-character segments
                while (message.length() > LCD_DISP_WIDTH) {
                        messageSplit.add(new String(message.substring(0, LCD_DISP_WIDTH)));
                        message = message.substring(LCD_DISP_WIDTH, message.length());
                }

                int printRow = 0;
                Stopwatch sw = new Stopwatch();
                // print all of the messages
                for (String msg : messageSplit) {
                        LCD.drawString("Here", 0, 4);
                        LCD.drawString("CLS:" + ex.getClass().toString(), 0, 5);
                        sw.reset();
                        while (sw.elapsed() < 1000)
                                Thread.yield();
                        LCD.drawString(msg, 0, printRow);
                        printRow++;
                }
                LCD.refresh();
                LCD.drawString("Press to exit…", 0, printRow);
                Button.waitForPress();
        }
}

颜色和光线传感器的编程实践

在本编程练习中,您的任务是设计并编程 Mindstorms NXT 2.0 机器人,使其在两个预定义的路线中导航。第一个球场是由白底黑线定义的区域,第二个球场配备了障碍物——也就是黑线上的两个盒子。你的机器人应该认识到什么时候有障碍物挡住了它的路,绕过障碍物,然后重新加入这条路,继续走到路的尽头。图 11-2 展示了你的机器人将要探索的两条路线,其中红色方框表示障碍物。

Java 机器人编程入门手册(四)

图 11-2。

Two courses for programming practice with color/light sensor

在本编程练习中,您将创建一个具有以下功能的循线机器人:

  1. 你的机器人可以直接沿着黑线从起点到终点。您不必硬编码颜色来指示您是跟随白色背景上的黑色轨迹还是黑色背景上的白色轨迹。相反,您的程序允许您在启动时选择线条和背景颜色,然后等待按钮启动运行。
  2. 您的机器人可以避开到达终点途中的障碍,然后它可以顺利地重新加入原路线,直到到达目的地。

下面的程序,ch11p3.java被设计成没有任何障碍地跟随第一个程序。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch11p3.java
//This program allows your robot to follow a line.
//If line is lost the robot will rotate left and right at increasing //angles
//until the line is found again.
//******************************************************************

import lejos.nxt.Button;
import lejos.nxt.ColorSensor;
import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.robotics.navigation.DifferentialPilot;

public class ch11p3
{
        public static void main(String[] args)
        {
                // set up differential pilot and nav path controller to // use for navigation
                DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f, Motor.A, Motor.C);
                pilot.setTravelSpeed(4);

                //set up color sensor
                ColorSensor colorSense = new ColorSensor(SensorPort.S1);
                colorSense.setFloodlight(false);

                //used to store values returned by color sensor
                //follow is color robot is to follow, search is value //returned by sensor when searching
                int follow, search;

                //degrees robot will rotate when searching for line
                int rotation;

                //calibrate sensor
                LCD.drawString("Place color sensor\nabove color to follow", 0, 0);
                Button.waitForPress();
                follow = colorSense.getColorID();

                // place robot on start and wait for button press to
                // begin main loop
                LCD.clear();
                LCD.drawString("Place robot", 0, 0);
                Button.waitForPress();

                // main loop
                // follow line. if line is lost turn left and right to // search for it
                while(!Button.ESCAPE.isPressed())
                {
                        rotation = 5;
                        search = colorSense.getColorID(); //make sure we // are still on the line

                        //line is found continue forward
                        while(search == follow)
                        {
                                pilot.forward();
                                search = colorSense.getColorID();
                        }

                        //line lost
                        while(search != follow)
                        {
                                pilot.rotate(rotation); //rotate right
                                search = colorSense.getColorID();
                                if(search == follow)
                                        break; //found line again exit loop
                                else
                                {
                                        pilot.rotate(-rotation * 2); //rotate left back to start then to left position
                                        search = colorSense.getColorID();
                                        if(search == follow)
                                                break;        //found line again exit loop

                                        pilot.rotate(rotation);//rotate back to center
                                }
                                rotation+=5; //increase angle of rotation // and continue search
                        }//end search
                }//end main loop
        }//end main()
}//end ch11p3

下面的程序ch11p4.java,是设计来跟随第二条路线,避开任何障碍物的。

//******************************************************************
// Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch11p4.java
// Program that allows your robot to follow a line. It uses the
// ultrasonic
// sensor to detect if an object is in its path while following the // line.
// If an object is detected the robot will leave the line and travel // around
// the object. It will then search for the line and continue
// traveling.
//******************************************************************

import lejos.nxt.Button;
import lejos.nxt.ColorSensor;
import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.nxt.UltrasonicSensor;
import lejos.robotics.navigation.DifferentialPilot;

public class ch11p4 {

        public static void main(String[] args) {
                // set up differential pilot and nav path controller to // use for
                // navigation
                DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f, Motor.A, Motor.C);
                pilot.setTravelSpeed(5);

                UltrasonicSensor ultra = new UltrasonicSensor(SensorPort.S4);

                // set up color sensor
                ColorSensor colorSense = new ColorSensor(SensorPort.S1);
                colorSense.setFloodlight(false);

                // used to store values returned by color sensor
                // follow is color robot is to follow, search is value // returned by
                // sensor when searching
                int follow, search;

                // degrees robot will rotate when searching for line
                int rotation;

                // calibrate sensor
                LCD.drawString("Place color sensor\nabove color to follow", 0, 0);
                Button.waitForPress();
                follow = colorSense.getColorID();

                // place robot on start and wait for button press to
                // begin main loop
                LCD.clear();
                LCD.drawString("Place robot", 0, 0);
                Button.waitForPress();

                // main loop
                // follow line. if line is lost turn left and right to // search for it
                while (!Button.ESCAPE.isPressed()) {
                        rotation = 5;
                        search = colorSense.getColorID();
                        // make sure we // are still on the// line

                        // line is found continue forward
                        while (search == follow) {
                                // object detected
                                if (ultra.getDistance() <= 10) {
                                        // execute 90 degree turn to
                                        // navigate around object
                                        pilot.rotate(90);
                                        pilot.travel(10);
                                        pilot.rotate(-90);
                                        pilot.travel(30);
                                        pilot.rotate(-90);
                                        search = colorSense.getColorID();

                                        // find line again
                                        while (search != follow) {
                                                pilot.forward();
                                                search = colorSense.getColorID();

                                                // line found, rotate until // following again
                                                if (search == follow) {
                                                        pilot.rotate(90);
                                                        break;
                                                }
                                        }
                                }

                                // continue following line
                                pilot.forward();
                                search = colorSense.getColorID();
                        }

                        // line lost
                        while (search != follow) {
                                pilot.rotate(rotation); // rotate right
                                search = colorSense.getColorID();
                                if (search == follow)
                                        break; // found line again exit loop
                                else {
                                        pilot.rotate(-rotation * 2);// rotate left back to start
                                        // then to left position
                                        search = colorSense.getColorID();
                                        if (search == follow)
                                                break;// found line again exit loop

                                        pilot.rotate(rotation);// rotate back to center
                                }
                                rotation += 5; // increase angle of
                                // rotation and continue search
                        }// end search
                }// end main loop
        }// end main()
}

摘要

在本章中,您学习了光线传感器和颜色传感器的功能,以及这两种传感器的工作原理。您还熟悉了光传感器和颜色传感器的使用,并学习了如何应用 leJOS NXJ JAVA 编程来控制和操作它们,以使您的机器人能够与其周围环境进行交互。

在下一章,你将开始学习基于 leJOS 中定义的包容架构的行为编程。您还将了解如何在 leJOS Java 编程中应用仲裁器来控制和操作触摸传感器、超声波传感器和颜色传感器,按照一组行为的顺序,为机器人提供与其周围环境交互的能力。

十二、行为编程导论

机器人有能力通过使用行为编程来实现一系列类似人类的行为,如感知、规划和行动。在这一章中,你将学习基于 leJOS 中定义的包容架构的行为编程。您还将了解如何在 leJOS JAVA 编程中应用仲裁器,以一组行为的特定顺序来控制和操作触摸传感器、超声波传感器和颜色传感器,从而赋予机器人与其周围环境交互的能力。

特别是,本章将涵盖以下主题:

  • 行为编程导论
  • 行为 API 函数
  • 行为编程的设计模式
  • 行为编程实践

行为编程导论

行为编程是基于感觉-计划-行动控制模型的,在你开始编码之前,它需要更多的计划。通过感知,机器人通过视觉、触觉、声音和距离获取周围环境的信息。通过规划,机器人利用感官信息来决定行动。通过行动,机器人使用它的运动部件来完成计划。通过实现 Sense-Plan-Act 模型,从理论上讲,熟悉行为控制模型的其他程序员会更容易理解您的代码。此外,通过行为编程,您可以更容易地从整体结构中添加或删除特定的行为,而不会对代码的其余部分产生负面影响。

基于行为的机器人编程是设定一系列要执行的行为或要完成的目标的行为。几个简单的行为可以组合起来,理论上可以在现实世界中形成一个非常复杂的逻辑模型。基于行为的机器人编程理论最初是受昆虫的启发,被称为“硬连线思维”,由麻省理工学院人工智能实验室的罗德尼·布鲁克斯于 1983 年提出,如图 12-1 所示。

Java 机器人编程入门手册(四)

图 12-1。

Insects “hard-wired thinking ”

包容架构是一种广泛用于机器人编程的方法,它与基于行为的机器人技术密切相关。包容架构是一种将复杂的智能行为分解成许多更简单的行为模块的方法。依次地,这些被组织成不同的层。每一层都实现代理的一个特定目标,更高的层越来越抽象。例如,机器人的底层可以是“躲避物体”相比之下,在那一层之上的可能是“四处游荡”的人下一个更高的层次可能是“探索世界”,等等。这些层中的每一层都可以访问所有的传感器数据,并为致动器生成一组动作。使用这种方法,较低层可以充当快速适应机制,而较高层则努力实现总体目标。

Java leJOS 中实现的行为编程的概念遵循以下四条规则:

  1. 任何时候都应该只有一种行为是活动的,并且在机器人的控制之下。
  2. 每个行为都有一个预定义的固定优先级数。
  3. 每种行为都有能力决定是否应该控制。
  4. 优先级高于任何其他行为的活动行为应该进行控制。

一般来说,机器人的行为编程是一个解决问题的过程,在设计程序时,您可以按照所示的顺序执行以下步骤:

  1. 你想让机器人做什么?
  2. 机器人必须如何表现才能完成任务?
  3. 把复杂的任务分解成几个简单的行为,包括给每个行为分配一个优先级,一个优先级高的行为可能会停止当前的行为。
  4. 使用 Java 编程语言创建程序。
  5. 运行程序。
  6. 机器人的行为符合要求吗?(它正确执行任务了吗?)如果没有,请执行以下操作:
    1. 先检查机器人。如果有问题,你能解决吗?
    2. 接下来,检查程序。有问题吗?你能修好它吗?
    3. 最后回到开头,重读任务。你的程序真的告诉机器人它应该做什么吗? 

行为 API 函数

行为 API 有一个接口和一个类,即行为接口和仲裁器类。行为接口定义了单独的行为类,包括三个公共方法:takeControl()、action()和suppress()。一旦创建了所有的行为,就使用一个仲裁器来管理所有这些行为,以确定应该在什么时候激活哪一个行为。仲裁器类和行为接口位于lejos.subsumption包中。图 12-2 展示了 Java leJOS 中行为编程的工作原理。如图所示,行为接口中的三种方法都非常简单。假设您的机器人有三个独立的行为,那么您需要创建三个单独的类,每个类实现行为接口。一旦这些类完成,您的代码应该将行为对象交给仲裁器。仲裁器在lejos.subsumption.Arbitrator包中,其构造函数如下:

Java 机器人编程入门手册(四)

图 12-2。

Behavior programming

public Arbitrator(Behavior[] behaviors, boolean returnWhenInactive)  

这将创建一个仲裁器对象,它控制每个行为何时激活,其中参数behaviors用于索引数组中每个行为的优先级(例如,behaviors[0]具有最低优先级),如果参数boolean returnWhenInactive的值为真,当没有行为想要控制时,程序退出。否则,程序将一直运行,直到按下 NXT 模块上的 Enter 和 Escape 按钮而关闭。只需使用一个公共方法start(),就可以启动仲裁系统。

当仲裁器对象被实例化时,它被赋予一个行为对象数组。一旦有了这些,就调用start()方法,并开始仲裁和确定哪个行为将被激活。仲裁器对每个行为对象调用takeControl()方法,从行为数组中索引号最高的对象开始。它按照优先级递减的顺序工作,直到找到想要控制的行为。如果此行为的优先级指数大于当前活动行为的优先级指数,则活动行为将被抑制。在这之后,对这个索引的行为调用action()方法。因此,如果有几个行为希望取得控制权,那么只有优先级最高的行为才会被激活。

行为编程的设计模式

为了控制行为控制系统的性能,当调用suppress()时,迅速终止action()方法是非常重要的。为了实现这一点,leJOS 实际上为行为编程定义了一个设计模式。在这个设计模式中,仲裁器包含一个监视线程来循环遍历每个行为,检查takeControl()方法来查看行为是否应该被激活。它从具有最高索引号的行为开始,沿着数组向下进行。一旦遇到一个应该控制的行为,监视器线程就对这个活动行为执行suppress(),然后再从头开始检查每个行为。

下面是一个真实世界的例子,以及如何使用设计模式概念来解决问题。

如图 12-3 所示,你想直线前进,当机器人前方有物体时,你可以绕过这个物体。在这种情况下,您有两种行为:

  • 行为 1:当机器人前方检测到有物体时,向前移动。停止当前行为,开始行为 2。
  • 行为 2:旋转机器人,绕过物体。

Java 机器人编程入门手册(四)

图 12-3。

Scenario for design pattern of behavior programming

图 12-4 展示了行为 1 向前发展的设计模式。

Java 机器人编程入门手册(四)

图 12-4。

Design pattern of Behavior 1 moving forward

图 12-5 显示了行为 2 旋转到旁路的设计模式。图 12-6 接着说明了如何使用设计模式方法管理这两种行为,包括如何设置一组行为以及如何启动编程的行为。

Java 机器人编程入门手册(四)

图 12-6。

Managing behaviors using design pattern

Java 机器人编程入门手册(四)

图 12-5。

Design pattern of Behavior 2 rotating to bypass

总而言之,使用设计模式来管理行为的想法在下面的例子中得到了说明:

  1. 当仲裁器对象被实例化时,它被赋予一个数组中的一组行为。
  2. 然后调用start()方法,仲裁器开始确定哪些行为应该被激活。
  3. 仲裁器对每个行为调用takeControl()方法,从数组中索引号最高的对象开始。
  4. 然后仲裁器遍历每个行为,直到找到想要控制的行为。当它遇到一个时,它执行该行为的action()方法一次,并且只执行一次。
  5. 如果两个行为都想控制,那么只有更高级的行为才被允许。

行为编程的编程实践

在本编程练习中,您将创建一个循线机器人,该机器人通过基于包容架构的行为编程满足以下要求:

  • 行为 1:你的机器人可以从起点一直沿着绿线走,直到到达第一个障碍箱。
  • 行为 2:你的机器人随后可以应用触摸传感器来检测这个障碍物,然后绕过第一个障碍物;也就是你可以让你的机器人向后移动,做一个适当的延迟,然后右转。
  • 行为 3:在你的机器人绕过第一个障碍后,它将继续前进,然后回到这条线,它将沿着一条蓝色的线从它遇到的第一个障碍一直走到第二个障碍箱。
  • 行为 4:你的机器人然后使用超声波传感器避免与第二个障碍物碰撞。一旦测距仪检测到第二个障碍物,机器人就会旋转远离物体,然后继续前进。
  • 行为五:机器人在避开终点途中的障碍物后,再平滑地重新加入原来的蓝线,直到到达终点。

图 12-7 展示了这种编程实践的过程。

Java 机器人编程入门手册(四)

图 12-7。

Moving area for your programing practice

//******************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch12p1.java
//This program implements the leJOS behavior capabilities.
//The robot will begin following a green line. When the touch
//sensor is activated it will move around an object and search for a
//blue line. Then when it travels within 15cm of an object it will //avoid it
//and return to the blue line again.
//******************************************************************************

import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.robotics.subsumption.Arbitrator;
import lejos.robotics.subsumption.Behavior;

public class ch12p1 {
        public static void main(String[] args) {
                // create a sensors object to send to the behavior
                // classes
                Sensors sensors = new Sensors();

                // prompt to begin
                LCD.clear();
                LCD.drawString("Press to begin", 0, 0);
                Button.waitForPress();

                // set up behavior classes
                Behavior b1 = new FollowGreen(sensors);
                Behavior b2 = new TouchAvoid(sensors);
                Behavior b3 = new FollowBlue(sensors);
                Behavior b4 = new SonicAvoid(sensors);

                // create behavior array
                Behavior[] bArray = { b1, b3, b2, b4 };

                // send array to arbitrator and begin
                Arbitrator arby = new Arbitrator(bArray);
                arby.start();
        }
}
//*********************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0
// FollowGreen.java
// This behavior follows a green line
//*********************************************************

import lejos.nxt.Motor;
import lejos.robotics.Color;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.subsumption.Behavior;

public class FollowGreen implements Behavior {
        private boolean suppressed;
        // used to store values returned by color sensor
        // follow is color robot is to follow, search is value //returned by sensor
        // when searching
        private int follow, search;

        // degrees robot will rotate when searching for line
        int rotation;
        // set up differential pilot and nav path controller to use //for navigation
        private DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f,
                        Motor.A, Motor.C);

        Sensors sensors;

        public FollowGreen(Sensors globalSensors) {
                sensors = globalSensors;
                suppressed = false;
                pilot.setTravelSpeed(4);
                sensors.colorSense.setFloodlight(false);
                follow = Color.GREEN;
        }

        @Override
        public boolean takeControl() {
                return true;
        }

        @Override
        public void action() {
                suppressed = false;
                search = sensors.colorSense.getColorID();

                // move forward while line is green
                while (!suppressed) {
                        Thread.yield();
                        while (search == follow && !suppressed) {
                                pilot.forward();
                                search = sensors.colorSense.getColorID();
                                Thread.yield();
                        }

                        // line lost
                        while (search != follow && !suppressed) {
                                pilot.rotate(rotation); // rotate right
                                search = sensors.colorSense.getColorID();
                                Thread.yield();
                                if (search == follow)
                                        break; // found line again exit loop
                                else {
                                        pilot.rotate(-rotation * 2); // rotate left back to start
                                        // then to left position
                                        search = sensors.colorSense.getColorID();
                                        Thread.yield();
                                        if (search == follow)
                                                break; // found line again //exit loop

                                        pilot.rotate(rotation); // rotate //back to center
                                }
                                rotation += 5; // increase angle of //rotation and continue search
                                Thread.yield();
                        }// end search
                        Thread.yield();
                }
        }

        @Override
        public void suppress() {
                suppressed = true;
        }
}// end FollowGreen

//*********************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0         
//        Sensors.java
//Class to store sensor objects to make them available in all //behavior classes
//*********************************************************************************

import lejos.nxt.ColorSensor;
import lejos.nxt.SensorPort;
import lejos.nxt.TouchSensor;
import lejos.nxt.UltrasonicSensor;

public class Sensors {
        ColorSensor colorSense;
        TouchSensor touch;
        UltrasonicSensor sonic;
        boolean touchPressed = false; // used for take control method //in followBlue
                                                                        //behavior

        public Sensors() {
                // set up color, touch, and sonic sensors
                colorSense = new ColorSensor(SensorPort.S1);
                touch = new TouchSensor(SensorPort.S2);
                sonic = new UltrasonicSensor(SensorPort.S4);
        }
}

//***************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 //SonicAvoid.java
//This behavior avoids objects using the ultra sonic sensor
//***************************************************************************

import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.robotics.Color;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.subsumption.Behavior;

public class SonicAvoid implements Behavior {
        // set up differential pilot
        private DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f,Motor.A, Motor.C);
        private boolean suppressed;
        private int follow, search;
        Sensors sensors;

        public SonicAvoid(Sensors globalSensors) {
                pilot.setTravelSpeed(4);
                suppressed = false;
                follow = Color.WHITE;
                sensors = globalSensors;
        }

        // take control when within 15 cm of an object and on a blue //line
        @Override
        public boolean takeControl() {
                return (sensors.sonic.getDistance() < 15 && sensors.colorSense
                                .getColorID() == Color.WHITE);
        }

        @Override
        public void action() {
                LCD.drawString("Sonic triggered", 0, 0);
                suppressed = false;

                // stop and move around object
                pilot.stop();
                // execute 90 degree turn to navigate around object
                pilot.rotate(90);
                pilot.travel(10);
                pilot.rotate(-90);
                pilot.travel(30);
                pilot.rotate(-90);

                search = sensors.colorSense.getColorID();

                // find line again
                while (search != follow && !suppressed) {
                        LCD.drawString("HERE I AM", 0, 0);
                        pilot.forward();
                        search = sensors.colorSense.getColorID();

                        // line found, rotate until following again
                        if (search == follow) {
                                pilot.rotate(90);
                                suppressed = true;
                                break;
                        }
                        Thread.yield();
                }
        }

        @Override
        public void suppress() {
                suppressed = true;
        }

}

//**************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 //TouchAvoid.java
//This class represents a robotic behavior when the touch sensor
//is activated
//**************************************************************************

import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.robotics.Color;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.subsumption.Behavior;

public class TouchAvoid implements Behavior {
        // set up differential pilot
        private DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f, Motor.A, Motor.C);
        private boolean suppressed;
        private int follow, search;
        Sensors sensors;

        public TouchAvoid(Sensors globalSensors) {
                pilot.setTravelSpeed(4);
                suppressed = false;
                follow = Color.WHITE; // color to follow when
                // relocating line
                sensors = globalSensors;
        }

        @Override
        public boolean takeControl() {
                return sensors.touch.isPressed(); // behavior takes
                // control when touch
                // sensor is pressed
        }

        @Override
        public void action() // avoid the object
        {
                LCD.drawString("BUTTON IS PRESSED", 0, 0);
                sensors.touchPressed = true; // set flag in sensor //class so robot knows

                // to follow different color later
                suppressed = false;

                // moves to avoid object
                pilot.stop();
                pilot.travel(-15);
                // execute 90 degree turn to navigate around object
                pilot.rotate(90);
                pilot.travel(10);
                pilot.rotate(-90);
                pilot.travel(30);
                pilot.rotate(-90);

                search = sensors.colorSense.getColorID();

                // find line again
                while (search != follow && !suppressed) {
                        // LCD.clear();
                        pilot.forward();
                        search = sensors.colorSense.getColorID();
                        LCD.drawString("search: " + Integer.toString(search), 0, 0);
                        LCD.drawString("follow: " + follow, 0, 1);

                        // line found, rotate until following again
                        if (search == follow) {
                                pilot.rotate(90);
                                suppressed = true;
                                break;
                        }
                        Thread.yield();
                }
        }

        @Override
        public void suppress() {
                suppressed = true;
        }
}

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0         
//FollowBlue.java
//This behavior follows a blue line
//******************************************************************

import lejos.nxt.ColorSensor;
import lejos.nxt.Motor;
import lejos.nxt.SensorPort;
import lejos.robotics.Color;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.subsumption.Behavior;

public class FollowBlue implements Behavior {
        private boolean suppressed;
        // used to store values returned by color sensor
        // follow is color robot is to follow, search is value //returned by sensor
        // when searching
        private int follow, search;

        // degrees robot will rotate when searching for line
        int rotation;
        // set up differential pilot and nav path controller to use //for navigation
        private DifferentialPilot pilot = new DifferentialPilot(4.32f, 12.2f,
                        Motor.A, Motor.C);

        Sensors sensors;

        public FollowBlue(Sensors globalSensors) {
                sensors = globalSensors;
                suppressed = false;
                pilot.setTravelSpeed(4);
                sensors.colorSense.setFloodlight(false);
                follow = Color.WHITE;
        }

        @Override
        public boolean takeControl() {
                return sensors.touchPressed; // we want this to be the default behavior

                // after the touch sensor has been pressed
        }

        @Override
        public void action() {
                suppressed = false;
                search = sensors.colorSense.getColorID();

                // move forward if the line is the right color
                while (!suppressed) {
                        Thread.yield();
                        while (search == follow && !suppressed) {
                                pilot.forward();
                                search = sensors.colorSense.getColorID();
                                Thread.yield();
                        }

                        // line lost
                        while (search != follow && !suppressed) {
                                pilot.rotate(rotation); // rotate right
                                search = sensors.colorSense.getColorID();
                                Thread.yield();
                                if (search == follow)
                                        break; // found line again exit loop
                                else {
                                        pilot.rotate(-rotation * 2);
                                        // rotate left back to start
                                        // then to left position
                                        search = sensors.colorSense.getColorID();
                                        Thread.yield();
                                        if (search == follow)
                                                break;      // found line again exit loop

                                        pilot.rotate(rotation); // rotate back to center
                                }
                                rotation += 5; // increase angle of 
                                //rotation and continue search
                                Thread.yield();
                        }// end search
                        Thread.yield();
                }
        }

        @Override
        public void suppress() {
                suppressed = true;
        }
}// end FollowBlue

摘要

在这一章中,你学习了如何使用 leJOS 中定义的包容架构进行行为编程。您还学习了如何在 leJOS Java 编程语言中应用仲裁器,以一组行为的特定顺序控制和操作触摸传感器、超声波传感器和颜色传感器,从而赋予机器人与其周围环境交互的能力。

在下一章中,您将学习 Java leJOS 多线程编程的基本概念,然后学习如何应用它来控制和操作颜色传感器、触摸传感器和超声波传感器,以使您的机器人能够与其周围环境进行交互。

十三、使用 Java leJOS 的多线程编程

多线程是一个非常著名的编程特性,它允许您同时执行多个作业。在为机器人开发程序时,您需要将这种编程特性视为您的编程架构的基础。

在本章中,您将学习 Java leJOS 多线程编程的基本概念,然后学习如何应用它来控制和操作颜色传感器、触摸传感器和超声波传感器,以使您的机器人具有与其周围环境交互的能力。

特别是,本章将涵盖以下主题:

  • 线程概念介绍
  • 如何在 leJOS 中使用类 thread
  • Java leJOS 多线程编程实践

线程概念

通常,当您开始使用 Java 和 leJOS 进行开发时,您创建的程序会一个接一个地按顺序执行一组操作。例如,参见下面的程序结构,其中程序首先完成任务 1,然后完成任务 2、任务 3,依此类推,直到最后一个程序任务,即任务 10:

Program:
        Task 1
        Task 2
                Task 3
                ...
                Task 10

但是,如果您使用 Java 多线程,那么您可以并行执行机器人的任务,如下所示,其中程序同时执行所有 10 个任务:

Task 1 Task 2 Task 3 Task 4 Task 5 Task 6 Task 7 Task 8 Task 9 Task 10  

这和我们身体并行执行的操作非常相似,比如呼吸、血液循环、消化、思考、行走。此外,人类大脑并行处理来自你身体传感器的信息,包括视觉、触觉、嗅觉、味觉和听觉。

许多当前的编程语言允许程序员指定并发执行的活动。这是通过使用线程来实现的。Java 中的线程是并行处理的最小单位。因此,在使用线程设计机器人程序时,您应该考虑以下想法:

  1. 管理无线通信的线程
  2. 管理移动子系统的线程
  3. 管理机器人装备的线程
  4. 一根管理机器人感官的线

机器人的设计过程是迭代的,因为这是一项复杂的任务。这不同于传统的一次执行一个动作的设计过程,只有在前一个动作完成后才进行下一个动作。这就是线程技术非常有用的地方。当你想在将来加入一个新的特性时,你只需要添加一个新的任务,一个线程,或者选择一个旧的线程并用新的功能更新它。

在 leJOS 中使用线程

在下面的例子ch13p1.java中,你将看到如何开发你的第一个线程,它由一个主程序管理。该程序可以在 NXT 2.0 LCD 上打印消息“Hello World”和变量i的值。(i计算屏幕上打印了多少条信息。)这个程序执行线程HelloWorldThread,直到你按下 NXT 2.0 砖块上的 ESCAPE 按钮。

//****************************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch13p1.java
//This program executes the thread HelloWorldThread to print the //message
//"Hello World" and the value of a counter to count how
//many messages printed on the screen.
//****************************************************************************

import lejos.nxt.Button;
import lejos.nxt.LCD;

public class ch13p1 {
        private static HelloWorldThread hwt;

        public static void main(String[] args) {
                int i = 0;

                hwt = new HelloWorldThread();
                hwt.start();
                try {
                        while (!Button.ESCAPE.isPressed()) {
                                LCD.drawString("Hello World: " + i, 0, 0);
                                ++i;
                        }
                } catch (Exception ex) {
                } finally {
                        System.exit(0);
                }
        }
}

import lejos.nxt.*;

public class HelloWorldThread extends Thread {
        private int i = 0;

        public HelloWorldThread() {
        }

        public void run() {
                while (true) {
                        LCD.drawString("Hello World:", 0, 0);
                        LCD.drawInt(i, 0, 1);
                        LCD.refresh();
                        i++;
                }
        }
}

要理解多线程代码,了解线程的生命周期非常重要。线程的可能状态包括以下几种:

  1. 开始
  2. 睡眠
  3. 中断的
  4. 产量
  5. 加入
  6. 中断

通常,Java leJOS 中的 thread 类有三种方法:Start()、IsAlive()和Sleep()。我现在将分别介绍每种方法。

方法开始()

当您定义一个线程来执行机器人的任务时,您只需使用如下代码启动它:

private static HelloWorldThread hwt;

hwt = new HelloWorldThread();

hwt.start();

方法 isAlive()

在许多情况下,当处理多个线程时,有必要通过使用以下代码来知道一个线程是否仍然活动:

public void run() {
        while (true) {
                LCD.drawString("Hello World:", 0, 0);
                LCD.drawInt(i, 0, 1);
                LCD.drawString("" + this.isAlive(), 0, 2);
                LCD.refresh();
        }
}

方法睡眠()

在许多其他情况下,通过使用以下代码中的 sleep 方法生成一个定时暂停来确保其他任务已经完成是非常有用的:

import lejos.nxt.*;

public class SleepDemo {

        private static String messages[] = {
            "Java leJOS NXJ",
            "Java leJOS PC API",
            "Java leJOS Mobile API",
        };

        public static void main(String[] args) throws InterruptedException {
                for(int i=0;i< messages.length; i++) {
                   Thread.sleep(1000);
                    System.out.println(Messages[i]);
                }
        }
}

用 Java leJOS 练习多线程

在这个示例程序ch13p2.java中,您将完成一个名为 Music 的多线程程序,这样您的程序就可以在播放音乐的同时在 LCD 屏幕上打印一个序列号。

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 ch13p2.java
//This program demonstrates a threaded song/counter program
//******************************************************************

import lejos.nxt.Button;

public class ch13p2 {
        private static Music mThread;
        private static Counter cThread;

        public static void main(String[] args) {
                mThread = new Music();
                cThread = new Counter();

                mThread.start();
                cThread.start();
                try {
                        while (!Button.ESCAPE.isPressed()) {
                                Thread.yield();
                        }
                        System.exit(0);
                } catch (Exception e) {
                }
        }
}

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 Music.java
//Play a "song" consisting of predefined beeps on a thread
//******************************************************************

import lejos.nxt.Sound;

class Music extends Thread {
        public void run() {
                short Low_G = 392, B_Flat = 470, D = 588, C = 523, E_Flat = 627, F = 697, G = 784, A_Flat = 830;

                short[] note = {a C, 600, G, 100, F, 100, G, 400, C, 400, 0, 600,
                                   A_Flat, 100, G, 100, A_Flat, 200, G, 200, F, 400, B_Flat, 600,
                                  F, 100, E_Flat, 100, F, 400, B_Flat, 400, Low_G, 600, G, 100,
                                  F, 100, G, 200, F, 200, E_Flat, 200, D, 200, E_Flat, 600, D,
                                  100, E_Flat, 100, F, 600, E_Flat, 100, F, 100, G, 200, F, 200,
                                  E_Flat, 200, D, 200, C, 400, A_Flat, 400, G, 1400, A_Flat, 100,
                                  G, 100, F, 100, G, 1400
                };

                for (int i = 0; i < note.length; i += 2) {
                        short w = note[i + 1];
                        Sound.playTone(note[i], w);
                        Sound.pause(w);
                }
        }
}

//******************************************************************
//Wei Lu Java Robotics Programming with Lego EV3/NXT2.0 Counter.java
//Continuously write an incrementing integer to the LCD
//******************************************************************

import lejos.nxt.LCD;

class Counter extends Thread {
        public void run() {
                for (int i = 0; i < 1000; ++i) {
                        LCD.drawInt(i, 0, 2);
                        LCD.refresh();
                        try {
                                Thread.sleep(1000);
                        } catch (Exception e) {
                        }
                }
        }
}

在第十一章中,你创造了一个循线机器人,它可以沿着绿线从一条线的起点直线前进到终点。您创建的机器人还可以在到达终点的途中避开障碍物,并可以顺利地重新加入原始线,直到到达终点。

在多线程编程的第二个练习中,您将创建一个与您在第十一章中创建的机器人非常相似的机器人。在这次练习中,机器人的主要目标是沿着一条绿线前进,如图 13-1 所示,如果发现障碍物,它就会停下来。您必须使用 leJOS 多线程编程来实现这个循线机器人。

Java 机器人编程入门手册(四)

图 13-1。

Programming the line-following robot with multithreading

在分析所涉及的任务时,你会发现这个程序有两个主要过程:(1)沿着绿线走,(2)发现途中的障碍。基于这种分析,您可以用线程设计和开发一个可伸缩的解决方案:实现一个用于跟随绿线的单独线程,实现另一个用于检测障碍物的单独线程,再实现一个用于在线程间交换数据的类。

import lejos.nxt.*;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.robotics.navigation.NavPathController;

public class ch13p3 {

        private static data DE;
        private static line LFObj;
        private static object ODObj;
        static double diam = 2.8;
        static double trackwidth = 15.5;
        static DifferentialPilot pilot = new DifferentialPilot(diam / 2.54,
                        trackwidth / 2.54, Motor.A, Motor.C);
        static UltrasonicSensor sonic = new UltrasonicSensor(SensorPort.S4);
        NavPathController nav = new NavPathController(pilot);// Attaches NavPath

        public static void main(String[] args) {
                DE = new data();
                ODObj = new object(DE, pilot);
                LFObj = new line(DE, pilot);
                ODObj.start();
                LFObj.start();
                while (!Button.ESCAPE.isPressed()) {
                }
                LCD.drawString("Finished", 0, 7);
                LCD.refresh();
        }
}

public class data {
        // ObstacleDetector
        private boolean obstacleDetected = false;
        // Robot has the following commands: Follow Line, Stop
        private int CMD = 1;

        public data() {
        }

        public boolean isObstacleDetected() {
                return obstacleDetected;
        }

        public void setObstacleDetected(boolean obstacleDetected) {
                this.obstacleDetected = obstacleDetected;
        }

        public int getCMD() {
                return CMD;
        }

        public void setCMD(int cMD) {
                CMD = cMD;
        }
}

import lejos.nxt.*;
import lejos.robotics.Color;
import lejos.robotics.navigation.DifferentialPilot;
import lejos.util.Stopwatch;

public class line extends Thread {
        data DEObj;
        private ColorSensor cs;
        private UltrasonicSensor us;
        private DifferentialPilot pilot;
        static boolean leftfirst = true;

        public line(data DE, DifferentialPilot pi) {
                DEObj = DE;
                cs = new ColorSensor(SensorPort.S3);
                us = new UltrasonicSensor(SensorPort.S4);
                pilot = pi;
        }

        public void run() {
                if (DEObj.getCMD() == 1) {
                        if (cs.getColorID() == Color.GREEN) {
                                pilot.forward();
                        } else {
                                pilot.travel(1.5);
                                int found = 0;
                                LCD.clear(6);
                                double degrees = 10;
                                double look = 0, i = 1;
                                if (leftfirst != true) {
                                        i = 1 * i;
                                }
                                LCD.drawString("Looking", 0, 6);
                                Stopwatch clock = new Stopwatch();
                                clock.reset();
                                while (clock.elapsed() < 30000) {
                                        look = degrees * i;
                                        pilot.rotate(look);
                                        pilot.stop();
                                        int batman = cs.getColor().getColor();
                                        if (batman == Color.GREEN) {
                                                LCD.clear(6);
                                                LCD.drawString("Found it!", 0, 6);
                                                found = 1;
                                                break;
                                        } else if (batman == Color.BLACK) {
                                                found = 2;
                                                break;
                                        } else {
                                                look = look * 2 * 1;
                                                pilot.rotate(look);
                                                batman = cs.getColor().getColor();
                                                if (batman == Color.GREEN) {
                                                        pilot.stop();
                                                        LCD.clear(6);
                                                        LCD.drawString("Found it!", 0, 6);
                                                        found = 1;
                                                        break;
                                                } else if (batman == Color.BLACK) {
                                                        found = 2;
                                                        break;
                                                }
                                                pilot.rotate(look * 1);
                                        }
                                        i++;
                                }
                                if (look < 0) {
                                        leftfirst = false;
                                } else {
                                        leftfirst = true;
                                }
                        }
                } else {
                        pilot.stop();
                }
        }
}

import lejos.nxt.*;
import lejos.nxt.ColorSensor.Color;
import lejos.robotics.navigation.DifferentialPilot;

public class object extends Thread {
        private data DEObj;
        private UltrasonicSensor us;
        private ColorSensor cs;
        private final int securityDistance = 15;
        private DifferentialPilot pilot;

        public object(data DE, DifferentialPilot pi) {
                DEObj = DE;
                us = new UltrasonicSensor(SensorPort.S4);
                cs = new ColorSensor(SensorPort.S3);
                pilot = pi;
        }

        public void run() {
                while (true) {
                        if (us.getDistance() > securityDistance) {
                                DEObj.setCMD(1);
                        } else {
                                DEObj.setCMD(0);
                                pilot.rotate(80);// left
                                Motor.B.rotateTo(90);
                                while (us.getDistance() < 15) {
                                        pilot.forward();
                                }
                                pilot.travel(7);// offline

                                pilot.rotate(85);// back on track
                                pilot.travel(2);//
                                while (us.getDistance() < 30) {
                                        pilot.forward();
                                }
                                pilot.travel(8);
                                pilot.rotate(85);
                                pilot.setTravelSpeed(1);
                                while (cs.getColor().getColor() != Color.GREEN) {
                                        pilot.forward();
                                }
                                pilot.stop();
                                pilot.setTravelSpeed(2.25);
                                pilot.travel(2);
                                pilot.rotate(85);// left
                                Motor.B.rotateTo(0);
                                DEObj.setCMD(1);
                        }
                }
        }
}

摘要

在本章中,您学习了 Java leJOS 多线程编程的基本概念,以及如何应用这些概念来控制和操作颜色传感器、触摸传感器和超声波传感器,从而使您的机器人能够与其周围环境进行交互。

读完这本书并完成所有的编程练习后,让你的机器人编程更上一层楼。例如,尝试通过开发 Android 应用来管理和控制乐高 EV3 设备,并实现 Java 应用在各种乐高 EV3 设备之间建立无线连接,从而远程控制您的机器人。


链接:https://juejin.cn/post/7399862413233717282
- 本文内容来自网络,如有侵权,请联系本站处理。

2024-08   阅读(21)   评论(0)
 标签: robot EV3 leJOS

涨知识
SPI

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚。

评论:
相关文章
Scratch 3.0连接EV3

本文介绍如何在Scratch中对EV3机器人进行开发。


Java 机器人编程入门手册(三)

在本章中,您将学习启发式搜索策略背后的基本思想以及如何实现爬山算法,这是 leJOS EV3 中最典型的启发式方法之一。


Java 机器人编程入门手册(二)

这一章向你介绍了在莱霍斯 EV3 使用的笛卡尔坐标系的基础知识。它还教你如何在导航课程中应用编程方法来控制轮式车辆,以便在二维平面中用坐标描绘出预定义的路径。


Java 机器人编程入门手册(一)

本章提供了如何使用乐高 MindStorm EV3 公司建立 Java 机器人编程环境的分步指南,包括乐高 MindStorm EV3 的基本概述和leJOS-EV3的介绍。


乐高EV3遥控车搭建与编程指南

乐高EV3遥控车:从搭建到编程的全面指南


M5 EV3电机底座

Base X 是一款兼容乐高 EV3 电机的专用底座,可同时接入 4 路(RJ11)乐高电机,支持角度 / 速度的读取和控制,完美兼容原有电机功能。


乐高EV3 Java固件leJOS

leJOS是Lego Mindstorms可编程砖的固件替代品。 该软件的不同变体支持原始机器人发明系统,NXT和EV3。


EV3运行程序探究

本文主要探究Bytecode指令集、EV3应用开发与编译、VM运行时等相关内容。


开源:用乐高积木搭建的生物3D打印机,还能打印皮肤组织!

Oliver Castell 博士通过此次开源项目,希望研究人员能够采用这项技术来分享专业知识,并使用额外的 LEGO 组件开发模型,以造福于整个生物医学研究社区。


【EV3研究】EV3概述

乐高 MINDSTORMS Education EV3平台是专为课堂打造的第三代乐高教育机器人技术。

搜索
小鹏STEM教研服务

专属教研服务系统,助您构建STEM课程体系,打造一站式教学环境。