机器人制作开源方案 | 送餐机器人

news2024/11/15 17:47:19

作者:赖志彩、曹柳洲、王恩开、李雪儿、杨玉凯

单位:华北科技学院

指导老师:张伟杰、罗建国

一、作品简介

1. 场景调研

1.1项目目的

      近年来,全国多地疫情频发,且其传染性极高,食品接触是传播途径之一。疫情防控需要大量人员投入,常常出现人力紧张的情况,物资配送已经成为一大焦点问题。当前尚未有专门用于疫情送餐的机器人,因此开发一款自动化、智能化的送餐机器人,减少人员之间的接触,不仅可以提高工作效率、减轻人工劳动强度,而且可以有效减少交叉感染。

      在高校校园,学生外卖配送非常常见,但校门口琳琅满目的外卖往往被摊放在路边,甚至占用道路,造成交通混乱,因此急需一款高效便捷的送餐机器人来缓解这种情况。下图为校园人工送餐场景及外卖摊放情况。

疫情下校园送餐场景、学生点外卖场景

1.2市场调研

      我国送餐机器人行业发展起步较晚,大致经历了探索、起步、成长、爆发四个阶段。目前餐厅机器人距离真正意义上的智能化还存在技术门槛,在公共餐厅这种应用场景中,常规性的送餐、配菜需求机器人基本可以满足,但是功能比较单一。2020年疫情爆发之后,人们对于送餐机器人的需求很快上升,市场认可度不断上升,我国送餐机器人行业进入高速增长期,现在国内外疫情依然严重,送餐机器人的市场会还将不断提升扩大。

1.3项目意义

      减少人与人之间的接触是防止疫情扩散的有效手段,用机器人代替人工送餐,不仅可以代替耗时耗力的流程化作业,而且还可以避免工作人员在送餐过程中被感染的可能。而送餐机器人可以满足无接触送餐问题。

      送餐机器人开启了餐饮经营新模式,相较于传统的送餐方式,送餐机器人在疫情下减少人员接触,大大降低新冠传播率,提高食物配送效率,同时也可缓解因外卖造成的交通压力;送餐机器人对于餐厅价值有了很大的自主发挥空间,超越了传统餐饮消费的定义。

2. 创新点

2.1双工作模式

① 正常模式

      点餐者在与送餐机器人进行匹配的外卖App点餐后,送餐机器人到达指定地点,自助装取食物后,正常模式下灰度循迹传感器处于关闭状态,此时的机器人只基于终端路径来行进,利用超声波与红外避障对途中行人与障碍物避障,实现自助送餐。到达指定送餐地点后,当车身人体红外模块检测到点餐者接近时,解除安全锁定状态,装载食物的升降平台从保温箱中升起,点餐者取餐,完成一次送餐任务。

② 疫情模式

      在疫情爆发,学校处于静默期间时,送餐机器人可以通过灰度循迹传感器沿着工作人员设置的黑色送餐路径行走。送餐机器人利用超声波、红外避障等进行循迹与避障。到达指定送餐地点后,升降平台从保温装置升起食物,机械臂自动将食物放置在隔离人员房间门口,减少人员接触。

2.2高阻热保温箱

      采用质量轻、加工性好、致密度高、保温隔热效果好的聚氨酯泡沫包裹整个升降平台,达到阻热效果。保持食物原有温度,保证食物口感。

2.3数据实时显示

      机器人工作状态将在液晶显示屏右下角实时显示。小车底盘运行、机械臂与升降平台工作状态分别编号为1、2、3,正常工作状态分别为#,待机状态为*。与障碍物之间的距离间隔一秒在屏幕左下角显示。

      送餐信息实时显示在屏幕上方,时间为12:38时从A地向B地执行送餐任务,收餐者手机尾号为4404。送餐机器人在前方56厘米处有障碍物,底盘正在工作,机械臂与升降平台处于待机状态。送餐机器人整体工作正常,无异常情况出现(如下图所示)。

显示屏工作数据记录

3. 难点及解决方案

3.1转向不流畅且稳定性差

      原有机器人在循迹时因固定差速转弯时,难以适应不同转弯半径,常常因转弯半径过大导致左侧灰度传感器读取右侧灰度传感器或者右侧灰度传感器读取左侧灰度传感器,容易因为传感器数据冲突导致整体不断抖动。

      解决方案:在初步的代码基础上,利用蓝牙外加不断进行速度差调试,同时将实时速度差利用显示屏显示出来,找出最适宜的转向速度,在程序内部进行优化。并将车轮间距进行缩小,减小机器人转弯半径;在此基础之上,加入PID算法,使得速度差根据不同转弯半径不断自行调整差速。

