电子说
这篇文章来源于DevicePlus.com英语网站的翻译稿。
点击此处阅读本文的第1部分 >
在第1部分中,我们制作了一块定制的亚克力板底座,把NEMA 17步进电机安装到了底盘上。然后,我们将Arduino Uno 3和电机开发板也连到亚克力底座上,并安装了电机开发板库。在第2部分中,我们将添加机器人工作所需的其他系统部件,比如伺服器和激光测距仪(LRF),并编写一个程序,让机器人能够自主移动。
有关如何利用Arduino UNO R3从零开始搭建轮式机器人的信息,请参考我们在之前的文章《如何制作自己的机器人》 和《如何制作自己的机器人(第2部分)》。在本文中,我们将进一步增加机器人的功能:让该机器人动起来并为其增加激光测距(LRF)功能,以使该设备能够检测物体并测量距离。
该机器人的设计目标如下:
向前、向后移动;向左和向右旋转90度;向左和向右旋转45度。
避开障碍物时根据最佳可用路径朝不同的方向移动。
测量各个方向(前方、左右90度、左右45度)的距离。
根据可用的最长距离,确定朝哪个方向前进(向前、向后、向左或向右)。
硬件
制作此机器人所需的硬件请参见以下硬件明细,这些零部件在许多电商处都可以买到。
Arduino Uno rev 3
适用于Arduino的Adafruit步进电机开发套件
步进电机
底盘、螺丝和尾轮 Parallax.com
车轮、橡胶轮胎和轮毂 Makeblock.com
伺服装置和安装套件
Parallax激光测距仪
OLED显示屏
红色包装纸/盒
锂聚合物电池3型(11.1V)和连接器
195 x 195 x 3mm亚克力板
软件
Arduino IDE
Adafruit Motor Shield Library
LCD Display9696 Library.zip
工具
圆锉
Dremel 电动工具
电烙铁
迷你钢锯
安装并测试伺服器
下一步是安装用于平移的伺服器。首先,请用螺钉将两块小板拧到伺服基座上,然后用螺钉将其固定到亚克力底座上,如图1和图2所示。请用2个螺钉将铝制安装架安装到伺服器的顶部。
图1.用两小板将伺服器固定到亚克力底座上
图2.将铝制安装架安装到伺服器上
如图3所示,将伺服器的接头连至电机开发板。电机开发板上有2个伺服器接头,分别标记为“servo1”和“servo2”。本次连接请使用servo1接头(外侧的那个)。请注意,切勿接反。
图3.伺服与电机开发板的连接
现在,我们可以运行程序了,请将以下代码上传到Arduino。无需安装新库,系统所需的库(Servo.h)都已包含在Arduino IDE程序中。伺服器应使用Digital引脚10(servo 1),或者如果伺服器连接到电机开发板的servo 2,那么应使用引脚11。
//*********************************************************************************************** #include Servo panMotor; // servo for laser range finder (lrf) scanning int pos = 0; // variable to store the servo position const int a = 1000; void setup() { panMotor.attach(10); } // Attach Servo for scanning to pin 10 void loop() { // *************************** Scan Left *********************************** panMotor.write(180); // 90 degree delay(a); panMotor.write(135); // 45 degree delay(a); // ************************** Scan Right ********************************** panMotor.write(45); // 45 degree delay(a); panMotor.write(0); // 90 degree delay(a); // ************************* Neutral position ***************************** panMotor.write(90); delay(a); } //**************************************************************************************************
代码很简单,它负责让机器人从左向右进行扫描并返回原始位置。
伺服器的工作原理请参见以下视频:
安装激光测距仪(LRF)
激光测距仪产品文档– Parallax
Parallax激光测距仪(LRF)模块是一款利用激光技术计算仪器到目标物体的距离的测量仪器。该设备根据光学三角测量法(激光、摄像头和物体质心之间的简单三角函数)来计算仪器到目标物体的距离。其最佳测量范围为6–48英寸(15–122厘米),测量精度误差<5%(平均3%)。
硬件安装很简单。只需在亚克力板上钻两个与LRF位置相匹配的孔,然后用塑料垫片和螺钉将LRF固定到铝底盘上即可(请参见图5)。
图4.将LRF电缆连至电机开发板
图5.安装在铝制安装架上的LRF
由于LRF的最佳测量值上限为122厘米,我们需要将铝制安装架稍微向前弯曲,以使该范围始终小于120厘米(图6)。
图6.向前弯曲铝制安装架,使激光到地的距离小于120厘米
请完全按照图7将电缆接头连至LRF。GND接地,VCC接5V,SOUT接引脚8,SIN接引脚9。
图7. LRF与电机开发板的连接
我们已经完成了LRF的安装和连接,现在就来上传代码吧!同样,无需安装库。我们要用的SoftwareSerial.h已经包含在Arduino IDE中。
下面的代码源自示例代码,我们只是进行了一些修改,将距离数据从字符串转换为整数。其作用是测量传感器到前方物体的距离并打印结果。我们用串行监视器显示结果。
// ************************************************************************************************************************************************** #include #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output (connects to the LRF's SIN pin) #define ledPin 13 // Most Arduino boards have an on-board LED on this pin #define BUFSIZE 16 // Size of buffer int lrfDataInt; SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); void setup() // Set up code called once on start-up { // *************************************** setup for LRF *********************************************** pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready Serial.println("nnParallax Laser Range Finder"); lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor } // ****************************************** main loop ************************************************ void loop() // Main code, to run repeatedly { lrf(); } // ****************************************** end main loop ********************************************* void lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) // If there are any bytes available to read, then the LRF must have responded { lrfData[offset] = lrfSerial.read(); // Get the byte and store it in our buffer if (lrfData[offset] == ':') // If a ":" character is received, all data has been sent and the LRF is ready to accept the next command { lrfData[offset] = 0; // Null terminate the string of bytes we just received break; } // Break out of the loop offset++; // Increment offset into array if (offset >= BUFSIZE) offset = 0; // If the incoming data string is longer than our buffer, wrap around to avoid going out-of-bounds } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.print("Distance = "); // The lrfData string should now contain the data returned by the LRF, so display it on the Serial Monitor Serial.println(lrfDataInt); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor digitalWrite(ledPin, LOW); // Turn LED off delay(1000); } //*************************************************************************************************************************************************
串行监视器显示的结果如下所示。所有尺寸的单位均为毫米。
安装OLED显示屏
首先,我们将OLED塑料盒安装到亚克力底座中(请参见图9),然后再将OLED显示屏附带的线缆一端连至显示屏。要将显示屏连至电机开发板,请将线缆另外一端jst接头上的线缆剪下,然后将红导线焊至5V,将黑导线焊至Ground,将黄导线焊至SDA引脚,将绿导线焊至SCL引脚。请确保OLED显示屏的背面朝外。
图8 . OLED与电机开发板之间的连接
将OLED固定在底座上并连接电缆后,我们就可以运行部分软件了。
首先,请确保已经安装了SeeedOLED.h。然后,将以下代码上传到Arduino。该代码使用了名为oled1的函数,稍后的最终编码也会使用该函数。其基本功能就是显示100到109的数字。
//***************************************************************************************************************************************** #include #include int distanceFwd; void setup() { Wire.begin();} void loop() { int i = 0; for (i; (i < 10); i ++) { distanceFwd = 100 + i; oled1(); delay(1000); } } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //*****************************************************************************************************************************************
程序正常运行时,显示屏应该会显示以下视频中的内容:
安装最终代码
现在,我们已经完成了所有硬件的安装并测试了各个设备。让我们把所有软硬件结合起来,构建一个可以自主移动的智能激光机器人吧。最终程序会执行以下功能:
测量前方距离
如果距离超过70厘米,机器人将向前移动500步(大约50厘米);
如果距离大于40厘米小于70cm,机器人将向前移动200步(20厘米);
如果距离小于40厘米,机器人会向左扫描90度,向左扫描45度,向右扫描45度,向右扫描90度;
测量每个方向的距离,然后计算哪个方向的测量距离最长;
转向距离最长的那个方向;
返回第一步。
请复制以下代码并将其上传到Arduino:
//********************************************************************************************************************* #include #include #include "utility/Adafruit_MS_PWMServoDriver.h" #include #include #include #define ledPin 13 #define BUFSIZE 16 #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output(connects to the LRF's SIN pin) SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); // Size of buffer (in bytes) for incoming data // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // Connect a stepper motor with 200 steps per revolution (1.8 degree) Adafruit_StepperMotor *myMotor1 = AFMS.getStepper(200, 1); // motor port #1 (M1 and M2) Adafruit_StepperMotor *myMotor2 = AFMS.getStepper(200, 2); // motor port #2 (M3 and M4) Servo panMotor; // servo for laser range finder (lrf) scanning int leftDistance1; int leftDistance2; int rightDistance1; int rightDistance2; int maxDistance; int angleTurn; int directions; int distanceFwd; const int a = 30; //*********************************************************************************start set up ************************** void setup() { Serial.begin(9600); // set up Serial library at 9600 bps panMotor.attach(10); // Attach Servo for scanning to pin 10 AFMS.begin(); // create with the default frequency 1.6KHz myMotor1->setSpeed(100); // Set stepmotor1 speed at 100 rpm myMotor2->setSpeed(100); // Set stepmotor2 speed at 100 rpm pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); // Input pin for LRF pinMode(txPin, OUTPUT); // Output pin for LRF digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor panMotor.write(90); delay(a); } //******************************************************************* start Loop ***************************************** void loop() { distanceFwd = lrf(); maxDistance = distanceFwd; oled1(); if (distanceFwd > 700) { Motor(500,1);} else if (distanceFwd > 400) { Motor(200,1);} else // if path is blocked { checkTurn(); turn();} } //***************************************************************** check turn function ******************************** void checkTurn() { digitalWrite(ledPin, HIGH); // ************************** Scan Left *********************************** panMotor.write(180); delay(a); leftDistance1 = lrf(); panMotor.write(135); delay(a); leftDistance2 = lrf(); oled(); // *************************** Scan Right ********************************* panMotor.write(45); delay(a); rightDistance2 = lrf(); panMotor.write(0); delay(a); rightDistance1 = lrf(); oled(); panMotor.write(90); digitalWrite(ledPin, LOW); // ************************************ Turn Left ************************ maxDistance = leftDistance1; angleTurn = 100; directions = 0; if (maxDistance <= leftDistance2) {angleTurn = 50; maxDistance = leftDistance2; directions = 0; } //*********************************** Turn Right *********************** if (maxDistance <= rightDistance2) {angleTurn = 50; maxDistance = rightDistance2; directions = 1; } if (maxDistance <= rightDistance1) {angleTurn = 100; maxDistance = rightDistance1; directions = 1; } // ******************************* Turn Back****************************** if ((leftDistance1 < 300) && (rightDistance1 <300) && (distanceFwd <300)) {angleTurn = 200; directions = 3; } } //************************************************ turn function ********************************************************* void turn() { rightDistance1 = 0; rightDistance2 = 0; leftDistance1 = 0; leftDistance2 = 0; if (directions == 0) // turn left { Motor(angleTurn,3);} if (directions == 1) // turn right { Motor(angleTurn,4);} if (directions == 3) // turn back { Motor(angleTurn,4);} } //*************************************** Stepper Motor function **************************************************** void Motor(int x,int y) { int i = 0; for ( i; (i < x); i ++) { if (y == 1) // move forward {myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} if (y == 2) // move backward {myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 3) // move left { myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 4) // move right { myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} } } //*********************************************************************** LRF function ******************************* long lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int lrfDataInt; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) { lrfData[offset] = lrfSerial.read(); if (lrfData[offset] == ':') { lrfData[offset] = 0; break;} offset++; if (offset >= BUFSIZE) offset = 0; } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.flush(); digitalWrite(ledPin, LOW); return lrfDataInt; } //********************************************************* Oled function ************************************************ void oled() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(0,0); SeeedOled.putString("Left 1:"); SeeedOled.setTextXY(0,12); SeeedOled.putNumber(leftDistance1); SeeedOled.setTextXY(2,0); SeeedOled.putString("Left 2:"); SeeedOled.setTextXY(2,12); SeeedOled.putNumber(leftDistance2); SeeedOled.setTextXY(4,0); SeeedOled.putString("Right 1:"); SeeedOled.setTextXY(4,12); SeeedOled.putNumber(rightDistance1); SeeedOled.setTextXY(6,0); SeeedOled.putString("Right 2:"); SeeedOled.setTextXY(6,12); SeeedOled.putNumber(rightDistance2); } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //***********************************************************************************************************************************
图9. OLED显示屏安装在亚克力底座上的最终效果
结论
在之前的文章《如何制作自己的机器人》和《如何制作自己的机器人(第2部分)》中,我们用步进电机制作了一款简单的轮式机器人。这次,我们对其进行了功能改进:为机器人增加了激光测距仪(LRF)功能,并且安装了轮子,让机器人能够自由移动。我一直想制作一款能够进行测量的设备。在本例中,凭借激光传感器,我们的机器人不仅能够检测并避开物体,同时还能获取更准确的距离数据。激光机器人还有许多其他应用场景。您也可以利用该激光传感器设计自己喜欢的有趣项目。
接下来,我们会做一些更炫酷的事情,敬请期待!
Purnomo Nuhalim
来自墨尔本的Purnomo是一名退休人员,也是电子发烧友。目前,他正使用Arduino和Raspberry Pi从事各种开放式硬件项目的研发。除了电子学,他还对航空建模和天文学充满热情。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !