> **作者介绍**
>
> **Liz Looney**
>
> ![{%}](https://box.kancloud.cn/2015-08-31_55e3fd386f57b.png)
>
> Liz Looney是本书的合著者之一。她是谷歌的软件工程师,也是谷歌“机器人工作小组”(Robotics Task Force)的成员,作为App Inventor团队的初创成员,她领导了乐高头脑风暴机器人(LEGO MINDSTORMS)组件的开发工作。Liz Looney是一位卓越的软件工程师,有着超过25年的从业经历,曾先后在Borland、Oracle及Google公司工作。
本章将创建一个应用,将Android手机变成LEGO MINDSTORMS NXT 机器人的遥控器。应用中用按钮来控制机器人前后移动、左右转动和停止,如果机器人遇到障碍物,它还会自动停止。应用中使用具有蓝牙功能的手机与机器人通信。
LEGO MINDSTORMS机器人不只是玩具,更是教具。After-school program 使用机器人来教小学和初中的孩子们掌握解决问题的能力,并引导他们了解工程和计算机编程。NXT机器人也用于FIRST LEGO League 机器人竞赛,这项比赛允许9-14岁的孩子参加。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd3e321ac.png)
NXT可编程机器人套件中有一个“NXT智能积木”主单元,它可以控制三个电机及四个输入传感器。你可以用乐高的构造元件、齿轮、车轮、电机和传感器来组装机器人。该套件自带的软件可以对机器人进行编程,但现在我们将用App Inventor来创建Android应用,通过蓝牙连接来控制NXT机器人。
应用中参与协作的机器人具有超声波传感器以及用于移动的车轮,如Shooterbot 机器人。图中所示,这款机器人通常是人们利用LEGO MINDSTORMS NXT 2.0套件建造的第一个机器人。它的左车轮与输出端口C相连,右车轮与输出端口B相连,颜色传感器与输入端口3相连,超声波传感器与输入端口4相连。
## **学习要点**
本章用到了以下组件和概念:
* BluetoothClient组件:用于建立Android设备与NXT机器人之间的蓝牙连接;
* ListPicker组件:为用户提供机器人选择列表,选中后开始建立机器人到Android的连接;
* NxtDrive组件:用于驱动机器人的轮子;
* NxtUltrasonicSensor组件:利用机器人的超声波传感器探测障碍物;
* Notifier组件:显示错误消息。
## **准备开始**
本章的应用需要Android 2.0或以上版本。此外,出于安全原因,蓝牙设备必须首先配对才能彼此连通。在开始构建应用之前,需要按以下步骤使Android设备与NXT机器人配对:
1\. 在NXT上单击向右箭头,直到显示“Bluetooth”,然后按下橙色方块;
2\. 点击向右的箭头,直到显示“Visibility”,然后按下橙色方块;
3\. 如果“Visibility”值已设定为可见,继续步骤4;如果不可见,请单击向左或向右箭头设置其值为可见;
4\. 在Android设备上,进入设置→无线与网络;
5\. 确保打开蓝牙功能;
6\. 点击“蓝牙”;
7\. 在“可用设备”中查找名为“NXT”的设备;
8\. 如果机器人名字下显示“已配对但未连接”字样,则配对成功!否则,继续执行步骤9;
9\. 如果机器人名字下显示“与此设备配对”,则点击它;
10\. 在NXT上,要求输入密码,按下橙色方块接受1234为密码;
11\. 在Android上,也会要求输入PIN码,输入1234,然后按确定;
12\. 现在应该看到“已配对但未连接。”,说明配对成功!
> ![](https://box.kancloud.cn/2015-08-31_55e3fd43aca5c.png) 注意:如果你曾经修改过机器人的名字,则寻找机器人现在的名字,而非“NXT”。
连接到App Inventor网站,创建新项目“NXTRemoteControl”,将设置屏幕的标题为“遥控机器人”,并连接测试手机。
## **设计组件**
在这个应用中,我们需要分别创建可见组件及不可见组件,并分别定义它们的行为。
### **不可见组件**
在创建用户界面之前,先来创建表12-1中的不可见组件,如图12-1所示,用来控制NXT。
**表12-1 NXT“机器人遥控”应用中的不可见组件**
| 组件类型 |面板中分组 | 命名 | 作用 |
| --- | --- | --- | --- |
| BluetoothClient| Connectivity | BluetoothClient1 |建立Android与NXT的连接 |
| NxtDrive | LEGO® MINDSTORMS® | NxtDrive1 |驱动机器人的轮 |
| NxtUltrasonicSensor | LEGO® MINDSTORMS® |NxtUltrasonicSensor1 | 检测障碍物 |
| Notifier | User Interface | Notifier1 | 显示错误信息 |
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd490a4db.png)
**图 12-1 在组件设计器底部显示的不可见组件**
按以下方式设置组件的属性:
1\. 设置NxtDrive1及NxtUltrasonicSensor1的BluetoothClient属性为 BluetoothClient1;(说明轮子的驱动与障碍物的侦测都需要依赖蓝牙通信——译者注)
2\. 勾选NxtUltrasonicSensor1的BelowRangeEventEnabled属性(近距离侦测障碍物功能可用);
3\. 设置NxtDrive1的DriveMotors属性
* 如果机器人的左轮电机与输出端口C连接,右轮电机与输出端口B连接,则保持默认设置“CB”;
* 如果机器人的配置与上述不同,则将DriveMotors属性设置为两个字母的文本,其中第一个字母是连接左轮电机的输出端口,第二个字母是连接右轮电机的输出端口。
4\. 设置NxtUltrasonicSensor1的SensorPort属性
* 如果机器人的超声波传感器与输入端口4连接,则保持默认值“4”;
* 如果机器人的配置与上述不同,则将SensorPort设置为与超声波传感器连接的输入端口。
### **可视组件**
现在创建用户界面组件,如图12-2所示。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd4e979bf.png)
**图 12-2 组件设计器中的应用**
建立蓝牙连接时,Android设备需要访问NXT机器人具有唯一性的蓝牙地址,但蓝牙地址由8个用冒号分隔的2位数的十六进制数(二进制数的另一种表示方式)组成,输入起来异常麻烦,而且每次运行应用都要在手机上输入该地址。为了减少麻烦,使用ListPicker来显示已经与手机配对的机器人列表(列表项的值为机器人的名称及蓝牙地址),并从中选择一个。
使用按键来驱动机器人的前进、后退、左右转动、停止和断开连接,使用VerticalArrangement来放置除ListPicker以外的所有组件,用HorizontalArrangement来放置左右转向及停车按钮。
按照表12-2中列出的组件来创建图12-2所示的用户界面。
**表12-2 NXT机器人控制器应用中的可见组件**
| 组件类型 | 面板中分组 | 命名 | 作用 |
| --- | --- | --- | --- |
| ListPicker | User Interface | ConnectListPicker |选择要连接的机器人 |
| VerticalArrangement | layout | VerticalArrangement1 |布局容器,容纳除ListPicker之外的组件 |
| Button | User Interface | ForwardButton | 前进 |
| HorizonalArrangement | layout | HorizonalArrangement1 |布局容器,容纳左转、右转、停止按钮 |
| Button | User Interface | LeftButton | 左转 |
| Button | User Interface | StopButton | 停止 |
| Button | User Interface | RightButton | 右转 |
| Button| User Interface | BackwardButton | 后退 |
| Button | User Interface | DisconnectButton| 与NXT断开连 |
按照图12-2所示来设置可视组件布局:将LeftButton、StopButton和RightButton放在HorizontalArrangement1中,将ForwardButton、HorizontalArrangement1、BackwardButton和DisconnectButton放在VerticalArrangement1中。 按下列方式设置组件属性:
1\. 取消勾选Screen1的Scrollable属性(滚屏功能);
2\. 设置ConnectListPicker和DisconnectButton的宽度为“Fill parent”;
3\. 设置VerticalArrangement1、ForwardButton、HorizontalArrangement1、LeftButton、StopButton、RightButton及BackwardButton的Width与Height为“Fill parent”;
4\. 设置ConnectListPicker的Text属性为“连接”;
5\. 设置ForwardButton的Text属性为“∧”;
6\. 设置LeftButton的Text属性为“<”;
7\. 设置StopButton的Text属性为“—”;
8\. 设置RightButton的Text属性为“>”;
9\. 设置BackwardButton的Text属性为“∨”;
10\. 设置DisconnectButton的Text属性为“断开连接”;
11\. 设置ConnectListPicker和DisconnectButton的FontSize属性为30;
12\. 设置ForwardButton、LeftButton、StopButton、RightButton及BackwardButton的FontSize属性为40。
在这类应用中,当手机与NXT建立蓝牙连接之前,应该隐藏用户的操作界面,为此取消勾选VerticalArrangement1的Visible属性。不要担心,当NXT连通后,将重新显示用户界面。
## **为组件添加行为**
本节将编程来设置应用的行为,包括:
* 用户从列表中选择机器人,并与之建立连接;
* 断开机器人与应用的连接;
* 使用控制按钮来操控机器人;
* 在机器人侦测到障碍物时,让它停下来。
### **连接到NXT机器人**
添加第一个行为:连接到NXT。点击 ConnectListPicker将显示已配对的机器人列表,选中一个,将在应用与机器人之间建立蓝牙连接。
### **显示机器人列表**
使用ConnectListPicker组件来显示机器人列表。ListPicker的外表像按钮,被点击后则显示列表项,并允许进行单选。
使用BluetoothClient1.AddressesAndNames块来提供列表,列表项是已经与Android设备配对的蓝牙设备的名称及地址。由于NXT已经将轮驱动及超声波组件的BluetoothClient属性设定为BluetoothClient1,因此AddressesAndNames属性列表中的设备会自动限定为这类机器人,其他类型的蓝牙设备(如耳机)将不会出现在列表中。表12-3列出了所需要的块。
**表12-3 在应用中添加ListPicker列表所需要的块**
|块的类型 | 所在抽屉 | 作用 |
| --- | --- | --- |
| ConnectListPicker.BeforePicking | ConnectListPicker |当ConnectListPicker被点击时,触发该事件 |
| set ConnectListPicker.Elements to | ConnectListPicker |为ConnectListPicker设置可供选择的列表项 |
#### **块的作用**
点击ConnectListPicker将触发ConnectListPicker.BeforePicking事件,并显示可选项列表。将ConnectListPicker.Elements属性设置为 BluetoothClient1.AddressesAndNames块,来设定可选项;ConnectListPicker将显示已经与Android设备配对的机器人列表。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd4f13d9d.png)
**图 12-3 显示机器人列表**
> ![](https://box.kancloud.cn/2015-08-31_55e3fd4f79e4c.png) 测试:在手机上点击“连接”,看看会发生什么,你会看到所有已经与手机配对的机器人列表。
如果只见黑屏,说明手机尚未与任何机器人配对;如果见到其他蓝牙设备,如蓝牙耳机,说明 NxtDrive1与 NxtUltrasonicSensor1的BluetoothClient属性设置有误。
### **建立蓝牙连接**
从列表中选择一个机器人,应用将通过蓝牙与机器人连接。如果连接成功,用户界面将发生变化:隐藏ConnectListPicker,并显示用户界面的其余部分。如果机器人开关没有打开,则连接失败,会弹出错误信息。
使用call BluetoothClient1.Connect块与机器人进行连接。ConnectListPicker.Selection属性提供了选中机器人的地址和名称信息。
使用ifelse块来测试连接是否成功。ifelse块需要连接三个不同的块:“if”、“then”及“else”。“if”与BluetoothClient1.Connect块连接,“then”区域放置连接成功时要执行的块;“else”区域放置连接失败时要执行的块。
如果连接成功,使用Visible属性来隐藏 ConnectListPicker并显示VerticalArrangement1(其中放置了除ConnectListPicker之外的所有组件)。如果连接失败,则使用Notifier1.ShowAlert块来显示错误信息。表12-4列出了设置上述行为所需的块。
**表12-4与机器人建立蓝牙连接所需的块**
| 块的类型 | 所在抽屉 | 作用 |
| --- | --- | --- |
| ConnectListPicker.AfterPicking | ConnectListPicker |当从ConnectListPicker选中一个机器人时触发|
| ifelse | Control | 检验蓝牙连接是否成功 |
| call BluetoothClient1.Connect | BluetoothClient1 |连接到机器人 |
| ConnectListPicker.Selection | ConnectListPicker |选中的机器人的地址及名称 |
| set ConnectListPicker.Visible to | ConnectListPicker|隐藏ConnectListPicker按钮 |
| false | Logic | 插入set ConnectListPicker.Visible to块 |
| set VerticalArrangement1.Visible to | VerticalArrangement | 显示“连接”按钮之外的所有组件 |
| true | Logic | 插入set VerticalArrangement1.Visible to块 |
| Notifier1.ShowAlert | Notifier1 | 用来弹出错误信息 |
| “无法建立蓝牙连接。” | Text | 错误信息。 |
#### **块的作用**
选中机器人后将触发ConnectListPicker.AfterPicking事件,见图12-4,BluetoothClient1.Connect块用于建立与机器人之间的蓝牙连接。如果连接成功,执行“then”块:隐藏ConnectListPicker按钮并显示VerticalArrangement1内的所有组件,即,设置ConnectListPicker.Visible属性为false,设置VerticalArrangement1.Visible属性为true。如果连接失败,执行“else”块:用Notifier1.ShowAlert块弹出错误信息。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd54c9699.png)
**图 12-4 建立蓝牙连接**
### **与NXT断开连接**
让Android设备与NXT机器人连接着实让人兴奋,不过“断开连接”是我们下面要添加的行为,这样便于对连接与断开进行连续测试。
当点击DisconnectButton时,应用将关闭蓝牙连接,用户界面将发生变化:ConnectListPicker按钮将重新出现,而用户界面上的其余组件将被隐藏。
表12-5列出了构建BluetoothClient1.Disconnect(断开蓝牙连接)所需的块。设置Visible属性来显示 ConnectListPicker按钮并隐藏VerticalArrangement1中包含的所有组件。
**表12-5 与机器人断开连接所需的块**
| 块的类型 | 所在抽屉 | 作用 |
| --- | --- | --- |
| DisconnectButton.Click | DisconnectButton |当点击DisconnectButton时触发该事件 |
| BluetoothClient1.Disconnect | BluetoothClient1 |断开与机器人的蓝牙连接 |
| set ConnectListPicker.Visible to | ConnectListPicker|显示ConnectListPicker(“连接”按钮) |
| true | Logic | 插入set ConnectListPicker.Visible to块 |
| set VerticalArrangement1.Visible to | VerticalArrangement | 隐藏用户界面上的其余组件 |
| false | Logic | 插入set VerticalArrangement1.Visible to |
#### **块的作用**
点击DisconnectButton将触发DisconnectButton.Clicked事件,如图12-5所示,断开蓝牙连接要用BluetoothClient1.Disconnect块,之后设置ConnectListPicker.Visible属性为true来显示 ConnectListPicker,设置VerticalArrangement1.Visible属性为false来隐藏VerticalArrangement1。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd552a414.png)
**图 12-5 与机器人断开连接**
> ![](https://box.kancloud.cn/2015-08-31_55e3fd4f79e4c.png) 测试:请确保机器人已经打开,点击手机上的“连接”按钮,并选择要连接的机器人。建立蓝牙连接需要一点时间。一旦连接成功,用户界面将显示机器人的控制按钮,以及“断开连接”按钮。
单击“断开连接”按钮:控制机器人的按钮会消失,“连接”按钮则重新出现。
### **操控机器人**
下面是真正有趣的部分:添加前进、后退、左右转动及停止行为。不要忘记“停止”,否则你手中的机器人会失去控制!
NxtDrive组件提供了五个块,用来驱动机器人的电机:
* MoveForwardIndefinitely块:驱动两个电机前进;
* MoveBackwardIndefinitely块:驱动两个电机后退;
* TurnCounterClockwiseIndefinitely块:驱动机器人左转:让右侧电机向前而左侧电机后退;
* TurnClockwiseIndefinitely块:驱动机器人右转:让左侧电机向前而右侧电机后退;
* Stop将停止电机。
每个移动及转向块都有一个Power参数,需要与数字块配合使用,来设定机器人电机的输出功率,取值范围可以从 0到100。但如果设置的功率太小,电机会发出吱吱声而不运转。在本例中建议使用90(百分比)。表12-6中列出了所需的块。
**表12-6 用于控制机器人的块**
| 块的类型 | 所在抽屉 | 作用 |
| --- | --- | --- |
| ForwardButton.Clic | ForwardButton|点击ForwardButton时触 |
| NxtDrive1.MoveForwardIndefinitely | NxtDrive1 |驱动机器人前进 |
| 数字90 | Math | 功率值 |
| BackwardButton.Click | BackwardButton |点击BackwardButton时触发 |
| NxtDrive1.MoveBackwardIndefinitely | NxtDrive1 |驱动机器人后退 |
| 数字90 | Math | 功率值 |
| LeftButton.Click | LeftButton | 点击LeftButton时触发 |
| NxtDrive1.TurnCounterClockwiseIndefinitely | NxtDrive1 |驱动机器人逆时针转动 |
| 数字90 | Math | 功率值 |
| RightButton.Click | RightButton | 点击RightButton时触发 |
| NxtDrive1.TurnClockwiseIndefinitely | NxtDrive1 |驱动机器人顺时针转动 |
| 数字90 | Math | 功率值 |
| StopButton.Click | StopButton | 点击StopButton时触发 |
| NxtDrive1.Stop | NxtDrive1 | 让机器人停止 |
#### **块的作用**
如图12-6所示,点击ForwardButton按钮时触发ForwardButton.Clicked事件,此时调用NxtDrive1.MoveForwardIndefinitely块,让机器人以90%的功率前进,其余按钮的事件处理程序与此类似,并以相同的功率驱动机器人后退及左右转动。点击StopButton时触发StopButton.Clicked事件,调用NxtDrive1.Stop块让机器人停止运动。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd6ae8611.png)
**图 12-6 操控机器人**
> ![](https://box.kancloud.cn/2015-08-31_55e3fd4f79e4c.png) 测试:按照此前的“测试”说明,先连接NXT机器人。不要将机器人放在桌子上,以免跌落,然后测试以下行为:
>
> 1\. 点击前进按钮,机器人应该向前移动;
>
> 2\. 点击后退按钮,机器人应该向后移动;
>
> 3\. 点击左转按钮,机器人应逆时针转动;
>
> 4\. 点击右转按钮,机器人应顺时针转动;
>
> 5\. 点击停止按钮,机器人应停止。
>
> 如果机器人不动并发出吱吱声,可能需要加大电机的功率,可以用最大功率100。
### **用超声波传感器探测障碍物**
使用超声波传感器的机器人可以侦测到30厘米范围内的障碍物,遇到障碍物时机器人会像罪犯一样停下来,如图12-7所示。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd75ab082.png)
**图 12-7 为NXT机器人设置障碍**
NxtUltrasonicSensor组件用于侦测障碍物,两个属性BottomOfRange和TopOfRange用来定义侦测范围(以厘米为单位)。默认设定BottomOfRange为30厘米,TopOfRange为90厘米。
NxtUltrasonicSensor组件具有三个事件BelowRange、WithinRange及boveRange,当侦测到障碍物在BottomOfRange(下限)距离以内时,会触发BelowRange事件;当障碍物的距离在BottomOfRange与TopOfRange (上下限)之间时,会触发WithinRange事件;当障碍物的距离超过TopOfRange(上限)时,将触发AboveRange事件。
这里使用NxtUltrasonicSensor1.BelowRange事件块,用来侦测30厘米以内的障碍物,如果你想尝试侦测不同距离的障碍物,可以调整BottomOfRange属性。当BelowRange时间发生时,使用NxtDrive1.Stop块让机器人停下来。表12-7中列出了所需的块
**表12-7 使用NxtUltrasonicSensor需要的块**
| 块的类型 | 所在抽屉 | 作用 |
| --- | --- | --- |
| NxtUltrasonicSensor1.BelowRange | NxtUltrasonicSensor1 |超声波传感器在30厘米内遇到障碍物时触发 |
| NxtDrive1.Stop | NxtDrive1 | 让机器人停下来 |
#### **块的功能**
当机器人的超声波传感器侦测到30厘米以内的障碍物时,NxtUltrasonicSensor1.BelowRange事件被触发,如图12-8所示,此时NxtDrive1.Stop块让机器人停下来。
![{%}](https://box.kancloud.cn/2015-08-31_55e3fd7bba9bd.png)
**图 12-8 侦测障碍物**
> ![](https://box.kancloud.cn/2015-08-31_55e3fd4f79e4c.png) 测试:按照此前的“测试”说明,先连接NXT机器人。引导机器人朝着障碍物(如猫)的方向前进,机器人将在距离猫30厘米时停下来。如果机器人没停下来,可能是猫已经远离了机器人,它们之间的距离一直大于30厘米。可以换一个静止的障碍物来进行测试。
## **改进**
应用运行起来,想必你已经花了大量时间来操控这个机器人,不过还是想继续其他的尝试:
* 调节驱动电机的输出功率:
* 可以修改插入到前进(MoveForwardIndefinitely)、后退(MoveBackwardIndefinitely)、左转(TurnCounterclockwiseIndefinitely)及右转(TurnClockwiseIndefinitely) 块中的数字块的值。
* 当侦测到障碍物时,使用NxtColorSensor让红灯闪烁:
* 可以使用NxtColorSensor组件及其GenerateColor属性;
* 需要将DetectColor属性设置为false(或在组件设计器取消勾选该属性),因为颜色传感器无法同时检测和产生颜色。
* 使用Android的方向传感器OrientationSensor来控制机器人。
* 使用乐高的构造元件建立手机与机器人之间的物理连接,创建应用实现机器人的自主性。
## **小结**
以下是本章涵盖的内容:
* ListPicker组件:让用户可以从已配对的机器人列表中进行选择;
* BluetoothClient组件:使Android设备与机器人建立连接;
* Notifier组件:用来显示错误消息;
* Visible属性:用于隐藏或显示用户界面中的组件;
* NxtDrive组件:可以控制机器人的移动、转向及停止;
* NxtUltrasonicSensor组件:用于侦测障碍物。
### **外部链接**
[LEGO MINDSTORMS EV3控制程序](http://blog.sina.com.cn/s/blog_6210c9730101jqui.html)
[Android应用控制LEGO EV3(Video)](http://blog.sina.com.cn/s/blog_62218b990101k4yr.html)
- 简介
- 序言
- 前言
- 第 1 章 Hello 猫咪
- 第 2 章 油漆桶
- 第 3 章 打地鼠
- 第 4 章 开车不发短信
- 第 5 章 瓢虫快跑
- 第 6 章 巴黎地图旅游
- 第 7 章 安卓,我的车在哪?
- 第 8 章 总统测验
- 第 9 章 木琴
- 第 10 章 出题及答题
- 第 11 章 广播中心
- 第 12 章 遥控机器人
- 第 13 章 亚马逊掌上书店
- 第 14 章 理解应用的结构
- 第 15 章 软件工程与应用调试
- 第 16 章 应用中的存储
- 第 17 章 创建动画应用
- 第 18 章 程序中的决策:条件块
- 第 19 章 数据列表编程
- 第 20 章 循环
- 第 21 章 定义过程
- 第 22 章 数据库
- 第 23 章 传感器
- 第 24 章 与Web API通信