零基础入门学用Arduino 第二部分(二)

news2024/12/23 4:17:15

 重要的内容写在前面:

  1. 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。
  2. 个人把这个教程学完之后,整体感觉是很好的,如果有条件的可以先学习一些相关课程,学起来会更加轻松,相关课程有数字电路(强烈推荐先学数电,不然可能会有一些地方理解起来很困难)、模拟电路等,然后就是C++(注意C++是必学的)
  3. 文章中的代码都是跟着老师边学边敲的,不过比起老师的版本我还把注释写得详细了些,并且个人认为重要的地方都有详细的分析。
  4. 一些函数的介绍有参考太极创客官网给出的中文翻译,为了便于现查现用,把个人认为重要的部分粘贴了过来并做了一些修改。
  5. 如有错漏欢迎指正。

视频链接:2-1 MeArm项目概述_哔哩哔哩_bilibili

太极创客官网:太极创客 – Arduino, ESP8266物联网的应用、开发和学习资料

四、开发机械臂程序

1、准备工作

(1)电路部分按下图所示连接即可。

(2)连接完成后将下面的初始化调整程序下载到开发板中,让舵机转轴转到规定的初始位置。

#include <Servo.h> 
 
Servo base, rArm, fArm, claw ;  //建立4个舵机对象
 
void setup() 
{ 
  base.attach(11);     // base 伺服舵机连接引脚11 舵机代号'b'
  rArm.attach(10);     // rArm 伺服舵机连接引脚10 舵机代号'r'
  fArm.attach(9);      // fArm 伺服舵机连接引脚9  舵机代号'f'
  claw.attach(6);      // claw 伺服舵机连接引脚6  舵机代号'c'
  Serial.begin(9600);
} 
void loop() 
{ 
  base.write(90); // 将base(底盘)舵机设置为初始位置
  delay(100);
  rArm.write(90); // 将rArm(后臂)舵机设置为初始位置
  delay(100);
  fArm.write(90); // 将fArm(前臂)舵机设置为初始位置
  delay(100);
  claw.write(90); // 将claw(钳子)舵机设置为初始位置
  delay(3000); 
} 

(3)将4个MeArm舵机摇臂按以下示意图装配到舵机上。在MeArm机械臂安装过程中不要让调整好的舵机摇臂转动,如不小心转动了已经调整好的舵机摇臂,需要将摇臂恢复图示状态或使用MeArm舵机初始化调整程序再次对舵机进行初始化调整。

(4)根据图纸或说明书或视频教程安装机械臂,安装完毕后查看电路连接是否出现问题(比如正负极短接、舵机引线接错、舵机未与Arduino共地等问题),然后再运行调试程序,看看机械臂会不会产生异动或者异响,及时调整舵机摇臂的位置或者更换有问题的舵机。

2、通过串口控制机械臂(一步到位)

(1)连接完成后将下面的程序下载到开发板中。

①全局变量及包含的头文件:

#include <Servo.h>                //使用servo库
Servo base, fArm, rArm, claw ;    //创建4个servo对象
//建立4个int型变量存储当前电机角度值,初始角度值为设备启动后初始状态所需要的电机角度数值
int basePos = 90;
int rArmPos = 90;
int fArmPos = 90;
int clawPos = 90;
//存储电机极限值(const指定该数值为常量,常量数值在程序运行中不能改变)
const int baseMIN = 0;
const int baseMAX = 180;
const int rArmMIN = 45;
const int rArmMAX = 180;
const int fArmMIN = 35;
const int fArmMAX = 120;
const int clawMIN = 25;
const int clawMAX = 100;

②初始化工作部分:

void setup()
{
  base.attach(11);     // base 伺服舵机连接引脚11 舵机代号'b'
  delay(200);          // 稳定性等待
  rArm.attach(10);     // rArm 伺服舵机连接引脚10 舵机代号'r'
  delay(200);          // 稳定性等待
  fArm.attach(9);      // fArm 伺服舵机连接引脚9  舵机代号'f'
  delay(200);          // 稳定性等待
  claw.attach(6);      // claw 伺服舵机连接引脚6  舵机代号'c'
  delay(200);          // 稳定性等待
  Serial.begin(9600); 
  Serial.println("Welcome to Taichi-Maker Robot Arm Tutorial.");   
}

③循环工作部分:

void loop()
{
  //使用串口监视器输入电机指令控制机械臂电机
  if (Serial.available() > 0) 
  { //指令举例: b45,将底盘舵机调整到45度位置
    char serialCmd = Serial.read();  //获取串口接收缓存中的一个字符
    armDataCmd(serialCmd);           //更改所记录的“当前舵机角度”
  }

  //根据记录的当前舵机角度进行设置
  base.write(basePos);
  delay(10);
  fArm.write(fArmPos); 
  delay(10);
  rArm.write(rArmPos); 
  delay(10);
  claw.write(clawPos);  
  delay(10);   
}

④更改所记录的“当前舵机角度”:

