arduino总线舵机+实时串口通信控制

arduino语法

digitalWrite()函数

digitalWrite(pin,value);  //定义引脚的电平
  • pin*:*你希望设置模式的引脚的编号 0~13

  • value表示为HIGH(高电平)或LOW(低电平)

digitalRead函数

引脚电平读取函数

int digitalRead(pin)

数字IO口读输入电平函数,读出数字接口的值 比如可以读数字传感器。当感测到脚位处于高电位时时回传HIGH,否则回传LOW。

数字IO口读输入电平函数,读出数字接口的值 比如可以读数字传感器。当感测到脚位处于高电位时时回传HIGH,否则回传LOW。

范例:

val = digitalRead(7); // 读出脚位 7 的值并指定给 val

analogRead()函数

int analogRead(pin)

pin 要读取的模拟输入引脚的编号(Arduino Diecimila为0~5,Arduino nano为0~7 Mega上为0至15) 模拟IO口读函数,从指定的模拟接口读取值,Arduino对该模拟值进行10-bit的数字转换。 此函数返回0到1023之间的数字,表示0到5伏特之间的电压。例如,如果施加到编号0的引脚的电压为2.5V,则analogRead(0)返回512。

通过使用analogRead()函数,我们可以读取施加到其中一个引脚的电压。比如可以读模拟传感器(10位AD,0~5V表示为0~1023)。

analogWrite函数

analogWrite(pin, value);

数字IO口PWM输出函数,给一个接口写入模拟值(PWM波)改变PWM脚位的输出电压值可用于电机PWM调速或音乐播放。

  • pin:对于 ATmega168芯片的Arduino(包括Mini或BT),该函数可以工作于 3, 5, 6, 9, 10和 11号接口

  • value表示为0~255

delay(ms) 延时函数

delay (ms) ;
1

delay(ms) 延时函数(单位ms),延时一段时间,暂停晶片执行多少毫秒,

delay(1000)为一秒。

attachInterrupt()函数

attachInterrupt(interrupt,function,mode)

interrupt:中断源(在Arduino中,中断源可选值为0或1,一般分别对应2号和3号引脚)

function:需要中断的函数名

mode:

LOW(低电平触发)、

CHANGE(在引脚电平变化时触发)、

RISING(低电平变为高电平触发)、

FALLING(高电平变为低电平触发)

detachInterrupt()函数

detachInterrupt(interrupt)

中断开关函数,interrupt=1 开,interrupt=0 关。

范例:

detachInterrupt(1)//打开中断
中断使能函数

interrupts()// 使能中断

noInterrupts() //禁止中断

串口字符串

该函数可以将arduino接收到的串口字符串数据转换成十进制整形,以便于之后的使用和编程。

#define numdata_length 2
String comdata = "";
int numdata[numdata_length] = {0};
int flag = 0;
void setup() {
  Serial.begin(9600);
  }
void loop() {
int j = 0;
//不断循环检测串口缓存,一个个读入字符串
while (Serial.available() > 0)
{
  comdata += char(Serial.read());
  delay(2);
  flag = 1;
}
//如果接收到数据则执行comdata分析操作,否则什么都不做
if(flag == 1) {
for(int i = 0; i < comdata.length() ; i++){
  if(comdata[i] == ','){
    j++;
    }
  else{
    numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
    }
  }
comdata = String("");
flag = 0;
for(int i = 0; i < numdata_length; i++){
  Serial.println(numdata[i]);
  numdata[i] = 0;
  }
}
}