3.2循迹时容易撞上前方或侧方障碍物

      解决方案:利用舵机搭载超声波模块,实现送餐机器人前方210度范围障碍物探测,同时每隔三秒将信号返回值(前方障碍物距离)显示在显示屏上,同时侧方和前方搭载红外避障模块,与超声波共同构成完善的避障系统。在避障程序的编译中,设置了两种代码,根据各模块运动情况实施精准避障,连续避障,合理在小车行进中添加各种采集点、定位点,实现各种不同程度的行走及障碍躲避。

      在机器人四周也加上了红外测距模块,将这些传感器与车身其他专用模块相结合,辅助机器小车在拾取点位更精准,寻迹运动更精确,对四周障碍物做出灵敏的避障。

3.3机械臂稳定性差

      原有机器人的四轴机械臂工作时因延展太长,工作时稳定性较差,容易抖动。

      解决方案:调整机械臂的结构,将原有机械臂两段式结构缩短至一段;并将驱动机械臂工作的四个舵机的偏移量设置为3,大大提高机械臂的稳定性。

3.4循迹稳定性较差

      由于对机器人的初期调试全部在室内完成,使得送餐机器人在室外工作时因光线因素造成灰度传感器的返回值产生误差。

      解决方案:通过多次对室内外机器人循迹时灰度传感器的返回值进行串口读取并记录,不断调整灰度传感的高度的间距,使其因光线差造成的返回值误差降到最低。

3.5机器人工作时重心靠前

      初期建模与组装时未考虑到机械臂工作时伸展及重量问题,导致机械臂工作时,机器人整体重心过于靠前,容易侧翻。

      解决方案:通过solidwork进行运动算例分析,调整机械臂的位置与高度并将车身适当加长(如下图所示)。

修改前后对比

4. 未来前景展望及应用

      与校园或外卖等进行合作试点,不断优化调整,尽快投入市场;改善应用方面,不再局限于送餐,增加功能通过搭载多频段红外传感器、热成像传感器、GPS定位系统等传感器,将校园及周边环境信息导入机器人信息库,辅以语音识别和检索系统,送餐机器人将在校园送餐方面为消费者提供更加个性化、智能化的服务。

送餐机器人整体图

 二、本体设计

 1. 机械结构

      首先利用solidwork建模软件开展机器人结构设计和建模,然后根据建模图样进行组装;在机构设计过程中为了保证机械结构设计合理,使其拥有更加强大的承载力、拥有更大的内部空间,本小组事先对重要机械零件(步进电机底座)进行了应力分析(如下图所示),通过不断对模型的改进和优化设计,最后得到了承载能力强且结构合理的模型。

零件应力分析

送餐机器人整体构造图

1.1底盘

      采用舵机转向、电机驱动的四轮组合结构。利用各种板块零件组合成三角稳定结构制作底盘。这种组合底盘具有稳固和灵敏两个特点:一是对路况的适应能力强,在各种复杂的路况下都可进行基本运动;二是底盘结构稳固灵敏,不限制舵机操控四轮转向位置,也为其它机械结构运转提供了充足空间。底盘及所用电机如下图所示:

送餐机器人底盘和电机

1.2机械臂

      机械臂抓取装置为四轴机械臂,抓取更加灵活,同时在机械爪端部搭配热塑性弹性橡胶,增大机械抓端部抓取餐盒时的摩擦力。

结构如下图所示:

送餐机器人机械臂装置

1.3升降平台

      用于放置餐盒(或餐包)的平台(如下图所示),利用步进电机控制其沿丝杆导轨的升降动作,机械臂将食物放置在平台上后将食物降落到保温装置内,当到达目的地后将食物从保温装置内升起,让机械臂抓取,对其要求是位置精准。

      保温装置布置在升降平台的四周,由聚氨酯泡沫包裹而成,其质量轻、可加工性好、致密度高、保温隔热效果的特点,对放入其中的食物起到保温功能。

送餐机器人升降平台与保温装置

1.4传感器与其它

      送餐机器人除车身主体与底盘外,还搭载超声波模块、红外避障模块、蓝牙模块、人体红外探测模块等多种传感器与模块,各模块与车身之间有防护板材,并进行多重加固,增强了车身的稳定性。

2. 电路控制与设备调试

2.1循迹避障

      循迹避障功能需要用超声波模块、灰度传感器、近红外传感器三者共同作用实现。

如下图所示:

超声波测距模块
红外避障模块
灰度模块

      在舵机的驱动下,超声波模块实现对车头前210度范围进行实时探测,将前方障碍物距离传给单片机,再通过内部蚁群规划算法,规划出合理的避障路线。

      当灰度传感器未识别黑线返回值为0,识别黑线后返回值为1,将返回值传回机器人内部程序,并进行判断。送餐机器人搭载四个灰度传感器,当返回值为0000,机器人前进;只要有一个传感器返回值为1时,通过PID算法对直流电机转速进行调整,控制机器左右转进而实现循迹功能。

      车头与车身两侧搭载了近红外传感器,当监测到机器附近有人通过或存在障碍物,则返回值为0,控制送餐机器人停车。

      红外探测模块探测范围为10cm,超声波模块探测距离为3m-7m。

2.2警示装置

      警示装置由蜂鸣器、车灯、显示屏三部分构成。

      送餐机器人正常工作时,显示屏显示“正常工作”字样,车头指示灯显示绿色;当超声波传感器测到障碍物距离值<50cm时,显示屏显示“行人请注意”字样,同时蜂鸣器间隔性发出“滴滴”声,车前指示灯进行闪烁,提醒路人进行避让;若距离值<16cm时,车灯加快闪烁,蜂鸣器持续发出“滴滴”声,最大程度提醒行人注意避让;当送餐机器人左右转向时,对应方向指示灯也会闪烁;运行停止时,车灯均变为红色;机械臂与升降平台旁分别接一个工作指示灯,配合显示屏使用,工作状态下显示为绿色,待机状态下为红色。

      所用蜂鸣器和指示灯如下图所示:

蜂鸣器模块
工作指示灯

2.3机械臂的运动控制

      机械臂的运动包括旋转、上下移动、机械爪的张合。

      为了实现机械臂的精准夹取与放置,使用了五个舵机控制。底部舵机控制机械臂的左右旋转,中间三个舵机控制机械臂上下移动,末端舵机控制机械爪的张合(如下图所示)。在程序中设定偏移量为3度,即舵机每次以3度转动,减少了机械臂因为惯性所产生的误差,保证了机械臂的平稳运行,使精确性大大提高。

机械臂舵机位置与运动方向

2.4升降平台的控制

      步进电机通过动力传输零件驱动丝杠旋转,丝杠带动平台上的螺母移动,移动过程中通过平台上的卡扣限制平台只能沿上下移动,进而实现平台的升降功能。

      卡扣装置与平台结构如下图所示:

升降平台卡扣装置
升降平台装置

2.5人体红外感应

      送餐机器人到达指定地点后,通过人体红外检测模块,检测人体后返回值为1,升降平台开始运行。将送餐机器人携带的人体红外感应模块设置为可重复模式,并且探测范围设定为3米内,只要在探测范围内取餐者在活动,其信号返回值始终为1。

人体红外探测传感器

2.6蓝牙无线通讯

      基于蓝牙HC-05模块以及NRF905无线收发模块(如下图所示)实现送餐机器人的远距离通讯与控制。当遇到通过的障碍时,通过终端对机器人输入指令,控制机器人运行。

HC-05 蓝牙模块与NRF905模块

三、程序代码

1. 示例程序

① 显示屏代码:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C mylcd(0x27,16,2);

byte d[]={0B00000,0B00000,0B11011,0B11011,0B00000,0B11011,0B11011,0B00000};
byte a[]={0B00000,0B00100,0B01000,0B11111,0B11111,0B01000,0B00100,0B00000};
byte b[]={0B00000,0B00000,0B00000,0B11111,0B11111,0B00000,0B00000,0B00000};
byte c[]={0B00000,0B00100,0B00010,0B11111,0B11111,0B00010,0B00100,0B00000};
byte g[]={0B00000,0B11111,0B00100,0B00100,0B00100,0B00100,0B11111,0B00000};
byte h[]={0B00000,0B01010,0B10111,0B01010,0B01011,0B01010,0B01011,0B01010};
byte e[]={0B00000,0B11111,0B00010,0B01011,0B01010,0B01010,0B11111,0B00000};
byte f[]={0B00100,0B01110,0B11111,0B01110,0B01110,0B11111,0B10101,0B00100};

void setup(){
  mylcd.init();
  mylcd.backlight();
  mylcd.createchar(1, d);
  mylcd.createchar(2, a);
  mylcd.createchar(3, b);
  mylcd.createchar(4, c);
  mylcd.createchar(5, g);
  mylcd.createchar(6, h);
  mylcd.createchar(7, e);
  mylcd.createchar(0, f);
}

void loop(){
  mylcd.setCursor(1-1, 1-1);
  mylcd.print("1");
  mylcd.setCursor(2-1, 1-1);
  mylcd.print("2");
  mylcd.setCursor(3-1, 1-1);
  mylcd.write(1);
  mylcd.setCursor(4-1, 1-1);
  mylcd.print("3");
  mylcd.setCursor(5-1, 1-1);
  mylcd.print("8");
  mylcd.setCursor(7-1, 1-1);
  mylcd.print("A");
  mylcd.setCursor(8-1, 1-1);
  mylcd.write(2);
  mylcd.setCursor(9-1, 1-1);
  mylcd.write(3);
  mylcd.setCursor(10-1, 1-1);
  mylcd.write(4);
  mylcd.setCursor(11-1, 1-1);
  mylcd.print("B");
  mylcd.setCursor(13-1, 1-1);
  mylcd.print("4");
  mylcd.setCursor(14-1, 1-1);
  mylcd.print("4");
  mylcd.setCursor(15-1, 1-1);
  mylcd.print("0");
  mylcd.setCursor(16-1, 1-1);
  mylcd.print("4");
  mylcd.setCursor(1-1, 2-1);
  mylcd.print("5");
  mylcd.setCursor(2-1, 2-1);
  mylcd.print("6");
  mylcd.setCursor(3-1, 2-1);
  mylcd.print("c");
  mylcd.setCursor(4-1, 2-1);
  mylcd.print("m");
  mylcd.setCursor(6-1, 2-1);
  mylcd.write(5);
  mylcd.setCursor(7-1, 2-1);
  mylcd.write(6);
  mylcd.setCursor(8-1, 2-1);
  mylcd.write(7);
  mylcd.setCursor(9-1, 2-1);
  mylcd.write(0);
  mylcd.setCursor(11-1, 2-1);
  mylcd.print("1");
  mylcd.setCursor(12-1, 2-1);
  mylcd.print("√");
  mylcd.setCursor(13-1, 2-1);
  mylcd.print("2");
  mylcd.setCursor(14-1, 2-1);
  mylcd.print("#");
  mylcd.setCursor(15-1, 2-1);
  mylcd.print("3");
  mylcd.setCursor(16-1, 2-1);
  mylcd.print("x");
}

② 底座代码:

#include<SoftwareSerial.h>
SoftwareSerial LZC(A5,A4);
int a=80;
int b=220;
int c=250;
int d=220;
int e=250;
void chaoshengbo();
int i=0;
void setup() {
  Serial.begin(9600);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);

  pinMode(A5, OUTPUT);
  pinMode(A4, OUTPUT);//右灯
  pinMode(2, OUTPUT);
  pinMode(A3, OUTPUT);//左灯
}
void loop() {
   xunji();  
}
//封装小车前进动作子程序
void Forward() {
  analogWrite(5, 0);
  analogWrite(6, a);   //5   6左前     6前       
  analogWrite(9, 0);  
  analogWrite(10, a);//9   10右前 v    10前
  green();
}
//封装小车后退动作子程序
void Backward() {
  analogWrite(5, a);   //
  analogWrite(6, 0);
  analogWrite(9, a);   //
  analogWrite(10, 0);
  green();
}


//封装小车停止动作子程序
void Stop() {
  analogWrite(5, 0);   //右轮后退
  analogWrite(6, 0);
  analogWrite(9, 0);   //左轮后退
  analogWrite(10, 0);
  red();
}

//封装小车自转动作子程序
void turn_Left() {
  analogWrite(5, d);  
  analogWrite(6, 0);
  analogWrite(9, 0);  
  analogWrite(10, b);
  green();
}

//封装小车自转动作子程序
void turn_Right() {
  analogWrite(5, 0);  
  analogWrite(6, b);
  analogWrite(9, d);  
  analogWrite(10, 0);
  green();
}

//封装小车大半径右转子程序
void turn_Right_1() {
  analogWrite(5, 0);  
  analogWrite(6, c);
  analogWrite(9, e);  
  analogWrite(10, 0);
  green();
}

//封装小车大半径左转子程序
void turn_Left_1() {
  analogWrite(5, e);  
  analogWrite(6, 0);
  analogWrite(9, 0);  
  analogWrite(10, c);
  green();
}

void xunji() {
  int num1, num2, num3, num4;

 
  num1 = digitalRead(11);
  num2 = digitalRead(8);
  num3 = digitalRead(3);
  num4 = digitalRead(4);
  delay(5);
  //1111   0000    1011   1101     
  if (num1 == 1 && num2 == 1 && num3 == 1 && num4 == 1) {   //1111
    Forward();
    delay(5);
  }
    else if (num1 == 1 && num2 == 0 && num3 == 1 && num4 == 1 ) {
    turn_Left();
    delay(5);
  }
 
  else if (num1 == 1 && num2 == 1 && num3 == 0 && num4 == 1 ) {
    turn_Right();
    delay(5);
  }
 
  else if (num1 == 0 && num2 == 1 && num3 == 1 && num4 == 1 ) {
    turn_Left_1();
    delay(5);
  }
  else if (num1 == 1 && num2 == 1 && num3 == 1 && num4 == 0 ) {
    turn_Right_1();
    delay(5);
  }
    else if (num1 == 0 && num2 == 0 && num3 == 1 && num4 == 1 ) {
    turn_Left_1();
    delay(30);
  }
   else if (num1 == 1 && num2 == 1 && num3 == 0 && num4 == 0 ) {
    turn_Right_1();
    delay(5);
  }
  else if (num1 == 0 && num2 == 0 && num3 == 0 && num4 == 0) {    //0000
    Stop();
  }}
void red()
{
  digitalWrite( A5 , HIGH );
  digitalWrite( A4, LOW );
  digitalWrite( A3, LOW );
  digitalWrite( 2 , HIGH );
}
void green()
{
  digitalWrite( A5 , LOW );
  digitalWrite( A4 , HIGH );
  digitalWrite( A3 , LOW );
  digitalWrite( 2 , HIGH );
}

③ 机械臂代码:

#include<SoftwareSerial.h>
SoftwareSerial LZC(A5,A4);
#include <Servo.h>
#include <AccelStepper.h>  

// 定义电机控制用常量
const int enablePin = 8;   // 使能控制引脚
const int zdirPin = 5;     // z方向控制引脚
const int zstepPin = 2;    // z步进控制引脚
int trigPin = A0;    //Trig
int echoPin = A1;    //Echo
long duration, cm=0, inches;
const int STEPS_PER_REV = 2800;
AccelStepper stepper3(1,zstepPin,zdirPin);//建立步进电机对象
char X;

volatile int sj=25;
volatile int jdu=0;
Servo servo_11;
Servo servo_8;
Servo servo_3;
Servo servo_4;
Servo servo_7;
Servo servo_12;
void setup() {
  LZC.begin(9600);
    pinMode(zstepPin,OUTPUT);     // Arduino控制A4988z步进引脚为输出模式
  pinMode(zdirPin,OUTPUT);      // Arduino控制A4988z方向引脚为输出模式
 
  pinMode(enablePin,OUTPUT);   // Arduino控制A4988使能引脚为输出模式
  digitalWrite(enablePin,LOW); // 将使能控制引脚设置为低电平从而让

  pinMode(A3, OUTPUT);
    pinMode(2, OUTPUT);                       // 电机驱动板进入工作状态
                                 
  stepper3.setMaxSpeed(300.0);     // 设置电机最大速度300
  stepper3.setAcceleration(20.0);   // 设置电机加速度20.0
  servo_11.attach(11);
  servo_8.attach(8);
  servo_3.attach(3);
  servo_4.attach(4);
  servo_7.attach(7);
  servo_12.attach(12);
  LZC.begin(9600);
   servo_7.write(45);
   servo_11.write(135);
  delay(sj);
  delay(sj);
  digitalWrite(2,HIGH);
  digitalWrite(A3,LOW);
  for (int i = 150; i >= 105; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(305-i);
    delay(sj);
  }
  for (int i = 105; i >= 70; i = i + (-1)) {
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(325-i);
    delay(sj);
  }
}

void loop() {

   if( Serial.available())
{
  X= Serial.read();
   LZC.print(X);   
}
if(LZC.available())
{
  X=LZC.read();   
  Serial.print(X);}
  switch(X){
    case 'a':
    digitalWrite(2,1);
  digitalWrite(A3,0);
    LZC.println("开始抓东西");
  servo_11.write(135);
  delay(sj);
  servo_8.write(135);
  delay(sj);
  servo_3.write(135);
  delay(sj);
  servo_4.write(135);
  delay(sj);
  servo_7.write(45);
  delay(sj);
  delay(1000);
  for (int i = 45; i <= 110; i = i + (1)) {
    servo_7.write(i);
    delay(sj);
  }
  for (int i = 135; i >= 110; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    jdu = i;
    servo_3.write(jdu/2);
    delay(sj);
  }
  for (int i = 110; i <= 150; i = i + (1)) {
    servo_4.write(i);
    delay(sj);
    servo_8.write(255-i);
    delay(sj);
  }
  for (int i = 150; i <= 190; i = i + (1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i-100);
    delay(sj);
  }
  servo_11.write(190);
  delay(sj);
  delay(1000);
  for (int i = 190; i >= 150; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    jdu = i;
    servo_3.write(i-20);
    delay(sj);
    servo_8.write(310-i);
    delay(sj);
  }
  for (int i = 150; i >= 125; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i);
    delay(sj);
    servo_8.write(345-i);
    delay(sj);
  }
  for (int i = 110; i <= 170; i = i + (1)) {
    servo_7.write(i);
    delay(sj);
  }
  servo_11.write(135);
  delay(sj);
  delay(1000);
  for (int i = 170; i >= 42; i = i + (-1)) {
    servo_7.write(i);
    delay(sj);
  }
  for (int i = 150; i >= 105; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(305-i);
    delay(sj);
  }
   delay(10000);
  break;
    case 'b':
     digitalWrite(2,HIGH);
  digitalWrite(A3,LOW);
       LZC.println("开始放东西");
    servo_11.write(135);
  delay(sj);
  servo_8.write(135);
  delay(sj);
  servo_3.write(135);
  delay(sj);
  servo_4.write(135);
  delay(sj);
  servo_7.write(45);
  delay(sj);
  for (int i = 45; i <= 170; i = i + (1)) {
    servo_7.write(i);
    delay(sj);
  }
  for (int i = 135; i >= 105; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_8.write(i+45);
    delay(sj);
    servo_3.write(i-10);
    delay(sj);
  }
  for (int i = 135; i <= 260; i = i + (1)) {
    servo_8.write(i);
    delay(sj);
  }
  servo_11.write(190);
  delay(sj);
  for (int i = 260; i >= 155; i = i + (-1)) {
    servo_8.write(i);
    delay(sj);
  }
  for (int i = 170; i >= 110; i = i + (-1)) {
    servo_7.write(i);
    delay(sj);
  }
  for (int i = 90; i <= 150; i = i + (1)) {
    servo_4.write(i);
    delay(sj);
    servo_8.write(i+10);
    delay(sj);
    servo_3.write(i-40);
    delay(sj);
  }
  delay(1000);
  servo_11.write(135);
  delay(sj);
  delay(1000);
  for (int i = 110; i >= 45; i = i + (-1)) {
    servo_7.write(i);
    delay(sj);
  }
  for (int i = 150; i >= 105; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i-10);
   
    delay(sj);
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(305-i);
    delay(sj);
  }
  for (int i = 105; i >= 70; i = i + (-1)) {
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(325-i);
    delay(sj);
  }
  delay(10000);
        break;       
    case 'c':
    {digitalWrite(2,0);
  digitalWrite(A3,1);
      LZC.println("步进电机开始运动");
  digitalWrite(zdirPin,LOW);
 
  // 电机慢速旋转
  for(int x = 0; x < STEPS_PER_REV; x++) {
    digitalWrite(zstepPin,HIGH);
    delayMicroseconds(2000);
    digitalWrite(zstepPin,LOW);
    delayMicroseconds(2000);
  }
  // 等待一秒
  delay(10000);
  break;
    }
  case 'd':
  {   digitalWrite(2,0);
  digitalWrite(A3,1);
    LZC.println("步进电机开始运动");
  // 设置电机逆时针旋转
  digitalWrite(zdirPin,HIGH);
 
  // 电机快速旋转
  for(int x = 0; x < (STEPS_PER_REV * 2); x++) {
    digitalWrite(zstepPin,HIGH);
    delayMicroseconds(1000);
    digitalWrite(zstepPin,LOW);
    delayMicroseconds(1000);
  }
  // 等待一秒
  delay(10000);
    break;
}
case 'e':
{digitalWrite(2,0);
  digitalWrite(A3,1);
  LZC.println("云台开始运动");


  for (int i = 0; i <= 90; i = i + (1)) {
    servo_12.write(i);
    delay(15);
  }
  for (int i = 90; i >= 0; i = i + (-1)) {
    servo_12.write(i);
    delay(15);
  }
  break;
   
}
case 'f':
{digitalWrite(2,0);
  digitalWrite(A3,1);
   for (int i = 150; i >= 105; i = i + (-1)) {
    servo_4.write(i);
    delay(sj);
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(305-i);
    delay(sj);
  }
  for (int i = 105; i >= 70; i = i + (-1)) {
    servo_3.write(i-10);
    delay(sj);
    servo_8.write(325-i);
    delay(sj);
  }
  delay(15000);
  } }}