void armDataCmd(char serialCmd)
{
  Serial.print("serialCmd = ");
  Serial.print(serialCmd);  
  
  int servoData = Serial.parseInt();   //获取串口接收缓存中的整数数据作为角度值
  switch(serialCmd)    //根据命令的第一个字符判断需要控制哪个舵机
  {
    case 'b':  
      if(servoData > baseMAX) servoData = baseMAX;  //判断是否越上界
      if(servoData < baseMIN) servoData = baseMIN;  //判断是否越下界
      basePos = servoData;  //更改当前舵机角度
      Serial.print("  Set base servo value: ");Serial.println(servoData);break;
    case 'c':  
      if(servoData > clawMAX) servoData = clawMAX;  //判断是否越上界
      if(servoData < clawMIN) servoData = clawMIN;  //判断是否越下界
      clawPos = servoData;  //更改当前舵机角度
      Serial.print("  Set claw servo value: ");Serial.println(servoData);break;
    case 'f':  
      if(servoData > fArmMAX) servoData = fArmMAX;  //判断是否越上界
      if(servoData < fArmMIN) servoData = fArmMIN;  //判断是否越下界
      fArmPos = servoData;  //更改当前舵机角度
      Serial.print("  Set fArm servo value: ");Serial.println(servoData);break;
    case 'r':  
      if(servoData > rArmMAX) servoData = rArmMAX;  //判断是否越上界
      if(servoData < rArmMIN) servoData = rArmMIN;  //判断是否越下界
      rArmPos = servoData;  //更改当前舵机角度
      Serial.print("  Set rArm servo value: ");Serial.println(servoData);break;
    case 'o':  
      reportStatus();break;
    default:
      Serial.println(" Unknown Command.");
  }  
}
void reportStatus()
{
  Serial.println("");Serial.println("");
  Serial.println("++++++ Robot-Arm Status Report +++++");
  Serial.print("Claw Position: clawPos = "); Serial.println(claw.read());
  Serial.print("Base Position: basePos = "); Serial.println(base.read());
  Serial.print("Rear  Arm Position: rArmPos = "); Serial.println(rArm.read());
  Serial.print("Front Arm Position: fArmPos = "); Serial.println(fArm.read());
  Serial.println("++++++++++++++++++++++++++++++++++++");Serial.println("");
}

(2)然后进行人工调试。

①通过串口助手向Arduino发送内容“b45”,机械臂的base舵机摇臂将立刻旋转至45°的位置,同理可调试其它3个舵机。

②通过串口助手向Arduino发送内容“b200”,由于200°超出base舵机的上界180°,机械臂的base舵机摇臂将立刻旋转至上界180°的位置,同理可调试其它3个舵机的上下界。(需要注意的是,上下界指的是机械臂舵机能达到的不损坏机械臂时的最大/小角度,这个角度可以对四个舵机分别进行调试而得出,每个机械臂的舵机旋转上下界可能略有差异,但只要每个舵机都经过正确的初始化调整,差异应该是很小的)

3、通过串口控制机械臂(有缓慢转动的过程)

(1)在上例中,通过Arduino直接控制舵机旋转,会发现舵机摇臂旋转的速度非常快,然而现实中大多自动工作的机械臂都是缓慢转动的,如果每一个动作都是“一气呵成”,这将增加非常多不必要的麻烦与危险,为了让机械臂缓慢转动,可以将一次大幅度的转动分成若干次小幅度的转动完成,每次小幅度转动间隔一定的时间,这样即可实现机械臂的缓慢转动

(2)连接完成后将下面的初始化调整程序下载到开发板中,然后进行人工调试。

①全局变量及包含的头文件:

#include <Servo.h>                //使用servo库
Servo base, fArm, rArm, claw ;    //创建4个servo对象
//建立4个int型变量存储当前电机角度值,初始角度值为设备启动后初始状态所需要的电机角度数值
int basePos = 90;
int rArmPos = 90;
int fArmPos = 90;
int clawPos = 90;
//存储电机极限值(const指定该数值为常量,常量数值在程序运行中不能改变)
const int baseMIN = 0;
const int baseMAX = 180;
const int rArmMIN = 45;
const int rArmMAX = 180;
const int fArmMIN = 35;
const int fArmMAX = 120;
const int clawMIN = 25;
const int clawMAX = 100;

②初始化工作部分:

void setup()
{
  base.attach(11);     //base 伺服舵机连接引脚11 舵机代号'b'
  delay(200);          //稳定性等待
  rArm.attach(10);     //rArm 伺服舵机连接引脚10 舵机代号'r'
  delay(200);          //稳定性等待
  fArm.attach(9);      //fArm 伺服舵机连接引脚9  舵机代号'f'
  delay(200);          //稳定性等待
  claw.attach(6);      //claw 伺服舵机连接引脚6  舵机代号'c'
  delay(200);          //稳定性等待
  Serial.begin(9600); 
  Serial.println("Welcome to Taichi-Maker Robot Arm Tutorial.");   
}

③循环工作部分:

void loop()
{
  //使用串口监视器输入电机指令控制机械臂电机
  if (Serial.available() > 0) 
  { //指令举例: b45,将底盘舵机调整到45度位置
    char serialCmd = Serial.read();  //获取串口接收缓存中的一个字符
    armDataCmd(serialCmd);           //更改所记录的“当前舵机角度”
  }

  //根据记录的当前舵机角度进行设置
  base.write(basePos);
  delay(10);
  fArm.write(fArmPos); 
  delay(10);
  rArm.write(rArmPos); 
  delay(10);
  claw.write(clawPos);  
  delay(10);   
}

④更改所记录的“当前舵机角度”:(reportStatus函数的实现沿用上例即可)

void armDataCmd(char serialCmd)
{
  Serial.print("serialCmd = ");Serial.print(serialCmd);  
  int servoData = Serial.parseInt();  //获取串口接收缓存中的整数数据作为角度值
  int fromPos, toPos;
  switch(serialCmd)   //根据命令的第一个字符判断需要控制哪个舵机
  {
    case 'b':  
      fromPos = base.read();    //读取base舵机的当前角度值
      toPos = servoData;        //命令中的角度值作为调整后角度值
      if(servoData > baseMAX) servoData = baseMAX;  //判断是否越上界
      if(servoData < baseMIN) servoData = baseMIN;  //判断是否越下界
      if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值” ,每15ms向目标转动1°
        for (int i=fromPos; i<=toPos; i++){base.write(i);delay(15);}
      else                //否则“起始角度值”大于“目标角度值”,每15ms向目标转动1°
        for (int i=fromPos; i>=toPos; i--){base.write(i);delay(15);}
      basePos = servoData;
      Serial.print("  Set base servo value: ");Serial.println(servoData);break;
    case 'c':
      fromPos = claw.read();    //读取claw舵机的当前角度值
      toPos = servoData;        //命令中的角度值作为调整后角度值
      if(servoData > clawMAX) servoData = clawMAX;  //判断是否越上界
      if(servoData < clawMIN) servoData = clawMIN;  //判断是否越下界
      if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值” ,每15ms向目标转动1°
        for (int i=fromPos; i<=toPos; i++){claw.write(i);delay(15);}
      else                //否则“起始角度值”大于“目标角度值”,每15ms向目标转动1°
        for (int i=fromPos; i>=toPos; i--){claw.write(i);delay(15);}
      clawPos = servoData;
      Serial.print("  Set claw servo value: ");Serial.println(servoData);break;  
    case 'f':  
      fromPos = fArm.read();    //读取fArm舵机的当前角度值
      toPos = servoData;        //命令中的角度值作为调整后角度值
      if(servoData > fArmMAX) servoData = fArmMAX;  //判断是否越上界
      if(servoData < fArmMIN) servoData = fArmMIN;  //判断是否越下界
      if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值” ,每15ms向目标转动1°
        for (int i=fromPos; i<=toPos; i++){fArm.write(i);delay(15);}
      else                //否则“起始角度值”大于“目标角度值”,每15ms向目标转动1°
        for (int i=fromPos; i>=toPos; i--){fArm.write(i);delay(15);}
      fArmPos = servoData;
      Serial.print("  Set fArm servo value: ");Serial.println(servoData);break;
    case 'r':  
      fromPos = rArm.read();    //读取rArm舵机的当前角度值
      toPos = servoData;        //命令中的角度值作为调整后角度值
      if(servoData > rArmMAX) servoData = rArmMAX;  //判断是否越上界
      if(servoData < rArmMIN) servoData = rArmMIN;  //判断是否越下界
      if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值” ,每15ms向目标转动1°
        for (int i=fromPos; i<=toPos; i++){rArm.write(i);delay(15);}
      else                //否则“起始角度值”大于“目标角度值”,每15ms向目标转动1°
        for (int i=fromPos; i>=toPos; i--){rArm.write(i);delay(15);}
      rArmPos = servoData;
      Serial.print("  Set rArm servo value: ");Serial.println(servoData);break;
    case 'o': reportStatus();break;
    default: Serial.println(" Unknown Command.");
  }  
}

(3)然后进行人工调试。

①通过串口助手向Arduino发送内容“b45”,机械臂的base舵机摇臂将缓慢地旋转至45°的位置,同理可调试其它3个舵机。

②通过串口助手向Arduino发送内容“b200”,由于200°超出base舵机的上界180°,机械臂的base舵机摇臂将缓慢地旋转至180°的位置,同理可调试其它3个舵机的上下界。(需要注意的是,每个机械臂的舵机旋转上下界可能略有差异,但只要每个舵机都经过正确的初始化调整,差异应该是很小的)

4、通过串口控制机械臂(有设置快捷指令)

(1)电路连接完成后将下面的程序下载到开发板中。

①全局变量及包含的头文件:

#include <Servo.h>                //使用servo库
Servo base, fArm, rArm, claw ;    //创建4个servo对象
 
//存储电机极限值(const指定该数值为常量,常量数值在程序运行中不能改变)
const int baseMin = 0;
const int baseMax = 180;
const int rArmMin = 45;
const int rArmMax = 180;
const int fArmMin = 35;
const int fArmMax = 120;
const int clawMin = 25;
const int clawMax = 100;
 