//定义一个comdata字符串变量,赋初值为空值
    String comdata = "";
    //numdata是分拆之后的数字数组
    int numdata[6] = {0}, PWMPin[6] = {3, 5, 6, 9, 10, 11}, mark = 0;
    void setup()
    {
    //定义0~6脚是输出
      for(int i = 0; i < 6; i++) pinMode(PWMPin[i], OUTPUT);
      Serial.begin(9600);
    }
     
    void loop()
    {
    //j是分拆之后数字数组的位置记数
      int j = 0;
     
      //不断循环检测串口缓存,一个个读入字符串,
      while (Serial.available() > 0)
      {
      //读入之后将字符串,串接到comdata上面。
        comdata += char(Serial.read());
          //延时一会,让串口缓存准备好下一个数字,不延时会导致数据丢失,
        delay(2);
        //标记串口读过数据,如果没有数据的话,直接不执行这个while了。
        mark = 1;
      }
     
      if(mark == 1)  //如果接收到数据则执行comdata分析操作,否则什么都不做。
      {
      //显示刚才输入的字符串(可选语句)
        Serial.println(comdata);
          //显示刚才输入的字符串长度(可选语句)
        Serial.println(comdata.length());
     
    /*******************下面是重点*******************/
    //以串口读取字符串长度循环,
        for(int i = 0; i < comdata.length() ; i++)
        {
        //逐个分析comdata[i]字符串的文字,如果碰到文字是分隔符(这里选择逗号分割)则将结果数组位置下移一位
        //即比如11,22,33,55开始的11记到numdata[0];碰到逗号就j等于1了,
        //再转换就转换到numdata[1];再碰到逗号就记到numdata[2];以此类推,直到字符串结束
          if(comdata[i] == ',')
          {
            j++;
          }
          else
          {
             //如果没有逗号的话,就将读到的数字*10加上以前读入的数字,
             //并且(comdata[i] - '0')就是将字符'0'的ASCII码转换成数字0(下面不再叙述此问题,直接视作数字0)。
             //比如输入数字是12345,有5次没有碰到逗号的机会,就会执行5次此语句。
             //因为左边的数字先获取到,并且numdata[0]等于0,
             //所以第一次循环是numdata[0] = 0*10+1 = 1
             //第二次numdata[0]等于1,循环是numdata[0] = 1*10+2 = 12
             //第三次是numdata[0]等于12,循环是numdata[0] = 12*10+3 = 123
             //第四次是numdata[0]等于123,循环是numdata[0] = 123*10+4 = 1234
             //如此类推,字符串将被变成数字0。
            numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
          }
        }
        //comdata的字符串已经全部转换到numdata了,清空comdata以便下一次使用,
        //如果不请空的话,本次结果极有可能干扰下一次。
        comdata = String("");
     
     
        //循环输出numdata的内容,并且写到PWM引脚
        for(int i = 0; i < 6; i++)
        {
          Serial.print("Pin ");
          Serial.print(PWMPin[i]);
          Serial.print(" = ");
          Serial.println(numdata[i]);
          analogWrite(PWMPin[i], numdata[i]);
          numdata[i] = 0;
        }
        //输出之后必须将读到数据的mark置0,不置0下次循环就不能使用了。
        mark = 0;
      }
    }

arduino代码

#include "LobotServoController.h"
#include <Servo.h>
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define L1 75
#define L2 125
#define pi 3.1415926
#define up  200
#define down 800
LobotServoController myse(Serial);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int User_receive_buffer_flag = 0;
int lift_flag = 0;//0为落下
double s_1,s_2;
double  s_1_pre,s_2_pre;
int lift = 300;
int recv_size = 0;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~ */
Servo myservo;  //创建一个舵机控制对象
Servo myservo1; // 使用Servo类最多可以控制8个舵机
Servo myservo2;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~ */

