作者:张宸豪 戚益凡 陈世达 高梓钦 谭清
单位:华北科技学院
指导老师:罗建国 韩红利
阅读对于学生的重要性毋庸置疑,因此图书馆是一个校园非常重要的组成部分,图书馆的书籍借阅,能为学生提供非常大的学习平台。而学校图书馆藏书最少近万本,按照不同的类别进行摆放,加之图书馆每天庞大的人流量形成巨大的借阅量,图书的提取成为了一个非常繁重的工作(如下图所示)。为减轻工作人员的劳动量、提高效率及缩短图书的提取时间,本团队提出设计一个智能图书搬运机器人。本设计的主要任务是成功完成要用于完成图书的提取、搬运工作。因此要求机器人具有行走、自动识别图书类别、并将图书从书架上准确提取的功能。同时机器人应考虑到在人流量大场所中工作的安全稳定以及图书馆中作业的安静运行等特性。
经过反复考察,征求各方专家老师的意见,结合现阶段学校图书馆的现状,开发研制了专用校园智能图书搬运机器人。该机器包括了自动巡线、避障、取书、送书等功能,通过自行设计的独特机械结构,实现了书架上书籍的精确提取,操作人员对机器人的指令输入,机器人自行分析路径,稳定准确的行驶至对应的书架前,并实现取书、运书一体化,节约了大量的时间、人力、财力。
1. 作品简介
智能图书搬运机器人(如下图所示),是基于校园图书馆内服务的一款机器人,该机器人具备自动循迹技术,可在图书馆内沿划定路线进行循迹自动行驶,并在达到目标位置时停止,机械臂运动,通过瞄-抽-夹-提-放5步,将目标书籍从书架上提取至机器人货仓,再循迹前往下一个目标位置。通过这些功能,可以完成帮助图书管理员取书的任务,图书管理员只需向机器人导入取书指令,机器人便可将目标书籍取回。同时,其安装有红外传感器、LED 显示屏、以及运行指示灯,可以在实际运行中,在运行轨迹上出现障碍物时紧急制动,并通过 LED 显示屏和指示灯向外界传达避让信息,保证了在人流活动场所中机器运行所必须考虑的安全性。智能图书搬运机器人具有全自动作业、工作容量大、安全稳定等优点。
2. 作品设计
2.1 机械结构
① 底盘及行走结构:通过杆件和板块形成底盘,宽广而平整,配合两侧的栏杆,形成了理想的货仓。底盘下安装有四个驱动电机,形成四轮驱动,由arduino 单独控制左右侧轮的转速,形成差速转向,运动灵活敏捷,在校园图书馆内可进行安静平稳的循迹运动,将书籍安全地送至指定地点。模块化搭建,拆卸方便,利于维护修理,方便后期进行二次改造。底盘结构如下图所示:
② 机械臂结构:机械臂由升降台和抓取臂两部分组成。升降台使用步进电机配合螺纹柱,形成抓取臂的竖直移动底座,方便完成不同书架高度的作业要求,通过对螺纹柱长度的加长,可以提高抓取臂的移动高度上限。升降台的底座,是270°舵机,其控制升降台旋转,带动了以升降台为轴的抓取臂的圆周运动,使机械臂灵活地在书架与货仓间转移。抓取臂采用两个舵机横向安装,形成两轴结构,使得抓取臂能在竖直平面上在一定范围内灵活、精准地运动,与升降台的互相配合,形成大范围作业区域广,小范围作业精度高的特点,抓取臂的头部是提取胶轮和机械爪,舵机驱动胶轮旋转,在摩擦力的作用下将目标书籍从书堆中分离,机械爪由舵机和杆件配合控制夹放,在胶轮提取后进行夹取运动,爪上套有橡皮套,在增大摩擦的同时,可以保护书籍,整套机械臂结构通仿生抽-夹-提的拿书动作,有效保证书籍提取的精确性稳定性。机械臂结构如下图所示:
2.2 运动分析
① 底盘行走分析:底盘开机,四轮默认转速为正转 130,形成匀速前进,由四个灰度传感器返回的值来判断小车巡线准确性,同时控制转向调整。若偏离航向,灰度传感器向 arduino 传递信号发生改变,Arduino 板驱动电机变速形成左右差速变向;例如,若巡线偏左,传感器数值由 1001 变为 1100,开发板接收到信号变化,控制右方两个电机速度变速为正转 205、左方两个电机变速为反转80,形成差速左转;经过不断调试后,我们将左转右转延迟设置为0.05秒,该延迟下既保证了巡线的准确度,也保证了车辆行进姿态的稳定。
② 机械臂运动分析:自下而上定义定义舵机号数为1、2、3、4、5,机械臂默认状态下为1号舵机90°、2号舵机180°、3 号舵机150°、4号舵机90°、5号舵机180°,折叠停放在货仓上方。小车抵达指定取书位置,3号舵机转至135°,前臂向上展开;1号舵机转至15°,机械臂向右转;2号舵机转至115°,机械臂前伸,胶轮压住目标书籍;随后5号舵机转至0°,胶轮开始缓慢转动,将目标书籍抽出;4号舵机转至0°,夹子抓紧,夹住目标书籍;夹取稳定后,2号舵机转至180°、1号舵机转至90°、3号舵机转至165°,机械臂回收至货仓上方;4号舵机转至45°,夹子放开;书落到小车架子上。机械臂运作示意图如下所示:
2.3 传感器应用
① 灰度传感器循迹:灰度传感器利用一只发光二极管和一只光敏电阻,通过反馈的形式给数字引脚提供具体数据。本机器采用四个灰度传感器集合的方式,进行黑色线寻迹,对线路的识别进行精准把控,arduino 控制板把四个传感器获得的数据进行位处理,对处理后的数字进行范围的判断,并通过选择语句对机器人的左转、右转、前进进行有序地调控,保证在无人操控的场景下,机器人能按照既定轨迹在取书台、阅览区、书库间执行运输任务,提高了容错率。
② 红外传感器避障:近红外传感器是一种开关量传感器,它可以检测到前方是否有物体存在,当检测到有物体存在时,传感器输出口会输出低电平;相反在未检测到前方有物体时,传感器输出口会输出一个高电平,通过电平变化向Aduino 控制板反映前方障碍物情况,我们在车辆前方安装有近红外传感器,用以调度车轮电机,在图书馆中巡线过程中,若有人进入小车循迹轨迹中,Arduino控制板接收到传感器高电平变为低电平,发出对驱动电机的停止指令,控制小车急停,前方人员离开后,红外传感器输出高电平,机器人继续执行循迹取书任务。
2.4 设备安装与调试
① 使用不同的零件拼装实体,搭载舵机电机,进行调试,调整舵机转向的角度,模拟小车转向移动使小车转向更加合理。增加灰度传感器、红外避障传感器等多项装置,增加其功能。
② 编写程序,控制抓取装置进行测试,测试出抓取装置的最远距离和最近距离,以及编写避障与循迹轨迹的程序,测试循迹和避障的实际效果,并进行轨迹调整和停留时间的修改。
③ 对轮距、机械臂位置进行调整,平衡机器人的重心,保证机器人的轮胎抓地力,提高过弯流畅性和夹取稳定性。
④ 测试夹取不同大小、厚度的书籍,观察抽取流畅度、夹取稳定性。及时对力臂长短、施压大小、夹取紧度进行调整,取综合最优值,使机器人在夹取不同类型书籍时既稳定可靠,又不会对书籍造成破坏。
⑤ 在不同的光照强度下进行模拟试验,模拟机器人穿越书库、阅览区等光照强度不同的场所,让小车完成一定轨迹的整套流程动作测试其性能使之能更加适应应用场景,并记录每次完成一整套动作的时间,用于判断节省的时间。
3. 作品创新点
3.1 胶轮式抓取结构
在图书馆中,书籍多为成排立放与书架,书与书之间紧贴放置,无法直接使用机械爪直接夹取单本书,我们思考过仿人手设计,在原型机上设置了一个手指型的爪子先将书勾出一个角,但机械臂的力道控制无法达到生物级精度,考虑到钩子会对书产生损坏,我们转变思路,采用了胶轮结构(如下图所示),使用舵机配合带橡胶圈的圆片,安装在机械臂头部,组成提取结构,可以精确地将目标书籍从书堆中分离,并且对书籍的损害降到最低。对舵机旋转角度的设置即对图书抽离最远及最近距离的设置,方便接下来机械爪进行夹取动作。
3.2 单柱式升降结构
区别于传统螺纹柱加光滑柱组成的双柱升降结构,我们采用步进电机与螺纹杆连接,螺纹杆与升降模块相互配合,后方支柱限位升降模块的旋转,形成了单柱式升降结构,如下图 所示:
为解决传统单柱升降所产生的晃动,我们将升降模块通过卡扣紧贴与后方主支柱,如下图所示,让支柱同时充当升降模块的滑轨和支柱,达成了步进电机旋转带动机械臂平稳升降的效果,该结构既节省空间,又具备了双柱式的稳定性。
3.3 遇障处理
图书馆狭小的通道内,机器人的绕行避障实现困难,结合图书馆运行场所,我们机器人前部设置红外传感器,主要用于识别在规定航线上的路人,当前部的红外传感器识别到路人时,便会通过顶部显示屏,如下图所示,发出提醒字样,提醒路人注意避让。显示屏采用 LED 显示,LED 显示清晰,提示明显。
4. 示例程序
① 机械臂代码
#include <AccelStepper.h> //调用步进电机库
#include <Servo.h>
volatile int ys;
const int moveSteps = 2000;
Servo servo_3;
Servo servo_4;
Servo servo_12;
Servo servo_8;
Servo servo_11;
const int xdirPin = 5; // 方向控制引脚
const int xstepPin = 2; // 步进控制引脚
const int xenablePin = 7; // 使能控制引脚
AccelStepper stepper1(1,xstepPin,xdirPin);//建立步进电机对象
void jingzhi() //机械臂调整到初位置
{
servo_3.write(90);
delay(0);
servo_4.write(180);
delay(0);
servo_12.write(150);
delay(0);
servo_8.write(90);
delay(0);
servo_11.write(180);
delay(0);
delay(1000);
}
void zhuaqu() //机械臂开始抓取
{
ys = 30;
servo_3.attach(3);
servo_4.attach(4);
servo_12.attach(12);
servo_8.attach(8);
servo_11.attach(11);
servo_3.write(90);
delay(0);
servo_4.write(180);
delay(0);
servo_12.write(150);
delay(0);
servo_8.write(90);
delay(0);
servo_11.write(180);
delay(0);
delay(1000);
for (int i = 180; i >= 135; i = i + (-1)) {
servo_12.write(i);
delay(ys);
}
for (int i = 90; i >= 15; i = i + (-1)) {
servo_3.write(i);
delay(ys);
}
for (int i = 180; i >= 115; i = i + (-1)) {
servo_4.write(i);
delay(ys);
}
for (int i = 120; i >= 0; i = i + (-1)) {
servo_11.write(i);
delay(ys);
}
for (int i = 90; i >= 0; i = i + (-2)) {
servo_8.write(i);
delay(ys);
}
for (int i = 115; i <= 180; i = i + (1)) {
servo_4.write(i);
delay(ys);
}
for (int i = 30; i <= 90; i = i + (1)) {
servo_3.write(i);
delay(ys);
}
for (int i = 135; i <= 165; i = i + (1)) {
servo_12.write(i);
delay(ys);
}
for (int i = 0; i <= 45; i = i + (1)) {
servo_8.write(i);
delay(ys);
}
while(true);
}
void bujin() //控制步进电机
{
if ( stepper1.currentPosition() == 0 )
{
stepper1.moveTo(moveSteps);
delay(20000);
}
else if ( stepper1.currentPosition() == moveSteps )
{
stepper1.moveTo(0);
}
stepper1.run(); //步进电机运行
}
void setup(){
pinMode(A0, INPUT);
Serial.begin(9600);
pinMode(xstepPin,OUTPUT); //步进引脚设置为输出模式
pinMode(xdirPin,OUTPUT); //方向引脚设置为输出模式
pinMode(xenablePin,OUTPUT); //使能引脚设置为输出模式
digitalWrite(xenablePin,LOW); //将使能控制引脚设置为低电平从而让电机驱动板进入工作状态
digitalWrite(xdirPin,LOW);
stepper1.setMaxSpeed(300.0); // 设置电机最大速度300
stepper1.setAcceleration(50.0); // 设置电机加速度50
}
void loop(){
Serial.println(digitalRead(A0));
if(!digitalRead(A0))
{
bujin();
zhuaqu();
}
else
{
jingzhi();
}
}
② 底盘代码
#include <U8g2lib.h> //调用显示屏库函数
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,SCL,SDA); //配置构造函数
void runfoward(); //前进
void runback(); //后退
void turnL(); //左转
void turnR(); //右转
void stop(); //停止
void xunshujia(); //小车寻找书架
void red(); //亮红灯
void off(); //灯熄灭
void green(); //亮绿灯
void pingmu(); //显示屏工作
int c=66; //定义常量
int a=205;
int b=80;
void runfoward() //前进
{
analogWrite(5, 130);
analogWrite(6, 0);
analogWrite(9, 130);
analogWrite(10, 0);
}
void runback() //后退
{
digitalWrite(5, 0);
digitalWrite(6, 130);
digitalWrite(9, 0);
digitalWrite(10, 130);
}
void turnL() //左转
{
analogWrite(5, 0);
analogWrite(6, b);
analogWrite(9, a);
analogWrite(10, 0);
}
void turnR() //右转
{
analogWrite(5, a);
analogWrite(6, 0);
analogWrite(9, 0);
analogWrite(10, b);
}
void stop() //停止
{
analogWrite(5, 0);
analogWrite(6, 0);
analogWrite(9, 0);
analogWrite(10, 0);
}
void xunshujia() //小车寻找书架
{
if (!digitalRead(A1)) //A1引脚 近红外传感器
{
stop();
red();
delay(25000);
runfoward();
delay(2000);
}
else
{
runfoward();
green();
}
}
void red() //亮红灯
{
digitalWrite( 8 , HIGH );
digitalWrite( 3 , LOW );
}
void off() //灯熄灭
{
digitalWrite( 8 , LOW );
digitalWrite( 3 , LOW );
}
void green() //亮绿灯
{
digitalWrite( 8 , LOW );
digitalWrite( 3 , HIGH );
}
void face()
{
u8g2.clearBuffer(); //清空显示屏缓存
u8g2.drawCircle(56,40,8,U8G2_DRAW_LOWER_LEFT); //画四分之一圆,圆心坐标(56,44),半径8
u8g2.drawCircle(56,40,8,U8G2_DRAW_LOWER_RIGHT); //画四分之一圆
u8g2.drawCircle(72,40,8,U8G2_DRAW_LOWER_LEFT); //画四分之一圆
u8g2.drawCircle(72,40,8,U8G2_DRAW_LOWER_RIGHT); //画四分之一圆
u8g2.drawCircle(56,41,8,U8G2_DRAW_LOWER_LEFT); //加粗画四分之一圆
u8g2.drawCircle(56,41,8,U8G2_DRAW_LOWER_RIGHT); //加粗画四分之一圆
u8g2.drawCircle(72,41,8,U8G2_DRAW_LOWER_LEFT); //加粗画四分之一圆
u8g2.drawCircle(72,41,8,U8G2_DRAW_LOWER_RIGHT); //加粗画四分之一圆
u8g2.drawLine(40,18,20,30); //画斜线,两端点坐标分别是(40,18)(20,30)
u8g2.drawLine(88,18,108,30); //画斜线
u8g2.drawLine(40,17,20,29); //加粗画斜线
u8g2.drawLine(88,17,108,29); //加粗画斜线
u8g2.sendBuffer(); //加载以上内容
}
void heart()
{
u8g2.clearBuffer(); //清空显示屏缓存
u8g2.setFont(u8g2_font_open_iconic_human_2x_t); //设置字体
u8g2.drawGlyph(58,30,66); //画心
u8g2.setFont(u8g2_font_unifont_t_chinese2); //设置字体
u8g2.drawUTF8(34,46,"ATTENTION"); //显示英文
u8g2.sendBuffer(); // 加载以上内容
}
void letter(int c)
{
u8g2.clearBuffer(); //清空显示屏缓存
u8g2.setFont(u8g2_font_unifont_t_chinese2); //设置字体
u8g2.drawUTF8(32,32,"工作中"); //显示文字
u8g2.sendBuffer(); //显示内容
}
void pingmu() //显示屏工作
{
face(); //调用函数,显示图案
delay(1000); //持续一秒钟
heart(); //调用函数,显示英文字母和图案
delay(1000); //持续一秒钟
letter(c); //调用函数,显示中文和变量
delay(1000); //持续一秒钟
}
void setup() {
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(A5, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(8, OUTPUT);
pinMode(3, OUTPUT);
Serial.begin(9600);
u8g2.begin(); //启动u8g2驱动程序
u8g2.clearBuffer(); //清空显示屏缓存
}
void loop() {
int num1, num2, num3, num4;
num1 = digitalRead(A5);
num2 = digitalRead(A2);
num3 = digitalRead(A3);
num4 = digitalRead(A4);
Serial.print(num1); //读取串口数据
Serial.print(num2);
Serial.print(num3);
Serial.print(num4);
if (num1 == 1 && num2 == 1 && num3 == 1 && num4 == 1 ) {
runfoward();
green();
delay(5);
}
else if (num1 == 1 && num2 == 0 && num3 == 1 && num4 == 1) {
turnR();
green();
delay(50);
}
else if (num1 == 1 && num2 == 1 && num3 == 0 && num4 == 1) {
turnL();
green();
delay(50);
}
else if (num1 == 0 && num2 == 0 && num3 == 0 && num4 == 0) {
stop();
red();
}
if ((!digitalRead(14))) //前方有障碍物,小车停止
{
stop();
red();
pingmu();
}
else //小车继续运行,寻找书架
{
runfoward();
green();
xunshujia();
}
}
5. 作品难点及解决方案
5.1 循迹稳定性
在实际运行中,机器人要经过书库、阅览区,环境光线变化大,且移动的人影也容易造成较大干扰,受到以上不确定因素的影响,机器人很难准确识别循迹路线。为了提高循迹稳定性我们比较了灰度传感器、近红外传感器等多种方案,最后选用灰度传感器。
我们在实验过程中,多次调整灰度传感器距离地面的高度,同时也对各传感器间距进行调整,提高了循迹精度。在转向时,常出现车辆出线和方向无法按轨迹转向的状况,我们通过降低转向时的速度,减少反应的延迟时间,让灰度传感器得到及时反应,最后通过左右两组电机间的差速,来让小车准确转向。下图为循迹行驶中的机器人:
5.2 机械臂稳定性
最初设计了四轴的机械臂,由于悬臂结构过长,轴关节过多,造成稳定性与精度不高。我们最终改用了两轴的机械臂,如下图所示,防止机械臂重量过大和重心过远,避免了结构与运行不稳的情况出现。机械臂全部使用 270°舵机控制,结合升降平台的高度调整,使得取书更加稳定可靠方便。
6. 作品前景应用
① 通过搭载多频段红外传感器、热成像传感器、室内 GPS 定位系统等传感器,将图书馆及藏书信息导入机器人信息库,辅以语音识别和检索系统,智能图书搬运机器人将来在校园图书馆内提供更加个性化、智能化的服务。
② 通过优化小车装载结构,让机器人可以实现不同类型书籍的装载,增大容错率,拓宽小车应用范围。
③ 通过小车的二次开发,应用在大型体育馆以及公共运动场馆等公共区域进行物资搬运。例如在高校突发疫情的时候运送防疫物资,可大大地降低搬运物资时间,保证物资能够及时送达学生手中,减轻防疫人员的负担。
* 更多详情请见 【S026】智能图书搬运机器人