2. 资料下载

资料内容:

①程序源代码

②模型3D文件

③仿真渲染文件

④关键零件应力分析图

资料下载地址:送餐机器人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/901360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis之List类型解读

目录 List简介 数据结构 常见命令 概述 ​LPUSH key value1 [value2] ​ LPUSHX key value LINDEX key index LLEN key LPOP key LRANGE key start stop List简介 列表list是一个单键多值的 Redis 列表是简单的字符串列表&#xff0c;按照插入顺序排序。你可以添加…

k8s 常见面试题

前段时间在这个视频中分享了 https://github.com/bregman-arie/devops-exercises 这个知识仓库。 这次继续分享里面的内容&#xff0c;本次主要以 k8s 相关的问题为主。 k8s 是什么&#xff0c;为什么企业选择使用它 k8s 是一个开源应用&#xff0c;给用户提供了管理、部署、扩…

Python将网络文件下载到本地

Python将网络文件下载到本地 前言相关介绍Python将网络文件下载到本地 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入Python日常小操作专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看基于DETR的人脸伪…

Kubernetes 安全机制 认证 授权 准入控制

客户端应用若想发送请求到 apiserver 操作管理K8S资源对象&#xff0c;需要先通过三关安全验证 认证&#xff08;Authentication&#xff09;鉴权&#xff08;Authorization&#xff09;准入控制&#xff08;Admission Control&#xff09; Kubernetes 作为一个分布式集群的管理…

FreeCAD的傻瓜式初级使用教程

起因&#xff1a;自己想DIY一套线性手刹和序列档&#xff0c;以便和我之前的freejoy控制器相连接应用&#xff0c;需要自己制图和在某宝找代加工的商家&#xff0c;但我又不想安装体积巨大的AutoCAD&#xff0c;所以找了以下开源、免费的解决方案&#xff0c;所以就有了这篇文章…

这是真的“技术驱动”的公司吗?

“ 软件交付团队的DevOps能力&#xff0c;恰恰是技术能力的最好体现。” 01 — “我们是技术驱动的公司” 跟我们合作的软件供应商&#xff0c;每次发生软件变更或升级就一地鸡毛&#xff0c;而且经过屡次沟通&#xff0c;都没有什么本质改善。 当我们跟他们的高层投诉时&#…

JVM面试题-1

1、什么是JVM内存结构&#xff1f; jvm将虚拟机分为5大区域&#xff0c;程序计数器、虚拟机栈、本地方法栈、java堆、方法区&#xff1b; 程序计数器&#xff1a;线程私有的&#xff0c;是一块很小的内存空间&#xff0c;作为当前线程的行号指示器&#xff0c;用于记录当前虚拟…

数据结构:队列Queue详解

文章目录 一、队列的概念和特点二、队列的使用三、队列的简单实现四、循环队列 一、队列的概念和特点 队列:只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。进行插入操作的一端称为队尾&#xff0c;删除操作的一端称队头。 入队列&#xff…

使用Nginx调用网关,然后网关调用其他微服务

