伺服臂问题

Servo Arm Issue

我用 Arduino uno 和一个伺服扩展板构建了一个机械臂。手臂由 C# visual studio 和 USB 控制。目前有 6 个舵机连接到手臂上,电源使用 5V,2A。我可以通过向它发送数据来控制它。问题是在发送少量数据后,伺服臂失去控制并随机移动。

*更新 我使用 5V,5A 电源并将伺服器的数量减少到 3 个。在向伺服器发送少量数据后,机器人仍然 misbehave/randomly 移动。(机器人行为异常几秒钟,然后回到我想要的位置本身。) (我有6个舵机,我每个都试了3个舵机,我有同样的问题。)

这是我的arduino代码

#include <Servo.h>  
Servo myservoA;  
Servo myservoB;
Servo myservoC;
Servo myservoD;
Servo myservoE;
Servo myservoF;
int i,pos,myspeed,myshow,COM0,MOVE=0;
int sea,seb,sec,sed,see,sef;
static int v=0;
byte Readbytes [10];
byte byte0, byte1,byte2,byte3,byte4,byte5,byte6,byte7;
byte newpos0, newpos1,newpos2,newpos3,newpos4,newpos5,newpos6,newpos7;
byte oldpos0, oldpos1,oldpos2,oldpos3,oldpos4,oldpos5,oldpos7,oldpos8;

String mycommand="";    /// Serial capture   #auto: automatic operation  
#com: computer serial port control  #stop: standstill
static int mycomflag=2; // #auto:2 automatic operation  , #com: 1  computer 
serial port control    #stop:0  standstill 

void setup() 
{ 
  pinMode(13,INPUT);
  pinMode(12,INPUT);  
  Serial.begin(9600);
  myshow=0;
  mycomflag=2;         // the  ARM default  state: 2 automatic operation
                   //                          1 pc
  myservoA.attach(3);     
  myservoB.attach(5);    
  myservoC.attach(6);  
  myservoD.attach(9);  
  myservoE.attach(10); 
  myservoF.attach(11); 

  myservoA.write(10);   //0A Control wrist rotation
  myservoB.write(20);   //14
  myservoC.write(10);   //0A
  myservoD.write(20);   //3C
  myservoE.write(30);  //64
 myservoF.write(5);    //4B Control waist

oldpos0=10;oldpos1=20;oldpos2=10;oldpos3=20;oldpos4=30;oldpos5=5;


newpos0=10;newpos1=20;newpos2=10;newpos3=20;newpos4=30;newpos5=5;


}