int DSD = 15; //Default Servo Delay (默认电机运动延迟时间)
//此变量用于控制电机运行速度,增大此变量数值将降低电机运行速度,从而控制机械臂动作速度

②初始化工作部分:

void setup()
{
  base.attach(11);     //base 伺服舵机连接引脚11 舵机代号'b'
  delay(200);          //稳定性等待
  rArm.attach(10);     //rArm 伺服舵机连接引脚10 舵机代号'r'
  delay(200);          //稳定性等待
  fArm.attach(9);      //fArm 伺服舵机连接引脚9  舵机代号'f'
  delay(200);          //稳定性等待
  claw.attach(6);      //claw 伺服舵机连接引脚6  舵机代号'c'
  delay(200);          //稳定性等待
 
  base.write(90); delay(10);     //base 伺服舵机旋转角度初始化+稳定性等待
  fArm.write(90); delay(10);     //fArm 伺服舵机旋转角度初始化+稳定性等待
  rArm.write(90); delay(10);     //rArm 伺服舵机旋转角度初始化+稳定性等待
  claw.write(90); delay(10);     //claw 伺服舵机旋转角度初始化+稳定性等待
 
  Serial.begin(9600); 
  Serial.println("Welcome to Taichi-Maker Robot Arm Tutorial");   
}

③循环工作部分:

void loop()
{
  if (Serial.available() > 0) 
  {  
    char serialCmd = Serial.read();  //获取指令中的第一个字符
    armDataCmd(serialCmd);           //根据串行指令执行相应操作
  }
}
 
void armDataCmd(char serialCmd)
{
  if (serialCmd == 'b' || serialCmd == 'c' || serialCmd == 'f' || serialCmd == 'r')  //如果第一个字符是舵机代号
  {
    int servoData = Serial.parseInt();    //获取指令中的整数数据
    servoCmd(serialCmd, servoData, DSD);  //调用机械臂舵机运行函数(参数:舵机名,目标角度,单次延迟时间)
  } 
  else 
  {
    switch(serialCmd)
    {    
      case 'o':  //输出舵机状态信息
        reportStatus();break;
      case 'i':  //机械臂初始化
        armIniPos();break;     
      default:   //未知指令反馈
        Serial.println("Unknown Command.");
    }
  }  
}

④机械臂舵机运行函数:

void servoCmd(char servoName, int toPos, int servoDelay)
{  
  Servo servo2go;  //创建servo对象
  //串口监视器输出接收指令信息
  Serial.println("");
  Serial.print("+Command: Servo ");Serial.print(servoName);
  Serial.print(" to ");Serial.print(toPos);
  Serial.print(" at servoDelay value ");Serial.print(servoDelay);
  Serial.println(".");Serial.println("");  
  int fromPos; //建立变量,存储电机起始运动角度值
  switch(servoName)   //根据命令的第一个字符判断需要控制哪个舵机
  {
    case 'b':
      if(toPos >= baseMin && toPos <= baseMax){  //判断是否越界,越界就报错
        servo2go = base;        //把对象base拷贝到servo2go
        fromPos = base.read();  //获取当前base电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: Base Servo Value Out Of Limit!");return;}
    case 'c':
      if(toPos >= clawMin && toPos <= clawMax){  //判断是否越界,越界就报错
        servo2go = claw;        //把对象claw拷贝到servo2go
        fromPos = claw.read();  //获取当前claw电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: Claw Servo Value Out Of Limit!");return;}
    case 'f':
      if(toPos >= fArmMin && toPos <= fArmMax){  //判断是否越界,越界就报错
        servo2go = fArm;        //把对象fArm拷贝到servo2go
        fromPos = fArm.read();  //获取当前fArm电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: fArm Servo Value Out Of Limit!");return;}
    case 'r':
      if(toPos >= rArmMin && toPos <= rArmMax){  //判断是否越界,越界就报错
        servo2go = rArm;        //把对象rArm拷贝到servo2go
        fromPos = rArm.read();  //获取当前rArm电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: rArm Servo Value Out Of Limit!");return;}  
  }
  //通过对象servo2go指挥电机运行
  if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值”
    for (int i=fromPos; i<=toPos; i++){servo2go.write(i);delay(servoDelay);}
  else                  //否则“起始角度值”大于“目标角度值”
    for (int i=fromPos; i>=toPos; i--){servo2go.write(i);delay(servoDelay);}
}

⑤报告舵机当前角度函数:

void reportStatus()
{
  Serial.println("");
  Serial.println("");
  Serial.println("+ Robot-Arm Status Report +");
  Serial.print("Claw Position: "); Serial.println(claw.read());
  Serial.print("Base Position: "); Serial.println(base.read());
  Serial.print("Rear  Arm Position:"); Serial.println(rArm.read());
  Serial.print("Front Arm Position:"); Serial.println(fArm.read());
  Serial.println("++++++++++++++++++++++++++");
  Serial.println("");
}

⑥机械臂重新初始化函数:

void armIniPos()
{
  Serial.println("+Command: Restore Initial Position.");
  int robotIniPosArray[4][3] =   //使用二维数组存储4个舵机的初始化信息
  {/*  舵机代号 目标角度 单次延迟  */
    {    'b',     90,    DSD},
    {    'r',     90,    DSD},
    {    'f',     90,    DSD},
    {    'c',     90,    DSD} 
  };   
 
  for (int i = 0; i < 4; i++)  //调用4次机械臂舵机运行函数,分别初始化4个舵机
  {
    servoCmd(robotIniPosArray[i][0], robotIniPosArray[i][1], robotIniPosArray[i][2]);
  }
}

(2)然后进行人工调试。

①通过串口助手向Arduino发送内容“b45”,机械臂的base舵机摇臂将缓慢地旋转至45°的位置,同理可调试其它3个舵机。

②通过串口助手向Arduino发送内容“b200”,由于200°超出base舵机的上界180°,机械臂的base舵机不会有任何动作,同时Arduino通过串口报指令有误,同理可调试其它3个舵机。

③通过串口助手向Arduino发送内容“o”,Arduino将通过串口发送四个舵机当前的状态。

④通过串口助手向Arduino发送内容“i”,Arduino将控制四个舵机恢复初始状态。

5、通过串口控制机械臂(设有手柄控制方式)

(1)电路连接完成后将下面的程序下载到开发板中。

①全局变量及包含的头文件:

#include <Servo.h>                //使用servo库
Servo base, fArm, rArm, claw ;    //创建4个servo对象
 
//存储电机极限值(const指定该数值为常量,常量数值在程序运行中不能改变)
const int baseMin = 0;
const int baseMax = 180;
const int rArmMin = 45;
const int rArmMax = 180;
const int fArmMin = 35;
const int fArmMax = 120;
const int clawMin = 25;
const int clawMax = 100;
 
int DSD = 15; //Default Servo Delay (默认电机运动延迟时间)
//此变量用于控制电机运行速度,增大此变量数值将降低电机运行速度,从而控制机械臂动作速度

bool mode;          //记录当前的模式:mode = 1 —— 指令模式,mode = 0 —— 手柄模式
int moveStep = 3;  //每一次按下手柄按键的舵机移动量(仅适用于手柄模式)

②初始化工作部分:

void setup()
{
  base.attach(11);     //base 伺服舵机连接引脚11 舵机代号'b'
  delay(200);          //稳定性等待
  rArm.attach(10);     //rArm 伺服舵机连接引脚10 舵机代号'r'
  delay(200);          //稳定性等待
  fArm.attach(9);      //fArm 伺服舵机连接引脚9  舵机代号'f'
  delay(200);          //稳定性等待
  claw.attach(6);      //claw 伺服舵机连接引脚6  舵机代号'c'
  delay(200);          //稳定性等待
 
  base.write(90); delay(10);     //base 伺服舵机旋转角度初始化+稳定性等待
  fArm.write(90); delay(10);     //fArm 伺服舵机旋转角度初始化+稳定性等待
  rArm.write(90); delay(10);     //rArm 伺服舵机旋转角度初始化+稳定性等待
  claw.write(90); delay(10);     //claw 伺服舵机旋转角度初始化+稳定性等待
 
  Serial.begin(9600); 
  Serial.println("Welcome to Taichi-Maker Robot Arm Tutorial");   
}

③循环工作部分:

void loop()
{
  if (Serial.available()>0) 
  {  
    char serialCmd = Serial.read();  //获取指令中的第一个字符
    if(mode == 1)  //根据mode判断现在处于什么模式
    {
      armDataCmd(serialCmd);  //指令模式
    } 
    else 
    {
      armJoyCmd(serialCmd);   //手柄模式
    }
  }
}

④指令模式下的处理逻辑:

void armDataCmd(char serialCmd)
{
  //判断用户是否因搞错模式而输入错误的指令信息(即指令模式下输入手柄按键信息)
  if (   serialCmd == 'w' || serialCmd == 's' || serialCmd == 'a' || serialCmd == 'd'
      || serialCmd == '5' || serialCmd == '4' || serialCmd == '6' || serialCmd == '8' ){
    Serial.println("+Warning: Robot in Instruction Mode..."); delay(100);
    while(Serial.available() > 0) char wrongCommand = Serial.read();  //清除串口缓存中的错误指令
    return;
  }                
  if (serialCmd == 'b' || serialCmd == 'c' || serialCmd == 'f' || serialCmd == 'r'){
    int servoData = Serial.parseInt();
    servoCmd(serialCmd, servoData, DSD);  //调用机械臂舵机运行函数(参数:舵机名,目标角度,单次延迟)
  } 
  else
    switch(serialCmd)
    {   
      case 'm':    //切换至手柄模式 
        mode = 0; Serial.println("Command: Switch to Joy-Stick Mode.");break;
      case 'o':    //输出舵机状态信息
        reportStatus();break;
      case 'i':    //机械臂初始化
        armIniPos();break;  
      default:     //未知指令反馈
        Serial.println("Unknown Command.");
    }  
}

⑤手柄模式下的处理逻辑:

void armJoyCmd(char serialCmd)
{
  //判断用户是否因搞错模式而输入错误的指令信息(即手柄模式下输入舵机指令)
  if (serialCmd == 'b' || serialCmd == 'c' || serialCmd == 'f' || serialCmd == 'r'){
    Serial.println("+Warning: Robot in Joy-Stick Mode...");delay(100);
    while(Serial.available()>0) char wrongCommand = Serial.read();  //清除串口缓存中的错误指令
    return;
  } 
  int baseJoyPos, rArmJoyPos, fArmJoyPos, clawJoyPos;
  switch(serialCmd)
  {
    case 'a':  //Base向左
      Serial.println("Received Command: Base Turn Left");                
      baseJoyPos = base.read() - moveStep;  //目标角度=当前角度-单次操作移动角度
      servoCmd('b', baseJoyPos, DSD);break; //调用机械臂舵机运行函数 
    case 'd':  //Base向右
      Serial.println("Received Command: Base Turn Right");                
      baseJoyPos = base.read() + moveStep;  //目标角度=当前角度+单次操作移动角度
      servoCmd('b', baseJoyPos, DSD);break; //调用机械臂舵机运行函数        
    case 's':  //rArm向下
      Serial.println("Received Command: Rear Arm Down");                
      rArmJoyPos = rArm.read() + moveStep;  //目标角度=当前角度+单次操作移动角度
      servoCmd('r', rArmJoyPos, DSD);break; //调用机械臂舵机运行函数              
    case 'w':  //rArm向上
      Serial.println("Received Command: Rear Arm Up");     
      rArmJoyPos = rArm.read() - moveStep;  //目标角度=当前角度-单次操作移动角度
      servoCmd('r', rArmJoyPos, DSD);break; //调用机械臂舵机运行函数  
    case '8':  //fArm向上
      Serial.println("Received Command: Front Arm Up");        
      fArmJoyPos = fArm.read() + moveStep;  //目标角度=当前角度+单次操作移动角度
      servoCmd('f', fArmJoyPos, DSD);break; //调用机械臂舵机运行函数  
    case '5':  //fArm向下
      Serial.println("Received Command: Front Arm Down");        
      fArmJoyPos = fArm.read() - moveStep;  //目标角度=当前角度-单次操作移动角度
      servoCmd('f', fArmJoyPos, DSD);break; //调用机械臂舵机运行函数  
    case '4':  //Claw关闭
      Serial.println("Received Command: Claw Close Down");        
      clawJoyPos = claw.read() + moveStep;  //目标角度=当前角度+单次操作移动角度
      servoCmd('c', clawJoyPos, DSD);break; //调用机械臂舵机运行函数  
    case '6':  //Claw打开
      Serial.println("Received Command: Claw Open Up");     
      clawJoyPos = claw.read() - moveStep;  //目标角度=当前角度-单次操作移动角度
      servoCmd('c', clawJoyPos, DSD);break; //调用机械臂舵机运行函数  
    case 'm':   //切换至指令模式 
      mode = 1; 
      Serial.println("Command: Switch to Instruction Mode.");
      break;
    case 'o':   //输出舵机状态信息
      reportStatus();break;
    case 'i':   //机械臂初始化
      armIniPos();break;
    default:    //未知指令反馈
      Serial.println("Unknown Command.");return;
  }  
}

⑥报告舵机当前角度函数:

void reportStatus()
{
  Serial.println("");
  Serial.println("");
  Serial.println("+ Robot-Arm Status Report +");
  Serial.print("Claw Position: "); Serial.println(claw.read());
  Serial.print("Base Position: "); Serial.println(base.read());
  Serial.print("Rear  Arm Position:"); Serial.println(rArm.read());
  Serial.print("Front Arm Position:"); Serial.println(fArm.read());
  Serial.println("++++++++++++++++++++++++++");
  Serial.println("");
}

⑦机械臂重新初始化函数:

void armIniPos()
{
  Serial.println("+Command: Restore Initial Position.");
  int robotIniPosArray[4][3] =   //使用二维数组存储4个舵机的初始化信息
  {/*  舵机代号 目标角度 单次延迟  */
    {    'b',     90,    DSD},
    {    'r',     90,    DSD},
    {    'f',     90,    DSD},
    {    'c',     90,    DSD} 
  };   
 
  for (int i = 0; i < 4; i++)  //调用4次机械臂舵机运行函数,分别初始化4个舵机
  {
    servoCmd(robotIniPosArray[i][0], robotIniPosArray[i][1], robotIniPosArray[i][2]);
  }
}

⑧机械臂舵机运行函数:

void servoCmd(char servoName, int toPos, int servoDelay)
{  
  Servo servo2go;  //创建servo对象
  //串口监视器输出接收指令信息
  Serial.println("");
  Serial.print("+Command: Servo ");Serial.print(servoName);
  Serial.print(" to ");Serial.print(toPos);
  Serial.print(" at servoDelay value ");Serial.print(servoDelay);
  Serial.println(".");Serial.println("");  
  int fromPos; //建立变量,存储电机起始运动角度值
  switch(servoName)
  {
    case 'b':
      if(toPos >= baseMin && toPos <= baseMax){  //判断是否越界,越界就报错
        servo2go = base;        //把对象base拷贝到servo2go
        fromPos = base.read();  //获取当前base电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: Base Servo Value Out Of Limit!");return;}
    case 'c':
      if(toPos >= clawMin && toPos <= clawMax){  //判断是否越界,越界就报错
        servo2go = claw;        //把对象claw拷贝到servo2go
        fromPos = claw.read();  //获取当前claw电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: Claw Servo Value Out Of Limit!");return;}
    case 'f':
      if(toPos >= fArmMin && toPos <= fArmMax){  //判断是否越界,越界就报错
        servo2go = fArm;        //把对象fArm拷贝到servo2go
        fromPos = fArm.read();  //获取当前fArm电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: fArm Servo Value Out Of Limit!");return;}
    case 'r':
      if(toPos >= rArmMin && toPos <= rArmMax){  //判断是否越界,越界就报错
        servo2go = rArm;        //把对象rArm拷贝到servo2go
        fromPos = rArm.read();  //获取当前rArm电机角度值用于“电机运动起始角度值”
        break;
      } 
      else {Serial.println("+Warning: rArm Servo Value Out Of Limit!");return;}  
  }
  //通过对象servo2go指挥电机运行
  if (fromPos <= toPos) //如果“起始角度值”小于“目标角度值”
    for (int i=fromPos; i<=toPos; i++){servo2go.write(i);delay(servoDelay);}
  else                  //否则“起始角度值”大于“目标角度值”
    for (int i=fromPos; i>=toPos; i--){servo2go.write(i);delay(servoDelay);}
}

(2)根据程序注释进行人工调试。

6、配合HC-06蓝牙模块控制机械臂

(1)按照下图所示将电路连接好。

(2)沿用上例的程序即可,手机连接上HC-06蓝牙模块,接着打开配套软件的手柄操作界面,设置好每个键所对应的指令信息,在机械臂处于手柄操作模式的前提下对其进行调试。

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

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

相关文章

【仿真建模-anylogic】FlowchartPort原理解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-14 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 类图 2. 原理解析 2.1 核心函数 FlowchartPort继承Port类&#xff0c;并定义了一系列抽象函数&#xff1b;核心函数如下&#xff1…

基于BP神经网络对鸢尾花数据集分类

目录 1. 作者介绍2. 关于理论方面的知识介绍2.1 BP神经网络原理2.2 BP神经网络结构 3. 关于实验过程的介绍&#xff0c;完整实验代码&#xff0c;测试结果3.1 鸢尾花数据集介绍3.2 代码演示3.3 结果演示 4. 问题与分析 1. 作者介绍 侯硕&#xff0c;男&#xff0c;西安工程大学…

智能合约之路:Web3时代的商业革新之道

随着区块链技术的日益成熟和普及&#xff0c;智能合约作为其重要应用之一&#xff0c;正逐渐引领着我们进入一个全新的商业时代&#xff0c;即Web3时代。在这个时代&#xff0c;智能合约不仅改变着商业交易的方式&#xff0c;更为商业模式带来了颠覆性的革新。本文将深入探讨智…

18.9k star!一个高性能的嵌入式分析型数据库,主要用于数据分析和数据处理任务

大家好&#xff0c;今天给大家分享的是一个开源的面向列的关系数据库管理系统(RDBMS)。 DuckDB是一个嵌入式的分析型数据库&#xff0c;它提供了高性能的数据分析和数据处理能力。DuckDB的设计目标是为数据科学家、分析师和数据工程师提供一个快速、灵活且易于使用的数据分析工…

从路边摊到五星级酒店:六西格玛培训的价格与品质探秘!

当我们深入探讨市面上的六西格玛培训价格差异时&#xff0c;确实会发现不同机构之间存在着显著的差别。以张驰咨询和xx机构为例&#xff0c;两者在价格定位上形成了鲜明的对比&#xff0c;同时也展示了不同机构在教学理念和服务品质上的不同。 xx机构之所以能以亲民的价格吸引…

同三维T80005JEHVA 4K视频解码器

同三维T80005JEHVA视频解码器 可解1路4K30HDMI/VGA/CVBS1路3.5音频 可解电台音频网络流&#xff0c;可同时解4个网络流&#xff0c;分割输出 可预设十个流&#xff0c;任意切换1路流输出 <!--[endif]----><!--[if !vml]--> <!--![endif]----> 介绍&…

反贿赂管理体系认证:提升企业诚信与防范风险的双重利器

反贿赂管理体系认证在当今商业环境中发挥着至关重要的作用。这一认证不仅有助于提高企业的道德标准和社会责任感&#xff0c;还能有效防范商业风险&#xff0c;并提升内部管理水平和工作效率。 反贿赂管理体系认证要求企业制定和执行严格的反贿赂政策和程序&#xff0c;从而在…

优思学院|做质量没有前途?10年质量人想对大家说...

你是否也有过这样的困惑&#xff1f;做质量工作究竟有没有前途&#xff1f;是不是感觉每天都在重复一样的事情&#xff0c;看不到未来的希望&#xff1f; 今天&#xff0c;优思学院分享一个任职于五百强企业、有着10年经验的质量人、六西格玛黑带学生徐某的文章&#xff0c;和…

投资策略如何降低风险?WeTrade众汇一分钟分享

通过投资不同的公司、行业甚至国家&#xff0c;投资策略涉及多元化投资&#xff0c;投资者可以平衡潜在的收益与风险&#xff0c;这确实是降低风险的一种常见方法。下面WeTrade众汇分享一种更现代的投资策略&#xff0c;将指数中所有工具的资本化纳入考量&#xff0c;确保不遗漏…

cdh中的zookeeper怎么配置zoo.cfg

你手动改了zoo.cfg目录是不会生效的&#xff0c;因为是cdh在管控&#xff0c;所以只能通过cdh修改。 首先打开cdh。 xxx:7180 点击zookeeper 选配置&#xff0c;然后选高级 在右边找&#xff0c;有一个就是zoo.cfg&#xff0c;可以点击右边的感叹号。然后在里面编辑的就会直…

差分个人见解(一)

差分个人见解&#xff08;一&#xff09; 一维差分什么是差分构造差分数组差分数组的用处实战演练题目 一维差分 什么是差分 前缀和或许你已经了解了&#xff0c;差分其实就是前缀和的逆运算。 假设 a1 到 an 为 b1到 bn 的前缀和。 那么 b1 到 bn&#xff0c;分别就是 a1 到…

基于深度强化学习算法的火力-目标分配方法

源自&#xff1a;指挥控制与仿真 作者&#xff1a;李伟光 陈栋 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 摘 要 针对火力-目标分配问题解空间较大、离散、非线性等特点,提出了一种基于DQN的深度强化学习算法,通过…

算法02 递归算法及其相关问题

递归 在编程中&#xff0c;我们把函数直接或者间接调用自身的过程叫做递归。 递归处理问题的过程是&#xff1a;通常把一个大型的复杂问题&#xff0c;转变成一个与原问题类似的&#xff0c;规模更小的问题来进行求解。 递归的三大要素 函数的参数。在用递归解决问题时&…

大模型赛道有前景吗?

前言 随着人工智能技术的飞速发展&#xff0c;大模型作为新一代AI技术的核心驱动力&#xff0c;正在全球范围内掀起一场科技革命。在这个浪潮中&#xff0c;大模型赛道以其巨大的发展潜力、广泛的应用前景&#xff0c;成为了众多企业和投资者关注的焦点。本文将从多个角度探讨…

VirtualHere 允许通过网络远程使用 USB 设备,就像本地连接一样!

传统上&#xff0c;USB 设备需要直接插入计算机才能使用。有了 VirtualHere&#xff0c;就不再需要这样做&#xff0c;网络本身就变成了传输 USB 信号的电缆&#xff08;也称为 USB over IP、USB/IP、USB over WiFi、USB over Ethernet、USB 设备服务器&#xff09;。 此 USB …

振弦采集仪在水利工程中的应用与效果评估

振弦采集仪在水利工程中的应用与效果评估 河北稳控科技振弦采集仪是一种用于测量和监测结构振动的设备&#xff0c;广泛应用于水利工程中。它能够实时监测结构的振动情况&#xff0c;帮助工程师评估结构的安全性和稳定性。 在水利工程中&#xff0c;振弦采集仪主要用于以下几个…

JS 中的各种距离 scrollTop?clientHeight?

元素的各种距离 DOM 对象 属性描述offsetWidth只读&#xff0c;返回元素的宽度&#xff08;包括元素宽度、内边距和边框&#xff0c;不包括外边距&#xff09;offsetHeight只读&#xff0c;返回元素的高度&#xff08;包括元素高度、内边距和边框&#xff0c;不包括外边距&am…

VS2019中添加FFTW3库 + cmake工程

一、FFTW3官网 http://www.fftw.org/install/windows.html 二、我这里是选的64位的版本&#xff0c;可根据自己的需要选择对应的版本 三、解压得到 四、根据官网的说明&#xff0c;需进行编译生成.lib文件 4.1 在解压目录打开cmd 4.2 生成 .lib 文件 终端依次输入 lib /machine…

解决:安装MySQL 5.7 的时候报错:unknown variable ‘mysqlx_port=0.0‘

目录 1. 背景2. 解决步骤 1. 背景 吐槽1&#xff0c;没被收购之前可以随便下载&#xff0c;现在下载要注册登录吐槽2&#xff0c;5.7安装到初始化数据库的时候就会报错&#xff0c;而8.x的可以一镜到底&#xff0c;一开始以为是国区的特色问题&#xff0c;google了一圈&#x…

动力学仿真平台:让模型配置与仿真测试更高效!

背景概述 动力学仿真平台是一种基于计算机技术的模拟工具&#xff0c;旨在模拟和分析物理系统中的动力学行为。通过建立数学模型&#xff0c;并借助高效的数值计算方法来模拟复杂系统的运动规律&#xff0c;为科研、设计、工程等领域提供重要的决策支持。动力学仿真平台的重要性…