使用英特尔® XDK、Node.js 和 MRAA 库对机器人进行编程
简介:
现在,我们可以从兴趣商店,或者 dfrobot.com 这样的在线商店购买各种各样的可编程机器人套件。 而且,您也可以学习并掌握多种不同的平台、编程语言和工具。 Dfrobot* 创建了一个坦克机器人平台 — Devastator,其中包含 Romeo* 控制器板。 该控制器板经过修改后可与英特尔® Edison 计算模块配合使用,以便提供更多的功能,以及更多的 I/O、集成 WiFi、USB 主机、伺服控制和处理性能。 该套件随机提供 USB 连接并可通过 Arduino* IDE 编程。
本文介绍了另外一种机器人编程方法,即使用英特尔® XDK 并通过 WiFi、Node.js* 和 MRAA 库进行编程。 本文特别讨论了所使用的工具 — Romeo 控制器板、映射外设针、创建英特尔 XDK 项目以及实施用于机器人的传感器和制动器。
最后,文本将对所讨论的概念进行汇总,同时提供一个机器人原型,以便实现自动运行并避免碰撞。
机器人平台入门:
如欲了解 Devastator 机器人平台的简介和入门信息,请访问:
https://software.intel.com/zh-cn/articles/overview-of-intel-edison-based-robotics-platform
使用的工具:
Node.js*:
Node.js* 是一种轻型 JavaScript 运行时,其非阻塞 I/O 模型拥有一个大规模的开源库生态系统。 如欲了解关于 Node.js 的更多信息,请访问:
MRAA 库:
MRAA 是一种基于 Linux 的开源低级别 C 外设库,具备面向 C++、Python、Node.js 和 Java 语言的绑定。 这可以帮助开发人员灵活地选择熟悉的语言来部署 IoT 应用。 该库支持多种 x86 和 ARM 平台,并具备通用的 API。 英特尔® Edison 在其 Linux 映像中预装了 MRAA 库,而且英特尔 XDK 可管理更新,确保库获得最新的版本。 如欲了解更多有关 MRAA 的信息,请访问下列链接:
https://github.com/intel-iot-devkit/mraa
http://iotdk.intel.com/docs/master/mraa/edison.html
英特尔 XDK:
英特尔 XDK 是一种全面的 IDE,可通过 WiFi 连接来开发、编程和调试英特尔® Edison 物联网应用。 它配备了 Node.js 环境并且支持 MRAA。 该工具可从以下地址免费下载:
https://software.intel.com/zh-cn/intel-xdk
Romeo 英特尔® Edison 控制器板:
查看示意图有助于了解关键的系统组件,以及如何将可用的外设映射至机器人使用的传感器和制动器。 机器人使用不同的外设来完成不同的任务。 以下列出了机器人映射的外设组件。 从物理针到 MRAA 针可以进行额外的映射。 初始化外设时,我们将在代码中使用该针外设。 此外,该控制器板上还配有转换器。 此外,下表还对这些针进行了说明,以帮助您在查看示意图时更好的理解。
组件: | 外设: | 英特尔® Edison 针: | 转换针: | MRAA 针: |
被动红外传感器 | 数字输入 | GPIO43 | D11 | 38 |
倾斜伺服 | PWM | PWM1 | D5 | 14 |
平移伺服 | PWM | PWM0 | D3 | 20 |
超声波传感器 | UART TX UART RX | GP131 GP130 | D1/TX D0/RX | UART0 |
左侧 LED | 数字输出 | GPIO48 | D7 | 33 |
右侧 LED | 数字输出 | GPIO41 | D10 | 51 |
蜂鸣器 | PWM | PWM2 | D6 | 0 |
有刷 DC 电机 | I2C SCL I2C SDA | SDA1 SCL1 | I2C1_SDA I2C1_SDL | I2C0 |
如欲查看英特尔® Edison 的 MRAA 针映射表,请访问:
http://iotdk.intel.com/docs/master/mraa/edison.html
Romeo 示意图可从以下地址下载:
创建英特尔 XDK 项目:
我们已经讨论了要使用的工具并了解了示意图,现在我们可以在英特尔 XDK 中创建新项目,开发机器人代码。
创建新项目:
点击 New Project 按钮
点击 Blank Iot Node.js 模板
命名->点击 Create
连接到开发板
点击 Upgrade XDK 后台程序并更新开发板上的库
传感器和制动器组件实施:
现在我们创建了一个新的项目并连接至开发板,下面我们要编写一些代码,以便连接至 LED、红外传感器、伺服器、蜂鸣器、超声波传感器和有刷 DC 电机组件。
MRAA 初始化:
通过下面的代码可以在项目中进行 MRAA 库的初始化。
//MRAA Initialization var m = require("mraa");
LED 指示灯:
机器人的前部安装了两个 LED 指示灯。 这些指示灯可用于指示机器人的移动方向、对象检测和距离等。
针对 MRAA 针 33 和 51 创建两个 MRAA GPIO 对象即可初始化 LED。 这些针均被设置一个带有dir()函数的 OUTPUT,默认情况下设置一个 LOW 值。
var leftLED = new m.Gpio(33); var rightLED = new m.Gpio(51); leftLED.dir(m.DIR_OUT_LOW); rightLED.dir(m.DIR_OUT_LOW);
典型的 LED 功能通常为开、关和切换 LED。 这些功能可通过下面的代码来实现,右侧 LED 使用 GPIO write()和 read()功能。
//Turn On rightLED.write(1); //Turn Off rightLED.write(0); //Toggle rightLED.write(rightLED.read()^1);
被动红外传感器 (PIR):
被动红外传感器安装在坦克机器人的后部,当机器人向后移动时可以发出移动指示。 如果检测到移动,传感器会发出 HIGH 指示;如果未检测到移动,传感器会发出 LOW 指示。
传感器初始化与 LED 初始化相似,它使用面向 MRAA 针 38 的 GPIO 对象,但是使用dir()函数将传感器配置为 INPUT。
var pirMotionSensor = new m.Gpio(38); pirMotionSensor.dir(m.DIR_IN);
使用下面的函数轮询传感器,有助于轻松地检测移动。 使用read()函数来轮询传感器。 如果检测到移动,函数返回 TRUE;如果未检测到移动,则返回 FALSE.
function isMotionDetected() { if (pirMotionSensor.read()) return true; else return false; }
伺服器:
该机器人包含两个伺服器。 第一个伺服器用于平移超声波传感器和摄像头。 平移操作可帮助机器人向左看和向右看,并确定其附近有何物品。 第二个伺服器用于向上和向下倾斜摄像头的角度。 标准伺服器最少每隔 20 毫秒或 50Hz 进行一次周期性脉冲。 脉冲宽度决定伺服器的位置,每隔 1 到 2 毫秒对伺服器的位置进行配置。 例如,1 毫秒脉冲宽度将伺服器移动至 0 度位置,1.5 毫秒脉冲宽度将伺服器移动至 90 度中间位置,2 毫秒脉冲将伺服器移动至 180 度位置。 使用脉冲宽度调制 (PWM) 外设可以轻松地实现这一点。
使用 MRAA 针 14(用于倾斜)和 20(用于平移) 创建两个 MRAA PWM 对象即可初始化伺服器。 使用 PWM period_us()函数可以配置伺服器周期(微妙)。 完成外设初始化之后,通过调用panCenter()和tiltCenter()函数使伺服器处于中间位置。
var tiltServo = new m.Pwm(14); //PWM1 var panServo = new m.Pwm(20); //PWM0 tiltServo.period_us(10000); //100Hz -> 10ms Period panServo.period_us(10000); //100Hz -> 10ms Period panCenter(); tiltCenter();
借助panRight()、panLeft() 和panCenter()函数可以移动平移伺服器。 这些函数可以配置 PWM 外设的脉冲宽度,具体方法是调用pulsewidth_us()函数,然后通过调用enable(true)函数来启动外设。 借助sleep()函数可实现一个短暂的延迟以便支持伺服器移动,然后通过调用enable(false)函数来禁用外设。
function panRight() { panServo.enable(false); panServo.pulsewidth_us(1000); //0 degree position panServo.enable(true); sleep(200); panServo.enable(false); } function panLeft() { panServo.enable(false); panServo.pulsewidth_us(2000); //180 degree position panServo.enable(true); sleep(200); panServo.enable(false); } function panCenter() { panServo.enable(false); panServo.pulsewidth_us(1500); //90 degree position panServo.enable(true); sleep(200); panServo.enable(false); }
上下移动倾斜伺服器与移动平移伺服器的操作非常相似,区别在于使用的角度有所不同。经过试验后确定使用更加实际的角度。 以下列出的是tiltUp()、tiltDown() 和 tiltCenter()函数。
function tiltUp() { tiltServo.enable(false); tiltServo.pulsewidth_us(1250); //45 degree position tiltServo.enable(true); sleep(200); tiltServo.enable(false); } function tiltDown() { tiltServo.enable(false); tiltServo.pulsewidth_us(1750); //135 degree position tiltServo.enable(true); sleep(200); tiltServo.enable(false); } function tiltCenter() { tiltServo.enable(false); tiltServo.pulsewidth_us(1500); //90 degree position tiltServo.enable(true); sleep(200); tiltServo.enable(false); }
蜂鸣器:
该机器人包含一个蜂蜜器,用于不同事件的声音提示。 例如,当机器人接近一个物体时,它会从 c 大调音阶发出不同的声音,以便提示其距离。 此外,蜂鸣器也使用 PWM 外设,因此其初始化和使用方式与伺服器相似。 其思维模式与伺服器稍有不同,因为我们将 PWM 外设用作一个简单的数字到模拟转换器 (DAC)。
借助 MRAA 针 0 上的 MRAA PWM 对象即可初始化蜂鸣器。 振幅可通过write()函数来配置,这样便可配置 PWM 工作周期并初始化为 0。 调用enable(false)函数可以禁用外设。
var buzzer = new m.Pwm(0); //PWM2 buzzer.write(0.0); //Duty Cycle buzzer.enable(false);
使用蜂鸣器创建声音,具体方法是使用period_us() 函数设置声音频率,然后通过调用enable(true)函数来启用声音。 以下是一个示例函数,可播放 c 大调音阶的一个八度音阶,用于初始化机器人时发出的提示音调。 一个简单的 125 毫秒计时器循环可播放音符,并且逐渐增加至八度音阶的下一个音符。
var state2=0; var handle2=setInterval(notes,125); //125ms timer loop function notes() { switch (state2){ //C4 case 0: buzzer.enable(false); buzzer.period_us(3831); buzzer.enable(true); state2++; break; //D4 case 1: buzzer.enable(false); buzzer.period_us(3412); buzzer.enable(true); state2++; break; //E4 case 2: buzzer.enable(false); buzzer.period_us(3039); buzzer.enable(true); state2++; break; //F4 case 3: buzzer.enable(false); buzzer.period_us(2865); buzzer.enable(true); state2++; break; //G4 case 4: buzzer.enable(false); buzzer.period_us(2551); buzzer.enable(true); state2++; break; //A4 case 5: buzzer.enable(false); buzzer.period_us(2272); buzzer.enable(true); state2++; break; //B4 case 6: buzzer.enable(false);buzzer.period_us(2028); buzzer.enable(true);state2++; break; //C5 case 7: buzzer.enable(false);buzzer.period_us(1912); buzzer.enable(true);state2++; break; //End default: clearInterval(handle2); state2=0; buzzer.enable(false); break; } }
超声波传感器:
机器人上的超声波传感器用于确定与一个物体的距离。 该传感器安装在机器人的前部,借助前面介绍的平移伺服器可进行左右平移,进而查看周围的环境。 该传感器具有 3 个不同的接口,用于收集距离数据。 这 3 个接口分别为 PWM 输出、模拟输出或 UART 接口。 您可以从下面的 wiki 了解关于该传感器的更多信息,其中包括传感器、不同的接口和命令协议等有用信息。
https://www.dfrobot.com/wiki/index.php/URM37_V4.0_Ultrasonic_Sensor_(SKU:SEN0001)#Introduction
对于我们的 Node.js 程序来说,UART 接口与传感器配合使用以读取距离数据。 传感器 TXD 针 9 连接到英特尔® Edison RX GP130,传感器 RXD 针 8 连接到英特尔® Edison TX GP131。
创建一个 MRAA UART 对象可实现 UART 初始化。 使用setBaudRate()函数将波特率设置为 9600 bps,并使用 setMode()设置数据模式,使用setFlowControl()函数设置流控制功能。 此外,还需要设置命令缓冲区来读取传感器数据。 命令缓冲区字节 0 包含命令代码,字节 1 和 2 是虚拟字节,字节 3 是数据包的校验和。
var u = new m.Uart(0); //Default u.setBaudRate(9600); u.setMode(8,0,1); u.setFlowcontrol(false, false); sleep(200); var command = new Buffer(4); command[0] = 0x22; command[1] = 0x00; command[2] = 0x00; command[3] = 0x22;
要从传感器获得距离数据,需要向 UART 发送命令数据包,然后接收响应数据包。 下面的函数可确定一个对象在既定阈值下的距离是否较近,并返回 TRUE 或 FALSE。 命令数据包在上面的初始化中设置,并通过调用write()函数向 UART 发送。 响应数据包在延迟后收到,然后调用read()函数。 响应数据包缓冲区字节 0 是虚拟字节,字节 1 和 2 是高字节和低字节(根据距离数据,单位是厘米),字节 3 是校验和。要确定数据是否有效,需要对校验和进行分析,然后将数据与阈值比较。
function isObjectClose(threshold) { var rxBuf; var result; u.write(command); sleep(200); rxBuf = u.read(4); sleep(200); if (rxBuf[3] == (rxBuf[0]+rxBuf[1]+rxBuf[2])) { result = (rxBuf[1]<<8) | rxBuf[2]; if (result < threshold) return true; else return false; } else return true; }
有刷 DC 电机:
该机器人能够前后和左右移动。 该机器人外壳的左右两侧相对安装了两个有刷 DC 电机。 查看示意图后,您将会发现 Romeo 板包含一个全桥电机控制驱动程序,该驱动程序连接至 Atmega8* 微控制器。 英特尔® Edison 使用 I2C 接口与微控制器通信。 您可以查看该微控制器中实施的代码,以便了解程序化的 I2C 从地址,以及用于控制电机的命令接口。 英特尔® Edison 是 I2C 主地址,微控制器是 I2C 从地址 0x4。
如欲了解关于微控制器代码的更多信息,请访问:
https://github.com/ouki-wang/remeo4edison/blob/master/NG/NG.ino
请查看下表,了解机器人方向与电机方向之间的关联。
方向: | 左电机 | 右电机 |
前进 | 逆时针 | 逆时针 |
后退 | 顺时针 | 顺时针 |
向左 | 逆时针 | 顺时针 |
向右 | 顺时针 | 逆时针 |
创建一个新的 MRAA I2C 对象可实现 I2C 外设初始化。 通过调用 address()函数设置从地址。 此外,对命令缓冲区进行初始化,包含头部字节 0 和 1。 命令数据包中的其余字节将在稍后讨论。
var x = new m.I2c(0); x.address(4); var buf = new Buffer(5); buf[0] = 0x55; //Header 1 buf[1] = 0xaa; //Header 2
下面的函数使机器人向前移动(向函数传递 8 位速度值)。 函数提供 I2C 命令,以便设置左电机方向、右电机方向、左电机速度和右电机速度。 命令缓冲区其余的命令字节是命令代码(字节 2),用于控制电机方向命令或电机速度命令;命令参数(字节 3),用于设置电机方向值或电机速度值;以及校验和(字节 4) 调用write()函数可发送 I2C 命令。
function tankForward(speed) { if (speed > 0xFF) speed = 0xFF; //Left Motor CounterClockwise buf[2] = 0xB1; buf[3] = 0x1; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor CounterClockwise buf[2] = 0xB2; buf[3] = 0x1; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Left Motor Speed buf[2] = 0xC1; buf[3] = speed; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Speed buf[2] = 0xC2; buf[3] = speed; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); }
下面列出了其他的函数,可通过相似的方法来向后、向左、向后以及停止机器人。
function tankBackward(speed) { if (speed > 0xFF) speed = 0xFF; //Left Motor Clockwise buf[2] = 0xB1; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Clockwise buf[2] = 0xB2; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Left Motor Speed buf[2] = 0xC1; buf[3] = speed; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Speed buf[2] = 0xC2; buf[3] = speed; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); sleep(2000); } function tankRight() { //Left Motor Clockwise buf[2] = 0xB1; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Counter-Clockwise buf[2] = 0xB2; buf[3] = 0x1; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Left Motor Speed buf[2] = 0xC1; buf[3] = 0x90; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Speed buf[2] = 0xC2; buf[3] = 0x90; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); sleep(2000); } function tankLeft() { //Left Motor Counter-Clockwise buf[2] = 0xB1; buf[3] = 0x1; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Clockwise buf[2] = 0xB2; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Left Motor Speed buf[2] = 0xC1; buf[3] = 0xC0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Speed buf[2] = 0xC2; buf[3] = 0xC0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); sleep(2000); } function tankStop() { //Left Motor Speed buf[2] = 0xC1; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); //Right Motor Speed buf[2] = 0xC2; buf[3] = 0x0; buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF; x.write(buf); }
自动机器人:
我们构建了相应的函数基础,以便使用 MRAA 库连接传感器和制动器。现在,我们可以创建一台状态机,以支持机器人自动移动并避免碰撞。 其原理是,机器人尝试向前移动,直到检测到接近一个物体。 当接近一个物体时,它将尝试向后移动(如果其后方没有移动)。 机器人将尝试向左或向右移动以避免碰撞物体,然后再次尝试向前移动。 请查看下面的流程图,了解向前、向后、向左和向右移动的状态以及后续的实施。
流程图:
实施:
var state=0; //0-forward 1-backward 2-right 3-left var turnDirection=0; //0-right 1-left while(1) { switch (state) { //Forward State case 0: panCenter(); if (isObjectClose(10)) { state=1; //Go to Backwards State } else { tankForward(0x7F); } break; //Backward State case 1: tankStop(); while (isMotionDetected()) {sleep(100); } tankBackward(0x7F); tankStop(); state=2^turnDirection; //Go to Right or Left State break; //Right State case 2: panRight(); if (isObjectClose(10)) { state=1; } else { tankRight(); tankStop(); turnDirection ^=1; state=0; //Go to Forward State } break; //Left State case 3: panLeft(); if (isObjectClose(10)) { state=1; } else { tankLeft(); tankStop(); turnDirection ^=1; state=0; //Go to Forward State } break; default: tankStop(); //Should never get here break; } }
总结:
借助英特尔 XDK,我们展示了如何通过 WiFi 对机器人进行无线编程。 我们讨论了如何查看外设和组件的示意图,以便与 MRAA 库结合使用。 我们介绍了传感器和制动器组件,以及如何使用 Node.js 和 MRAA 库来实施相关的功能。 最后,我们介绍了自动机器人概念,并提供了流程图和实施结果。
作者简介:
Mike Rylee 是英特尔公司的一名软件工程师,曾致力于开发基于 Android*、Windows*、iOS* 和 Mac* 运行的嵌入式系统和应用。 他目前正在从事物联网项目。
声明:
本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。
英特尔明确拒绝所有明确或隐含的担保,包括但不限于对于适销性、特定用途适用性和不侵犯任何权利的隐含担保,以及任何对于履约习惯、交易习惯或贸易惯例的担保。
本文包含尚处于开发阶段的产品、服务和/或流程的信息。 此处提供的信息可随时改变而毋需通知。 联系您的英特尔代表,了解最新的预测、时间表、规格和路线图。
本文件所描述的产品和服务可能包含使其与宣称的规格不符的设计缺陷或失误。 这些缺陷或失误已收录于勘误表中,可索取获得。
索取本文件中提的、包含订单号的文件的复印件,可拨打1-800-548-4725,或登陆 www.intel.com/design/literature.htm。
英特尔、Intel 标识、Intel RealSense 和英特尔实感是英特尔在美国和/或其他国家的商标。
* 其他的名称和品牌可能是其他所有者的资产。
**该示例源代码根据英特尔示例源代码许可协议发布。喜欢 订阅添加新评论标记为垃信息圾
英特尔公司 © 2016 年版权所有。