void loop() 
{ 
   if (Serial.available()>0) 
  {
  i=0;int bufferLimit=9;
  while(Serial.available()>0 && i < bufferLimit) 
  {      
    Readbytes[i]= Serial.read();
    i++;       
  }
    newpos0=(Readbytes[0]);
    newpos1=(Readbytes[1]);
    newpos2=(Readbytes[2]);
    newpos3=(Readbytes[3]);
    newpos4=(Readbytes[4]);
    newpos5=(Readbytes[5]);
    newpos6=(Readbytes[6]);
    newpos7=(Readbytes[7]);
  COM0=1;
  }


  delay(100);//Required for all bytes to be read at SP2 before sending
        //at SP2 again
  if (COM0==1&&(newpos5==2||newpos5==24||newpos5==80))
  {
  //Serial.write(Readbytes,i);//To Freezer
  //Serial.write(Readbytes[0]);
  //Serial.write(Readbytes[1]);
  Serial.write(newpos0);
  Serial.write(newpos1);
  Serial.write(newpos2);
  Serial.write(newpos3);
  Serial.write(newpos4);
  Serial.write(newpos5);
  Serial.write(newpos6);
  Serial.write(newpos7);

  COM0=0;
  MOVE=1;
  }

  if(MOVE==1)
  {
  myspeed=800;
  for(pos = 0; pos <=myspeed; pos += 1)  
  {                                
   myservoA.write(int(map(pos,1,myspeed,oldpos0,newpos0))); 
   myservoB.write(int(map(pos,1,myspeed,oldpos1,newpos1))); 
   myservoC.write(int(map(pos,1,myspeed,oldpos2,newpos2))); 
   myservoD.write(int(map(pos,1,myspeed,oldpos3,newpos3))); 
   myservoE.write(int(map(pos,1,myspeed,oldpos4,newpos4))); 
   myservoF.write(int(map(pos,1,myspeed,oldpos5,newpos5)));
   delay(1);                       
  }
  MOVE=0;
  oldpos0=newpos0;
  oldpos1=newpos1;
  oldpos2=newpos2;
  oldpos3=newpos3;
  oldpos4=newpos4;
  oldpos5=newpos5;
 }

这是我的 Visual Studio WINFORM 代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RoboAnimate
{
public partial class Form1 : Form
{
    string RxString;
    string c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, 
    c16, c17;
    string fourbytesHexStr;
    UInt32 fourbytesHex = 0;
    byte A, B, C, D, E, F,timer1val;

    bool do1=false, do2 = false, do3 = false, do4 = false, do5 = false, do6 
    = false, sequence1 = true;

    private void timer1_Tick(object sender, EventArgs e)
    {
        timer1val++;
        textBox4.Text = timer1val.ToString();
        if (timer1val == 2) Do1();
        if (timer1val == 5) Do2();
        if (timer1val == 7) Do3();
        if (timer1val == 9) Do4();
        if (timer1val == 11) Do5();
        if (timer1val == 13) Do6();
        if (timer1val == 15) Do7();
        if (timer1val == 17) Do8();
        if (timer1val == 19) Do7();
        if (timer1val == 21) Do6();
        if (timer1val == 23) Do5();
        if (timer1val == 25) Do4();
        if (timer1val == 27) Do3();
        if (timer1val == 29) Do2();
        if (timer1val == 32)
        {
            Do1();
            sequence1 = false;
            timer1.Enabled = false;
            timer1val = 0;
        }
    }

    byte whichdo;

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void Pos2_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 160, 30, 10, 20, 30, 24, 0x91, 
        0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos3_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 160, 10, 10, 20, 30, 24, 0x91, 
        0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos4_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 160, 10, 30, 60, 40, 24, 0x91, 
        0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos5_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 160, 60, 30, 60, 40, 24, 0x91, 
         0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos6_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 90, 60, 30, 60, 40, 24, 0x91, 
     0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos7_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 90, 100, 60, 100, 40, 80, 0x91, 
      0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Pos8_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 95, 90, 130, 100, 40, 80, 0x91, 
         0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void ScrollA_Scroll(object sender, ScrollEventArgs e)
    {
        ServoA.Text = ScrollA.Value.ToString();
    }

    private void ScrollB_Scroll(object sender, ScrollEventArgs e)
    {
        ServoB.Text = ScrollB.Value.ToString();
    }

    private void ScrollC_Scroll(object sender, ScrollEventArgs e)
    {
        ServoC.Text = ScrollC.Value.ToString();
    }

    private void ScrollD_Scroll(object sender, ScrollEventArgs e)
    {
        ServoD.Text = ScrollD.Value.ToString();
    }

    private void ScrollE_Scroll(object sender, ScrollEventArgs e)
    {
        ServoE.Text = ScrollE.Value.ToString();
    }

    private void ScrollF_Scroll(object sender, ScrollEventArgs e)
    {
        ServoF.Text = ScrollF.Value.ToString();
    }

    private void Send_Click(object sender, EventArgs e)
    {
        A = Convert.ToByte(ServoA.Text);
        B = Convert.ToByte(ServoB.Text);
        C = Convert.ToByte(ServoC.Text);
        D = Convert.ToByte(ServoD.Text);
        E = Convert.ToByte(ServoE.Text);
        F = Convert.ToByte(ServoF.Text);
        //                                    A     B     C     D     E     

        //byte[] M1bytesToSend = new byte[8] { 0x4A, 0x14, 0x0A, 0x3C, 0x64, 
        0x4B, 0x91, 0xCA };   
        byte[] M1bytesToSend = new byte[8] { A, B, C, D, E, F, 0x91, 0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void serialPort1_DataReceived(object sender, 
     SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(50);
        int i;
        int bytes = serialPort1.BytesToRead;
        byte[] buffer = new byte[bytes];
        string[] hex = new string[bytes];
        serialPort1.Read(buffer, 0, bytes);


        RxString = ByteArrayToHexString(buffer);
        for (i = 0; i < bytes; ++i)
        {
            hex[i] = buffer[i].ToString("X");
        }
        textBox2.Text = RxString;
        textBox3.Text = bytes.ToString();

        if (bytes == 9)
        {
            c1 = (hex[0]).PadLeft(2, '0');
            c2 = (hex[1]).PadLeft(2, '0');
            c3 = (hex[2]).PadLeft(2, '0');
            c4 = (hex[3]).PadLeft(2, '0');
            c5 = (hex[4]).PadLeft(2, '0');
            c6 = (hex[5]).PadLeft(2, '0');
            c7 = (hex[6]).PadLeft(2, '0');
            c8 = (hex[7]).PadLeft(2, '0');
            c9 = (hex[8]).PadLeft(2, '0');

            fourbytesHexStr = string.Concat(c1, c2, c3, c4, c5, c6, c7, c8, 
            c9);
            fourbytesHexStr = fourbytesHexStr.PadRight(8, '0');
            textBox1.Text = fourbytesHexStr;
            textBox3.Text = bytes.ToString();
            serialPort1.DiscardInBuffer();
        }

        if (bytes == 8)
        {
            c1 = (hex[0]).PadLeft(2, '0');
            c2 = (hex[1]).PadLeft(2, '0');
            c3 = (hex[2]).PadLeft(2, '0');
            c4 = (hex[3]).PadLeft(2, '0');
            c5 = (hex[4]).PadLeft(2, '0');
            c6 = (hex[5]).PadLeft(2, '0');
            c7 = (hex[6]).PadLeft(2, '0');
            c8 = (hex[7]).PadLeft(2, '0');


            fourbytesHexStr = string.Concat(c1, c2, c3, c4, c5, c6, c7, c8);
            fourbytesHexStr = fourbytesHexStr.PadRight(8, '0');
            textBox1.Text = fourbytesHexStr;
            textBox3.Text = bytes.ToString();
            serialPort1.DiscardInBuffer();
        }

        if (bytes == 7)
        {
            c1 = (hex[0]).PadLeft(2, '0');
            c2 = (hex[1]).PadLeft(2, '0');
            c3 = (hex[2]).PadLeft(2, '0');
            c4 = (hex[3]).PadLeft(2, '0');
            c5 = (hex[4]).PadLeft(2, '0');
            c6 = (hex[5]).PadLeft(2, '0');
            c7 = (hex[6]).PadLeft(2, '0');



            fourbytesHexStr = string.Concat(c1, c2, c3, c4, c5, c6, c7);
            fourbytesHexStr = fourbytesHexStr.PadRight(8, '0');
            textBox1.Text = fourbytesHexStr;
            textBox3.Text = bytes.ToString();
            serialPort1.DiscardInBuffer();
        }
    }

    private string ByteArrayToHexString(byte[] buffer)
    {
        StringBuilder sb = new StringBuilder(buffer.Length * 3);
        foreach (byte b in buffer)
            sb.Append(Convert.ToString(b, 16).PadLeft(2, '0').PadRight(3, ' 
       '));
        return sb.ToString().ToUpper();
    }

    private void Pos1_Click(object sender, EventArgs e)
    {
        //                                 Gripper
        //                                    A   B   C   D   E   F     
        byte[] M1bytesToSend = new byte[8] { 115, 95, 80, 21, 31, 2, 0x91, 
     0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Open_Click(object sender, EventArgs e)
    {
        serialPort1.PortName = "COM6";
        serialPort1.BaudRate = 9600;
        serialPort1.DataBits = 8;
        serialPort1.Parity = Parity.None;
        serialPort1.StopBits = StopBits.One;
        serialPort1.Open();

        if (serialPort1.IsOpen) PortStatus.Text = "PORT Opened...Click to 
        Start DAQ";
        Close.Enabled = true;
        Open.Enabled = false;
    }

    public Form1()
    {
        InitializeComponent();
        whichdo = 100;
    }

    private void Animate1_Click(object sender, EventArgs e)
    {
        timer1.Enabled = true;
    }


    private void Do1()
    {
        byte[] M1bytesToSend = new byte[8] { 115, 95, 80, 21, 31, 2, 0x91, 
    0xCA };
        serialPort1.Write(M1bytesToSend, 0, 8);
    }

    private void Do2()
    {
        byte[] M2bytesToSend = new byte[8] { 160, 30, 10, 20, 30, 24, 0x91, 
       0xCA };
        serialPort1.Write(M2bytesToSend, 0, 8);
    }

    private void Do3()
    {
        byte[] M3bytesToSend = new byte[8] { 160, 10, 10, 20, 30, 24, 0x91, 
      0xCA };
        serialPort1.Write(M3bytesToSend, 0, 8);
    }

    private void Do4()
    {
        byte[] M4bytesToSend = new byte[8] { 160, 10, 30, 60, 40, 24, 0x91, 
     0xCA };
        serialPort1.Write(M4bytesToSend, 0, 8);
    }

    private void Do5()
    {
        byte[] M5bytesToSend = new byte[8] { 160, 60, 30, 60, 40, 24, 0x91, 
     0xCA };
        serialPort1.Write(M5bytesToSend, 0, 8);
    }

    private void Do6()
    {
        byte[] M6bytesToSend = new byte[8] { 90, 60, 30, 60, 40, 24, 0x91, 
      0xCA };
        serialPort1.Write(M6bytesToSend, 0, 8);
    }

    private void Do7()
    {
        byte[] M7bytesToSend = new byte[8] { 90, 100, 60, 100, 40, 80, 0x91, 
      0xCA };
        serialPort1.Write(M7bytesToSend, 0, 8);
    }

    private void Do8()
    {
        byte[] M8bytesToSend = new byte[8] { 95, 90, 130, 100, 40, 80, 0x91, 
     0xCA };
        serialPort1.Write(M8bytesToSend, 0, 8);
    }




}
}

问题不在于您的代码,而在于您的电源...您为伺服系统消耗了太多电流。升级电源或减少舵机数量,在 2A 上,3 x 9g 舵机会推动它 imo,我会给每个舵机 750mA 以保持安全。

这不是 C# 问题:-)

但是,您的 posted 代码中有几个地方需要注意:

 i=0;
 while(Serial.available()>0) 
    {      
        Readbytes[i]= Serial.read();
        i++;       
    }

这不是个好主意 - 您需要限制 i 以便连续进入的垃圾流不会清除内存。这看起来有点像 C,所以它会像

 i=0;
 while(Serial.available()>0 && i < bufferLimit) 
    {      
        Readbytes[i]= Serial.read();
        i++;       
    }

其中 bufferLimit 是以某种方式设置为 ReadBytes 长度的值。

此外,您需要某种方式来构建您的通讯,以便您知道自己的位置。仅仅假设接下来的 8 个字节是正确的并将它们插入您的伺服系统是灾难的根源。您可以实现一个简单的协议,该协议具有明确的导入字符、数据字节和某种校验和或 CRC。如果您不确定如何操作,请随时 post 发表评论,我会提出建议。同时,请 post 无论是什么代码正在向您 post 编辑的代码发送数据,因为这很容易成为罪魁祸首。

编辑:

但是从 Aydin Adn 的评论开始,因为它是导致问题的第一竞争者。我在这个答案中的建议可以让你在未来免去悲伤。

编辑 9 月 30 日:

查看您的 C# 代码,我越来越认为某些东西正在干扰您的数据流并使 PC 与 Arduino 不同步,您无法从中恢复,因为您的通讯没有框架或错误检查。

有两种可能性:

  1. 你的串行适配器;我看到您正在使用 COM6,这指向某种 USB 适配器。如果您使用的是便宜且令人愉快的适配器,则它可能正在发明数据-我在工作中遇到过这种情况,唯一的解决方法是仅使用 USB-> 使用 FTDI 芯片组的串行适配器;来自中国的任何廉价产品都可能出现各种滑稽动作,包括在几个小时内完全正常工作,然后突然重复当天早些时候的一整块数据。问题出在驱动程序而不是硬件,其中一些可能很差。
  2. 您的数据lead/the Arduino 开发板本身;因为你有一个笨重的电源和一些伺服系统汲取电流,所以通信线路中完全有可能产生噪音,而这被 Arduino 解释为位于其接收缓冲区中的数据,直到你发送更多并处理它,此时您发送的字节不会发送到您想要的伺服器。
  3. 你在某个时候是 overrunning/racing;我注意到 Thread.Sleep() 和连续丢弃,它们显然是为了尝试和构建事物。这种事情永远不会有帮助,并且会使一切都非常依赖于事件的确切时间,而你需要它更具确定性和时间独立性。

唯一可以确定的方法是实施一个简单的协议,该协议绝对可以构建一个命令来移动和验证数据,以确保收到的是 PC 发送的。您只有 运行 9600 波特,每字节大约 1 毫秒,因此将命令长度从 8 个裸字节增加仍然会在 20 毫秒以下引入命令。我建议使用一种全部采用 ASCII 的 Intel Hex 格式的变体,您可以使用类似的东西:

:{sz}{cm}{data}{chk}

:是一个冒号字符,从一个命令到下一个命令是唯一的;得到一个冒号,你就知道接下来会发生什么。从那时起,一切都是十六进制字符对 0-9/A-F 组成编码字节。

{sz} 是 {data} 中的字节数(这是可选的,但如果您扩展命令结构,它会提供一些灵活性)

{cm} 是一个命令字节(你可以将其拆分为一些位以允许简单的重试检测)

{data} 是 "sz" 编码的数据字节(可以为零,但对于移动命令为 8)

{chk} 是 : 和校验和

之间所有原始字节值的负和

解码这很简单,如果您将解码后的字节从 sz 添加到 chk 并得到除零之外的任何内容,您可以丢弃所有内容而不处理它,因为出了点问题。同样,一旦你收到冒号,就只能是 0-9/A-Z,其他任何东西你都可以忽略它,让 PC 决定它要做什么。

此方案将允许 PC 发送状态命令“:000000”以获取当前状态,并允许发送类似“:0801A00A1E3C2891CA70”的移动命令,例如来自 Do4() 的值 "M4bytesToSend = new byte[8] { 160, 10, 30, 60, 40, 24, 0x91, 0xCA };" 假设 01 是移动命令。

一旦 Arduino 解码了上面的移动命令,它就可以 return 它设置了一个忙位来指示它已经收到它并且即将进行移动,说“:0861A00A1E3C2891CAD2”然后当它已经完成并准备好发送下一个命令“:0841A00A1E3C2891CAB2”。在这两种情况下,PC 发送的 0x01 命令都设置了额外的位,这样它自己的数据环回就不会欺骗 PC。使用命令的最高位将允许检测到重试;如果 PC 连续两次发送 0x01,Arduino 将不会执行第二次,而只会发送“:0841A00A1E3C2891CAB2”响应以表明它已执行该命令。 PC 必须使用 0x81 来接受新命令,这样 PC 可以愉快地重新发送命令,如果它没有得到响应,安全地知道如果 Arduino 没有得到它,它将被执行第一次,或者得到一个响应,告诉它命令已经完成。如果 PC 不简单地重试命令,它当然可以发送 :000000 并取回最后的响应。

我意识到当你只想发送六个字节来控制一个手臂时,这似乎很啰嗦,但这种性质的通信本质上是脆弱的,所以让它可靠运行的唯一方法是实施某种协议。

我已经研究了 Arduino 方法并且有代码可以做到这一点,如果你想尝试将它破解成一个工作程序(我没有办法编译它所以这是一个最好的猜测)。