问题前提&#xff1a;目前我的项目是已经搭建了网关根据访问路径路由到微服务&#xff0c;然后现在我使用了Nginx将静态资源都放在了Nginx中&#xff0c;然后我后端定义了一个接口访问一个html页面&#xff0c;但是html页面要用到静态资源&#xff0c;这个静态资源在我的后端是…

windows服务器下java程序健康检测及假死崩溃后自动重启应用、开机自动启动

前两天由于项目需要&#xff0c;一个windows上的批处理任务&#xff08;kitchen.bat&#xff09;&#xff0c;需要接到mq的消息通知后执行&#xff0c;为了快速实现这里我们通过springboot写了一个jar程序&#xff0c;用于接收mq的消息&#xff0c;并调用bat文件。 本程序需要实…

【欧拉计划】偶数斐波那契数

题目链接&#xff1a;偶数斐波那契数 解法一&#xff1a;暴力枚举 看见题目&#xff0c;第一反应就是先找到小于400万的所有斐波那契数&#xff0c;再从这些斐波那契数中筛选出偶数进行求和。 由于递归方法求斐波那契数的时间复杂度较高&#xff0c;故这里采用迭代的方法。 先…

C++笔记之全局函数做友元与类做友元

C笔记之全局函数做友元与类做友元 code review! 文章目录 C笔记之全局函数做友元与类做友元1.全局函数做友元2.类作友元 1.全局函数做友元 代码 #include <iostream> using namespace std;class MyClass { private:int x; public:MyClass(int a) : x(a) {}friend void…

互斥锁概念使用

互斥锁的创建两种方式 1.动态方式 #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <string.h> FILE *fp; void *func2(void *arg) {pthread_detach(pthread_self());printf("this is func2 thread\n");char str2[]…

mybatis入门的环境搭建及快速完成CRUD(增删改查)

又是爱代码的一天 一、MyBatis的介绍 ( 1 ) 背景 MyBatis 的背景可以追溯到 2002 年&#xff0c;当时 Clinton Begin 开发了一个名为 iBATIS 的持久化框架。iBATIS 的目标是简化 JDBC 编程&#xff0c;提供一种更直观、易用的方式来处理数据库操作。 在传统的 JDBC 编程中&…

【大数据】Flink 详解(五):核心篇 Ⅳ

Flink 详解&#xff08;五&#xff09;&#xff1a;核心篇 Ⅳ 45、Flink 广播机制了解吗&#xff1f; 从图中可以理解 广播 就是一个公共的共享变量&#xff0c;广播变量存于 TaskManager 的内存中&#xff0c;所以广播变量不应该太大&#xff0c;将一个数据集广播后&#xff0…

代码随想录算法训练营day39 | 62. 不同路径,63. 不同路径 II

目录 62. 不同路径 63. 不同路径 II 62. 不同路径 类型&#xff1a;动态规划 难度&#xff1a;medium 思路&#xff1a; 应用二维数组的动态规划&#xff0c;到达某个方格的方法数目&#xff0c;为这个方格的上一个方格和左一个方格的方法数目和。 需要先初始化第一行和第一…

ubuntu安装Microsoft Edge并设置为中文

1、下载 edge.deb 版本并安装 sudo dpkg -i microsoft-edg.deb 2. 设置默认中文显示 如果是通过.deb方式安装的&#xff1a; 打开默认安装路径下的microsoft-edge-dev文件&#xff0c;在文件最开头加上: export LANGUAGEZH-CN.UTF-8 &#xff0c;保存退出。 cd /opt/micr…

【Redis】什么是缓存击穿,如何预防缓存击穿?

【Redis】什么是缓存击穿&#xff0c;如何预防缓存击穿&#xff1f; 缓存击穿是指一个 Key 非常热点&#xff0c;大并发集中对这一个点进行访问&#xff0c;当这个Key 在失效的瞬间&#xff0c;持续的大并发就会穿破缓存&#xff0c;直接请求数据库。缓存击穿和缓存雪崩的区别…

代码随想录-字符串01 344.反转字符串541. 反转字符串II替换空格翻转字符串里的单词左旋转字符串

题目目录 ● 344.反转字符串 ● 541. 反转字符串II ● 剑指Offer 05.替换空格 ● 151.翻转字符串里的单词 ● 剑指Offer58-II.左旋转字符串 344.反转字符串 344.反转字符串 很经典的字符串考察点&#xff0c;考察对双指针的熟悉程度。 解法是通过双指针从字符串数组两边向中…

JavaScript函数式编程【进阶】

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…