boolean sendFlag = false; //指示是否允许通过串口发送数据
boolean readCompleted = false; //指示是否完成读取串口数据
String serialString = ""; //串口数据缓存字符串
int a[10]= {-1,560,230,520,999,290,-1,260,860,999};
int b[10]= {-1,680,320,370,999,360,-1,340,860,999};
int readnum;
int c[100] = {0};
int d[100] = {0};
double recv_data[200]; 
String comdata = "";
int numdata[200] = {0};
int mark=0;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void setup()
{
  pinMode(13, OUTPUT);
  myse.moveServo(1,500,1000);
  myse.moveServo(2,500,1000);
  myse.moveServo(3,500,1000);
  Serial.begin(9600);
  serialString.reserve(200);
  motorplay_1(a,b,10);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~华丽的分割线 ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void loop()
{
    read();
    delay(10);
}
void set_pen(double x_1,double y_1)
{
  double m;//点到原点的距离
  double a1;
  double a2;
  double a3;//计算需要的角度
  m=sqrt(x_1*x_1+y_1*y_1);
  a1=cos_angle(L1,L2,m);
  a1 = acos(a1);
  a2=atan2(y_1,x_1);
  a3=cos_angle(L1,m,L2);
  a3 = acos(a3);
  s_1=a2-a3;
  s_2=pi-a1;
  s_1 = (180/pi)*s_1+90;
  s_2 = (180/pi)*s_2;
}
int cos_angle(double q,double w,double e){
  double r;
  r=(q*q+w*w-e*e)/(2*q*w);
  return r;
}
void read()
{
  int p = 0;
  int j = 0;
  while (Serial.available() > 0)
  {
    comdata += char(Serial.read());
    delay(2);
    mark = 1;
  }
  Serial.flush();
  if(mark == 1)  
  {
    for(int i = 0; i < comdata.length() ; i++)
    {
      if(comdata[i] == ',')
      {
        j++;
      }
      else
      {
        numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
      }
    }
    if(numdata[0]>30)
    {
      digitalWrite(13,HIGH); 
    }
    else 
    {
      digitalWrite(13,LOW);
    }
    comdata = String("");
 
    for (int k =0;k<j;k++)
    {
      if(k<(j+1)/2)
      {
        c[k] = numdata[k];
      }
      else
      {
        d[p] = numdata[k];
        p++;
      }
      Serial.println(c[k]);
      Serial.println(d[k]);
    }
    mark = 0;
  }
}

void motorplay_1(int *thea1,int *thea2,int num)
{
  for (int i = 0;i<num;i++)
  {
    if(thea1[i] == 999&&thea2[i] == 999)
    {
      myse.moveServo(3,up,1000);
      delay(1000);
    }
    else if(thea1[i] == -1&&thea2[i] == -1)
    {
      myse.moveServo(3,down,500);
      delay(1000);
    }
    else
    {
      myse.moveServo(1,thea1[i],1000);
      myse.moveServo(2,thea2[i],1000);
      delay(1000);
    }
  }
  myse.moveServo(1,500,1000);
  myse.moveServo(2,500,1000);
  myse.moveServo(3,500,1000);
  delay(1000);
}


void lift_down( int flag)
{
  if (flag ==1)
  {
    myse.moveServo(3,900,1000);
    
  }
  if(flag == 0)
  {
    myse.moveServo(3,100,1000);
  }
}


static void StrToInt(char *buffer, int size)
{
    int i = 0;
 
    char *p = NULL;
 
    p = strtok(buffer, " ");
 
    while((p = strtok(NULL, " ")) != NULL)
    {
        recv_data[i++] = atoi(p);
    }
    recv_size = i;
}

整个arduino控制板块主要分为三个部分,分别是初始化端口、数据的处理与解析、舵机运动的控制

初始化端口模块

首先对于初始化端口,我们本次采用的是串口通信的方式,来实现舵机的控制以及与上位机系统的通信。其中舵机的控制我们使用了舵机板自配套的舵机控制函数,只需添加第三方库后就可进行调用控制。

#include "LobotServoController.h"

本次arduino只配备了一个硬件串口,这意味着数据的收发需要特定的方式,否则会出现舵机控制紊乱的现象,我们是用了总线舵机自带的通信格式进行通信巧妙地避免了这一问题。实现了接收数据和发送舵机控制指令互相之间不发生冲突的目的。

数据解析模块

我们使用字符串的方式进行与上位机通信,采用utf-8的编码格式,以“\n”作为发送或读取的终止位,通过void read()函数可以将串口接收到的字符串转化成相应的十进制数并以一维矩阵的形式储存下来。

紧接着我们对于收到的数据进行反解,可以根据目标的位置得到舵机相对应的角度进而控制舵机。

实时控制模块

我们上位机使用python进行数据的传输以及原始信息的采集,我们通过外接摄像头的方式来进行外部图像的采集,并且进行边缘提取,得到位置矩阵。在这之后以utf-8格式发送字符串到串口,在arduino进行数据的解析后实时控制舵机运动。

  • 3
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值