精华内容
下载资源
问答
  • 建立了二自由度机械臂的运动学模型,并在不调用任何库的情况下手写实现模糊PID控制,仿真中通过控制角速度实现机械臂末端位置的跟踪。
  • 一种二自由度机械臂的轨迹规划带代码,实现三点轨迹,包含加减速代码
  • 对该机械臂进行了自由度分析,得到该机械臂自由度为2,具有平面内的一个移动和一个转动自由度。基于几何解析法分析了该机构的运动学正反解,并且给出了5组正反解分析数值算例,验证了正反解分析的正确性。
  • (5)建立如下图所示的二自由度机械臂 P点坐标: P=Rot(theta2,‘z’)*[a;0;0]; E点坐标: E=P+Rot(theta2,‘z’)Rot(phi,‘z’)[b;0;0]; 若给定的E点坐标与实际利用上式求解的坐标一致则说明逆解正确 代码实现 (1)...

    一、基础知识

    (1) PID控制:Kp比例、Ki积分、Kd微分;
    (2) 坐标变换基础
    (3) atan2(y,x)双参数反正切

    二、具体案例

    在这里插入图片描述
    对机械臂进行逆运动学求解:
    (1) 在这里插入图片描述

    (2) 在这里插入图片描述
    (3)在这里插入图片描述
    (4)在这里插入图片描述
    (5)建立如下图所示的二自由度机械臂
    二自由度机械臂
    P点坐标:
    P=Rot(theta2,‘z’)*[a;0;0];
    E点坐标:
    E=P+Rot(theta2,‘z’)Rot(phi,‘z’)[b;0;0];
    若给定的E点坐标与实际利用上式求解的坐标一致则说明逆解正确

    代码实现

    (1)正运动学

    while 1
    clc;
    clear;
    
    %圆心坐标
    x0=110;
    y0=110;
    %半径
    R=40;
    %连杆长度
    a=100;
    b=100;
    t=1;
    for i=0:0.1:2*pi+0.1
        
        x=x0+R*cos(i);
        y=y0+R*sin(i);
    %     x=(a+b)*cos(i);
    %     y=(a+b)*sin(i);
        theta1=atan2(y,x); 
        % theta1=acos(x/sqrt(x*x+y*y));
        c=sqrt(x*x+y*y); % 末端到原点的距离
        theta3=acos((c*c+a*a-b*b)/(2*a*c));
        theta2=theta1-theta3; % 关节1 角度
        phi=pi-acos((a*a+b*b-c*c)/(2*a*b)); %关节2角度
        
        %连杆 P 位置
        P=Rot(theta2,'z')*[a;0;0];
        
        % 连杆末端位置(正运动学验证)
        E=P+Rot(theta2,'z')*Rot(phi,'z')*[b;0;0];
        
        %连杆连接处P的坐标
        Px=a*cos(theta2);
        Py=a*sin(theta2);
        
        %末端绘制圆的坐标
        rolx(t)=x;
        roly(t)=y;
        t=t+1;
        plotrobot(P(1),P(2),E(1),E(2),rolx,roly);  % 绘图验证
        pause(0.0000001)
        hold off
    end
    end
    
    

    (2)PID

    调速跟踪位置
     
    clc;
    clear;
    
    %圆心坐标
    x0=110;
    y0=110;
    %半径
    R=40;
    %连杆长度
    a=100;
    b=100;
    t=1;
    
    % 控制周期 
    dt=0.05 % 秒
    Time=[0]; % 当前时间
    realTheta=[0];% 关节1实际角度
    realPhi=[0];%关节2实际角度
    errTehta=[0]; % 关节1 角度误差
    errPhi=[0]; % 关节2 角度误差
    errThetaSum=0;%关节1累积误差
    errPhiSum=0;%关节2累积误差
    derrTheta=0;%关节1 本次角度误差与上一次角度误差的差值
    derrPhi=0;%关节2 本次角度误差与上一次角度误差的差值
    % PID 参数(关节1)
    Kp1=10;
    Ki1=2;
    Kd1=4;
    % PID 参数(关节2)
    Kp2=8;
    Ki2=1;
    Kd2=2;
    
    for i=0:1:150
        theta2=i/150*2*pi;
        phi=i/150*pi;
    
        
        %连杆 P 位置
        P=Rot(theta2,'z')*[a;0;0];
        
        % 连杆末端位置(正运动学验证)
        E=P+Rot(theta2,'z')*Rot(phi,'z')*[b;0;0];
        
        % PID 控制
        w1=Kp1*(theta2-realTheta(end))+derrTheta*Kd1+Ki1*errThetaSum;%关节1 瞬时角速度
        w2=Kp2*(phi-realPhi(end))+derrPhi*Kd2+Ki1*errPhiSum;%关节2 瞬时角速度
        realTheta(end+1)=realTheta(end)+w1*dt;
        realPhi(end+1)=realPhi(end)+w2*dt;
        
        % 误差
        errTehta(end+1)=theta2- realTheta(end);
        errPhi(end+1)=phi- realPhi(end);
        errThetaSum=errThetaSum+errTehta(end);
        errPhiSum=errPhiSum+errPhi(end);
        derrTheta= errTehta(end)-errTehta(end-1);
        derrPhi= errPhi(end)-errPhi(end-1);
        % 当前时间
        Time(end+1)=dt*i;
        
        %连杆 P 位置
        realP=Rot(realTheta(end),'z')*[a;0;0];
        % 连杆末端位置(正运动学验证)
        realE=P+Rot(realTheta(end),'z')*Rot(realPhi(end),'z')*[b;0;0];
        
        %末端绘制圆的坐标
        rolx(t)=E(1);
        roly(t)=E(2);
        t=t+1;
        subplot(121);
        plotrobot(realP(1),realP(2),realE(1),realE(2),rolx,roly);  % 绘图验证
        hold off
        subplot(122);
        plot(Time,errTehta,'k',Time,errPhi,'r');
        pause(0.0000001)
       
    end
    
    

    实现效果

    在这里插入图片描述

    思考与讨论

    根据调速跟踪位置的代码,写出用PID调节加速度跟踪位置的代码

    
    
    展开全文
  • 机电系统的动力与运动的计算机仿真 -----------基于二自由度两连杆平面机器人系统仿真 马国锋 梁应海 周凯 武汉理工大学机电工程学院机械工程及自动化系 摘要平面两连杆机器人机械臂是一种简单的两自由度的机械装置...
  • 运用C#在VS2017的PictureBox控件中绘制简易二自由度机械臂,并且让机械臂实现画直线、圆、人物轮廓及写字的功能。 给大家看看效果吧 演示写字视频在下: VID 首先放置了诸多控件 在给控件绑定事件前,先...

    运用C#在VS2017的PictureBox控件中绘制简易二自由度机械臂,并且让机械臂实现画直线、圆、人物轮廓及写字的功能。

    给大家看看效果吧

    在这里插入图片描述
    在这里插入图片描述
    演示写字视频在下:

    VID

    首先放置了诸多控件

    本来想详细标注的,但是手抖,qq截图就发出去了,也大致能看出各控件的功能了,还有一个定时器,用来实时画机械臂末端的轨迹。(其中机械臂的两臂的角度是只读的,实时显示角度数据,两臂长度可以设置。画直线、圆等肯定得有坐标数据)(特别提醒:坐标轴按画的那般,PictureBox1的中点为原点)

    在给控件绑定事件前,先定义几个函数,需要反复调用。

    绘制机械臂的函数

    部分全局变量定义:
    private int L1 = 100, L2 = 100; //表示机械臂的两臂长度
    private double Ang1 = 0, Ang2 = 0; //表示机械臂的扭转角度,单位为弧度,取值范围为(-PI,PI)

    #region 绘制机械臂
    /// <summary>
    /// 机械臂的底端位于坐标系原点
    /// </summary>
    public void DrawMechanism()
    {
        //特别注意:我定义的坐标系为我们平常时常用的坐标系,
        //但电脑屏幕等运用的坐标系是:左上角为原点,水平向右为X正半轴,水平向下为Y正半轴
        //因此两个坐标系之间存在装换。 
        PointF pointO = new PointF(200F, 200F);//机械臂O点位置,位于坐标原点
        PointF pointA = new PointF((float)(200 + L1 * Math.Cos(Ang1)), (float)(200 - L1 * Math.Sin(Ang1)));//机械臂A点位置
        PointF pointB = new PointF((float)(200 + L1 * Math.Cos(Ang1) + L2 * Math.Cos(Ang1 + Ang2)), (float)(200 - L1 * Math.Sin(Ang1) - L2 * Math.Sin(Ang1 + Ang2)));//机械臂B点位置
    
        Bitmap bitmap = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);//位图为PictureBox1的客户区尺寸
        Graphics g = Graphics.FromImage(bitmap);//定义图像,图像为抽象类,不可直接构造函数生成
        //绘制斜线时消除锯齿(鼠标放在那就可以看到函数功能)
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.SmoothingMode = SmoothingMode.HighQuality;
        Pen pen = new Pen(Color.Red, 1);//定义一只红色、1像素宽的笔
        g.DrawLine(pen, pointO, pointA);//绘制大臂(连接OA线段)
        g.DrawLine(pen, pointA, pointB);//绘制小臂(连接AB线段)
        //绘制转动副O
        g.DrawEllipse(pen, 200 - 5, 200 - 5, 10, 10);
        g.FillEllipse(new SolidBrush(Color.White), 200 - 5, 200 - 5, 10, 10);
        //绘制转动副A
        g.DrawEllipse(pen, (float)(200 + L1 * Math.Cos(Ang1)) - 5, (float)(200 - L1 * Math.Sin(Ang1)) - 5, 10, 10);
        g.FillEllipse(new SolidBrush(Color.White), (float)(200 + L1 * Math.Cos(Ang1)) - 5, (float)(200 - L1 * Math.Sin(Ang1)) - 5, 10, 10);//绘制转动副A
    
        pictureBox1.Image = bitmap;//所以Image每次都被更新了,只显示当前机械臂位置
        //释放资源
        g.Dispose();
        pen.Dispose();
    }
    #endregion
    

    根据机械臂末端位置求解两臂旋转角度的函数

    求解说明:见下图,对于一个末端点,机械臂存在两种姿态(除了边界点),因此存在选择哪种姿态的原则。
    在这里插入图片描述
    部分全局变量定义:
    private double Ang1 = 0, Ang2 = 0; //表示机械臂的扭转角度,单位为弧度,取值范围为(-PI,PI)

    #region 求解对应坐标下两臂转动角度
    /// <summary>
    /// 求解两臂转动角度,(反)三角函数都是弧度单位。
    /// </summary>
    /// <param name="X">目标点的Y坐标(对应我定义的坐标系)</param>
    /// <param name="Y">目标点的Y坐标(对应我定义的坐标系)</param>
    public void GetAngel(double X, double Y)
    {
        double x = X;
        double y = Y;
        double r = Math.Sqrt(x * x + y * y);//目标点到原点的距离
        double theta = Math.Atan2(y, x);//取值范围为(-PI,PI)
        double phi = Math.PI - Math.Acos((L1 * L1 + L2 * L2 - r * r) / (2 * L1 * L2));
        double theta1 = Math.Acos((r * r + L1 * L1 - L2 * L2) / (2 * L1 * r));
        if (r == 0)
        {
            Ang2 = Math.PI;
        }
        //两个解:(theta+theta1,-phi)、(theta-theta1,phi)
        //选解采用最短准则:即对应的两臂角度相对于上一时刻的两臂角度需要变动的角度和更小的解
        else
        {
            if ((Math.Abs(theta + theta1 - Ang1) + Math.Abs(-phi - Ang2)) > (Math.Abs(theta - theta1 - Ang1) + Math.Abs(phi - Ang2)))
            {
                Ang1 = theta - theta1;
                Ang2 = phi;
            }
            else
            {
                Ang1 = theta + theta1;
                Ang2 = -phi;
            }
        }
        textBox3.Text = (Ang1 / Math.PI * 180).ToString();//将弧度换算成角度显示
        textBox4.Text = (Ang2 / Math.PI * 180).ToString();//
    }
    #endregion
    

    网上随便找的延时函数

    忘记是哪位老哥了,真的抱歉啊!

    #region 延时函数
    
    /// <summary>
     /// 延时函数,单位为毫秒
     /// </summary>
     /// <param name="delayTime"></param>
     public void DelayMs(int delayTime)
     {
         DateTime now = DateTime.Now;
         int s;
         do
         {
             TimeSpan spand = DateTime.Now - now;
             s = spand.Milliseconds;
             Application.DoEvents();
         }
         while (s < delayTime);
     }
     #endregion
    

    下面开始给控件绑定事件

    部分全局变量:
    string graph;//用来标志画直线、画圆、画轮廓还是写字
    PointF pointLast;//机械臂末端上一时刻位置
    PointF pointNow;//机械臂末端现在位置

    首先给定时器绑定事件

    因为机械臂从当前位置移动到直线的起点、圆的起点、轮廓的起点和字的起点的过程中,机械臂末端移动轨迹并不需要绘制,故关闭Timer1
    只有机械臂末端在画线等功能时开启Timer1进行绘制轨迹

    private void timer1_Tick(object sender, EventArgs e)
    {
        //机械臂现在末端位置
        pointNow = new PointF((float)(200 + L1 * Math.Cos(Ang1) + L2 * Math.Cos(Ang1 + Ang2)), (float)(200 - L1 * Math.Sin(Ang1) - L2 * Math.Sin(Ang1 + Ang2)));
        if (pointLast == new PointF(0, 0))
        {//可以试试没这个if语句会发生什么
            pointLast = pointNow;//如果是绘制曲线的起点,便没有pointLast,令其就等与pointNow
        }
        Bitmap bt = new Bitmap(pictureBox1.BackgroundImage);//保证了现在绘图是在以前绘图的基础上,不会丢失先前轨迹
        Graphics g = Graphics.FromImage(bt);
        //绘制斜线时消除锯齿
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.SmoothingMode = SmoothingMode.HighQuality;
        //将机械臂上一次末端位置及现在末端位置连起来,因为两个位置及近,就是用直线不断拟合轨迹
        g.DrawLine(new Pen(Color.Black, (float)0.4), pointLast, pointNow);
        //释放资源
        g.Dispose();
        //将包含新的轨迹的位图赋给PictureBox1的BackgroundImage
        pictureBox1.BackgroundImage = bt;
        pointLast = pointNow;//更新上一次机械臂末端位置,
    }
    

    开始是画直线

    “画直线”按钮的点击事件,进行一些基本设置

    private void button1_Click(object sender, EventArgs e)
    {//“画直线”按钮的点击事件
        groupBox1.Visible = true;//坐标输入功能开启,用户输入起终点
        groupBox2.Visible = false;//写字输入功能关闭
        textBox4.Visible = true;
        label9.Text = "起点:";
        label10.Text = "终点:";
        graph = "Line";//标志其为画直线
    }
    

    画直线的函数:将直线细分成很多个点,机械臂末端依次到达这些点

    #region 绘制两点之间的一条直线
     /// <summary>
     /// 绘制两点之间的一条直线。
     /// </summary>
     /// <param name="startx">起点的X坐标</param>
     /// <param name="starty">起点的Y坐标</param>
     /// <param name="endx">终点的x坐标</param>
     /// <param name="X">终点的Y坐标</param>
     /// <param name="bl">是否是绘图,绘图:true;移动:false</param>
     public void DrawLine(double startx, double starty, double endx, double endy, Boolean bl)
     {
         if (bl)//如果是绘图,就开启Timer1进行绘制轨迹
         {
             timer1.Enabled = true;
         }
         double x, y;//用来表示下一个机械臂末端位置
         double r = Math.Sqrt((startx - endx) * (startx - endx) + (starty - endy) * (starty - endy));//直线长度
         for (int i = 0; i < r / 0.05; i++)//对所画图形进行分段计数
         {
             x = startx + i / (r / 0.05) * (endx - startx);
             y = starty + i / (r / 0.05) * (endy - starty);
             GetAngel(x, y);//通过调用此函数,计算得到对应(x,y)点时的两臂角度,对全局变量Ang1和Ang2赋值
             DrawMechanism();//通过Ang1和Ang2的实时数据,更新机械臂位置,因为每次变化很小,肉眼上以为机械臂在转动
             DelayMs(4);//每个机械臂状态停留4ms,如果不停留,程序运行很快,基本基本看不到机械臂中间移动过程
         }
         timer1.Enabled = false;//无论是机械臂绘图还是单纯移动到目标点,都关闭定时器
     }
     #endregion
    

    注:bl是用来标志是画图形还是前往图形绘制起点;false:前往图形绘制起点;true:画图形

    然后是画圆

    “画圆”按钮的点击事件,进行一些基本设置

    private void button2_Click(object sender, EventArgs e)
    {//“画圆”按钮的点击事件
         groupBox1.Visible = true;//坐标输入功能开启
         groupBox2.Visible = false;//写字输入功能关闭
         textBox4.Visible = false;
         label9.Text = "圆心:";
         label10.Text = "半径:";
         graph = "Circle";//标志其为画圆
    }
    

    画圆的函数:将圆细分成很多个点,机械臂末端依次到达这些点

    #region 绘制一个圆
    /// <summary>
    /// 绘制给定圆心和半径的圆。
    /// </summary>
    /// <param name="startx">圆心的X坐标</param>
    /// <param name="starty">圆心的Y坐标</param>
    /// <param name="r">圆的半径</param>
    public void DrawCircle(double startx, double starty, double r)
    {
        timer1.Enabled = true;
        double x, y;
        double theta;//存储画圆时的弧度,取值范围(-PI,PI)
        double k = Math.PI * r / 0.05; //将圆的周长细分为2k个点
        for (int i = 0; i < 2 * k; i++)//从圆的最左点开始逆时针绘制,即从-PI到PI绘制
        {
            theta = (i - k) / k * Math.PI;//每个点对应的弧度
            //计算点的坐标
            x = startx + r * Math.Cos(theta);
            y = starty + r * Math.Sin(theta);
            GetAngel(x, y);
            DrawMechanism();
            DelayMs(10);
        }
        timer1.Enabled = false;
    }
    #endregion
    

    接着画人物轮廓

    部分全局变量:VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();//创建用于存储轮廓的VectorOfVectorOfPoint数据类型(命名空间Emgu.CV.Util)(contours里面存储的数据可以自己查,里面就是存储了很多段线段,由线段组成了轮廓,所以一定要知道contours存储的数据格式)

    CvInvoke.FindContours函数的参数讲解参考了这篇老大哥:
    OpenCV中的findContours函数参数讲解

    “画轮廓”按钮的点击事件,提取图片轮廓

    private void button3_Click(object sender, EventArgs e)
    {
        IOutputArray hierarchy = null;//与contours对应的向量,hierarchy[i][0]~hierarchy[i][3]存储后、前、子、父级轮廓链表表头
        OpenFileDialog ofd = new OpenFileDialog();//创建一个对话框,选择需要画轮廓的图片
        ofd.Filter = "JPG图片|*.jpg|BMP图片|*.bmp";//选择文件的类型(filter:过滤)
        
        //处理图像,得到图像的轮廓信息!
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            Mat _inputmat = new Mat(ofd.FileName);
            imageBox1.Image = _inputmat;//读入图像,在ImageBox中显示
            CvInvoke.GaussianBlur(_inputmat, _inputmat, new Size(3, 3), 3, 3);//对输入图像进行高斯滤波,并将滤波后的图像存至_inputmat
            Mat dst = new Mat();//存储图片轮廓信息
            CvInvoke.Canny(_inputmat, dst, 120, 180);//Canny 边缘检测算子
            imageBox2.Image = dst;//显示轮廓图片
    
            #region  CvInvoke.FindContours方法参数讲解
            ///<summary>
            ///IOutputArray contours:检测到的轮廓。通常使用VectorOfVectorOfPoint类型。
            ///IOutputArray hierarchy:可选的输出向量,包含图像的拓扑信息。不使用的时候可以用 null 填充。
            ///每个独立的轮廓(连通域)对应 4 个 hierarchy元素 hierarchy[i][0]~hierarchy[i][4]
            ///(i表示独立轮廓的序数)分别表示后一个轮廓、前一个轮廓、父轮廓、子轮廓的序数。
    
            ///RetrType mode标识符及其解析:
            ///External = 0 提取的最外层轮廓;
            ///List = 1 提取所有轮廓
            ///Ccomp = 2 检索所有轮廓并将它们组织成两级层次结构:水平是组件的外部边界,二级约束边界的洞。
            ///Tree = 3 提取所有的轮廓和建构完整的层次结构嵌套的轮廓。
    
            ///ChainApproxMethod表示轮廓的逼近方法
            ///ChainCode = 0 Freeman链码输出轮廓。所有其他方法输出多边形(顶点序列)。
            ///ChainApproxNone = 1 所有的点从链代码转化为点;
            ///ChainApproxSimple = 2 压缩水平、垂直和对角线部分,也就是说, 只剩下他们的终点;
            ///ChainApproxTc89L1 = 3 使用The - Chinl 链逼近算法的一个
            ///ChainApproxTc89Kcos = 4 使用The - Chinl 链逼近算法的一个
            ///LinkRuns = 5, 使用完全不同的轮廓检索算法通过链接的水平段的1s轨道。
            ///用这种方法只能使用列表检索模式。
            ///</summary>
            #endregion
    
            CvInvoke.FindContours(dst, contours, hierarchy, Emgu.CV.CvEnum.RetrType.External,
                Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
        }
        graph = "Graph";//标志是画人物轮廓
    }
    

    因为存储的轮廓都是线段表示,故只要调用DrawLine函数就行

    最后是写字

    写字关键是字库,我选择的是CAD软件定制的SHP字体格式。网上很容易找到CAD软件中的SHX文件经过反汇编后得到SHP文件,反汇编后的SHP文件和普通的SHP文件不同,它是Shape文件,它是可以直接用记事本打开的,它是使用特殊代码法编写的文件,我们可以直接解码,不是普通的SHP文件的解码方式,那是不对的。Shape文件的代码可以通过看这位老大哥的文章学会然后解码:shx文件格式说明形文件及其开发
    我是将一个Shape文件(包含几千个字)里的每个字拿出来,单独建立一个记事本,然后根据这个字的GB2313编码给这个记事本命名。然后在根据机械臂要写的字的GB2313编码找到这个文件在读取解码出来。(主要就是利用字符串的split方法切片(split方法这位老哥讲解得很详细C#利用正则表达式实现字符串搜索),当然可能有更好的方法,我这方法较笨)

    “写字”按钮的点击事件,进行基础设置

    private void button4_Click(object sender, EventArgs e)
    {
        groupBox1.Visible = false;
        groupBox2.Visible = true;
        graph = "Font";
    }
    

    最后就是开始按钮的事件绑定了

    部分全局变量:
    double startX; // 用来存储运动开始的位置
    double startY;
    double endX = 200;//用来存储运动结束的位置
    double endY = 0;

    “开始”按钮的点击事件:有点多,

    private void button5_Click(object sender, EventArgs e)
    {
        //读取设置的两臂长度
        L1 = Convert.ToInt16(textBox6.Text);
        L2 = Convert.ToInt16(textBox7.Text);
    
        //绘制图像为直线
        if (graph == "Line")
        {
            //将上一次的机械臂的末端位置(即现在机械臂末端位置)设置为起点位置
            startX = endX;
            startY = endY;
            //将绘制目标图像(即线段)的起点位置设置为终点位置
            endX = double.Parse(textBox1.Text);
            endY = double.Parse(textBox2.Text);
            //然后机械臂末端从起点沿直线运动到终点位置,并且这段移动是不需要绘制轨迹的,所以为false
            DrawLine(startX, startY, endX, endY, false);
            //将上一次的机械臂的末端位置(即上面说的目标图像的起点位置)设置为起点位置
            startX = endX;
            startY = endY;
            //将绘制目标图像的终点位置设置为终点位置
            endX = double.Parse(textBox3.Text);
            endY = double.Parse(textBox4.Text);
            //然后机械臂末端从起点沿直线运动到终点位置,这段移动是需要绘制轨迹的,所以为true
            DrawLine(startX, startY, endX, endY, true);
        }
        //绘制图像为圆
        if (graph == "Circle")
        {
            //将上一次的机械臂的末端位置(即现在机械臂末端位置)设置为起点位置
            startX = endX;
            startY = endY;
            //将绘制目标图像(即圆)的起点位置(即圆的最左点)设置为终点位置
            endX = double.Parse(textBox1.Text) - double.Parse(textBox3.Text);
            endY = double.Parse(textBox2.Text);
            //然后机械臂末端从起点沿直线运动到终点位置,并且这段移动是不需要绘制轨迹的,所以为false
            DrawLine(startX, startY, endX, endY, false);
            //最后开始画圆,这段移动是需要绘制轨迹的,所以为true
            DrawCircle(double.Parse(textBox1.Text), double.Parse(textBox2.Text), double.Parse(textBox3.Text));
        }
        //绘制图像为人像轮廓
        if (graph == "Graph")
        {//轮廓
            for (int i = 0; i < contours.Size; i++)
            {
                for (int j = 0; j < contours[i].Size - 1; j++)
                {
                    //将上一次的机械臂的末端位置(即现在机械臂末端位置)设置为起点位置
                    startX = endX;
                    startY = endY;
                    //将绘制目标图像(即轮廓的第i组的第j条线段)的起点位置(即圆的最左点)设置为终点位置
                    endX = contours[i][j].X - 40;
                    endY = -(contours[i][j].Y - 125);
                    //然后机械臂末端从起点沿直线运动到终点位置,并且这段移动是不需要绘制轨迹的,所以为false
                    DrawLine(startX, startY, endX, endY, false);
                    //将上一次的机械臂的末端位置(即上面说的轮廓的第i组的第j条线段的起点位置)设置为起点位置
                    startX = endX;
                    startY = endY;
                    //将机械臂的目标末端位置(即上面说的轮廓的第i组的第j条线段的终点位置)设置为起点位置
                    endX = contours[i][j + 1].X - 40;
                    endY = -(contours[i][j + 1].Y - 125);
                    //然后绘制轮廓的第i组第j条线段
                    DrawLine(startX, startY, endX, endY, true);
                    pointLast = new PointF(0, 0);//每一次图像绘制成功后,将pointLast设置(0,0)(与定时器配合使用),自行体会作用,感觉讲不清
                }
            }
        }
        //绘制图像为汉字
        if (graph == "Font")
        {
            for (int j = 0; j < textBox5.Text.Length; j++)
            {
                char word = textBox5.Text[j];//取出第j个文字
                //将这个汉字转化为GB22313编码,并且加0构成文件名
                byte[] wordbytes = Encoding.GetEncoding("GB2312").GetBytes(new char[] { word });
                string filename = "0" + Convert.ToString((wordbytes[0] << 8) + wordbytes[1], 16);
                //从文件中读出数据
                StreamReader sr = new StreamReader("E:/下载软件/Visual Studio 2017/C#文件/Robotic Arm/汉字库/" + filename + ".txt");
                string wordtxt = sr.ReadToEnd();//将文字所有信息读入wordtxt变量
                string font = wordtxt.Split(new string[] { "7,-114,5,", "7,-113,0" }, StringSplitOptions.RemoveEmptyEntries)[1];//切取有用片段
                string[] fontarray = font.Split(new string[] { ",", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);//将字符串转化为字符数组,去掉逗号空格这些
                int i = 0;
                //调整字的初始位置,即字的左下角坐标
                Point point = new Point(100 * j - 50, 0);
                //将上一次机械臂末端位置设置为起点位置
                startX = endX;
                startY = endY;
                //将字的左下角位置设置为机械臂末端目标位置
                endX = point.X;
                endY = point.Y;
                //使机械臂从当前位置沿直线移动到字的走下角位置,此过程不需要画轨迹,故为false
                DrawLine(startX, startY, endX, endY, false);
                //解码文字信息,结合Shape文件格式解码出想要的数据
                while (i < fontarray.Length)
                {
                    if (fontarray[i] == "2")//说明为抬笔过程,就不用画轨迹,故为false
                    {
                        startX = endX;
                        startY = endY;
                        endX = startX + Convert.ToInt16(fontarray[i + 2]);
                        endY = startY + Convert.ToInt16(fontarray[i + 3]);
                        DrawLine(startX, startY, endX, endY, false);
                        i = i + 4;
                    }
                    if (fontarray[i++] == "1")//说明为落笔过程,需要画轨迹,故为true
                    {
                        for (; i < fontarray.Length && fontarray[i] != "2";)
                        {
                            if (fontarray[i] == "8")
                            {
                                startX = endX;
                                startY = endY;
                                endX = startX + Convert.ToInt16(fontarray[i + 1]);
                                endY = startY + Convert.ToInt16(fontarray[i + 2]);
                                DrawLine(startX, startY, endX, endY, true);
                                i = i + 3;
                            }
                            else if (fontarray[i] == "12")//因为用到圆弧的字较少,所以忽略
                            {
    
                            }
                        }
                        pointLast = new PointF(0, 0);//每一笔绘制成功后,将pointLast设置(0,0)(与定时器配合使用),自行体会作用
                    }
                }
            }
        }
        pointLast = new PointF(0, 0);
    
    }
    

    基本就是这些了

    本来想把一些函数写到另一个命名空间里,但是发现使用了太多全局变量,导致不太好弄,我也很头大。

    Emgu中的坑

    我下载的版本是4.2的,最新的,一下载完根本找不到网上说的啥bin文件夹,是需要运行EmguCV\Solution\Windows.Desktop文件夹下的Emgu.CV.Example.sln程序后会生成bin文件夹,并且bin文件夹里也没有网上说的啥Emgu.CV.World.dll和啥这文件,应该是版本更新问题,只要把bin文件夹里的那几个(我这是4个)Emgu开头的dll文件添加到项目的引用中(那几个像ImageBox工具也是通过添加其中一个就有了,忘了哪个了,自己试试),然后将EmaguCV文件夹下的bin文件夹下的X64文件夹里的所有文件复制到自己项目里的Debug文件夹下就行(可能不需要,但是复制了全部应该是没问题的,),然后就是右击项目选择属性,在“生成”中的平台目标改为X64,应该不会还有32位的电脑吧,不会吧。当然可以试试X86,可能电脑不一样。这些可以参照这个老大哥的视频EmguCV图像处理基础教程(-)

    SHP文件的坑

    一开始我以为shp文件就可以通过shp文件的格式解析,网上有关这的一大堆,但是实际CAD的shx文件反汇编后得到shp文件格式不一样,是可以用记事本打开看的,
    第一张CAD的SHP文件打开后的样子,第二张是普通SHP文件打开的样子
    在这里插入图片描述
    在这里插入图片描述
    什么差别不需要多说了吧

    最后

    如果Emgu安装有问题或者SHP文件找不到啥的,可以留言,但是俺不一定看,也可以加我qq(2897035088),想要源代码的就别来了,代码都贴出来了,也都给注释了。那个将一个SHP文件切片函数没发,这个我一不小心删了,也懒得写了,自己看看吧,也挺简单的,就是格式别错。
    并且没什么写文章的经验,有问题欢迎指出,有想法欢迎交流,特别是有改进的意见请老大哥一定要指导一下小弟,万分感激。

    展开全文
  • 二自由度自由度机械臂simmechanicsPD控制-threejixiebi.mdl 下一步自适应PD控制中的惯性矩阵、离心力和哥氏力如何将simmechanics机械臂联合起来 希望大家多给意见
  • 二自由度自由度机械臂simmechanicsPD控制-three_jixiebi.mdl 下一步自适应PD控制中的惯性矩阵、离心力和哥氏力如何将simmechanics机械臂联合起来 希望大家多给意见
  • JACO2 6自由度机械臂上手体验

    千次阅读 2017-10-14 08:46:26
    JACO2 6自由度机械臂控制器集成度非常高,厂家给出了机械臂控制的SDK,安装好以后可通过机械臂控制软件进行控制。控制软件界面图如下图所示。 该控制软件内集成了机械臂的控制功能、机械臂各个关节当前角度、运行...

    本次讲解有关机械臂软件及控制的部分。JACO2 6自由度机械臂控制器集成度非常高,厂家给出了机械臂控制的SDK,安装好以后可通过机械臂控制软件进行控制。控制软件界面图如下图所示。控制软件界面
    该控制软件内集成了机械臂的控制功能、机械臂各个关节当前角度、运行速度、温度、电流、电压的监视功能,USB或Internet通信方式的切换功能等,基本上满足了客户的需求。
    除此之外,为了方便客户进行二次开发,厂家给出了机械臂控制的动态链接库,通过此动态链接库可获取机械臂控制的API函数(注意函数底层不可获取),通过调用API函数,用于可以进行二次开发,自己编写程序对机械臂进行控制。厂家给的API函数通过C++语言编写的,所以推荐客户进行二次开发时同样使用C++语言编写。
    机械臂非常灵活,控制时可进行单个关节点动、多关节联动、还可进行机械臂的示教操作。通过设定机械臂的运行速度,可进行机械臂的快速运动控制与低速运动控制。同时厂家自带了机械臂的详细参数文档,DH坐标系建立的方法等,据此用户可自己进行机械臂的正反解算法。(机械臂的API函数中集成了机械臂的正反解算法。)
    JACO2机械臂是中国国内比较新的一款机械臂,整体集成度非常高,比较先进。

    展开全文
  • 针对目前航空发动机主泵装配主要靠人力搬运这一问题,设计了一种结构为级升降多自由度机械臂安装设备,该系统采用两级升降多自由度升降机械臂虚拟建模,通过机器人自动化水平的搬移和装配,不仅简化了重体力人工装配,...
  • 针对本文所研制的走 自 由度机械臂 , 设计 一种基于 CAN 总线通讯 的控 制系 统 。 通   过 D enav i t-Har tenber g 参数法构建机械臂的数学模型 , 推导正运动学 公式 , 设计基于牛 ...
  • 星载两自由度机械臂的碰撞动力学与稳定控制方法,郜志鹏,徐晓慧,本文主要针对星载两自由度机械臂系统,研究其动力学与阻尼控制。本文首先分析了所研究系统的运动学,之后利用Lagrange 第类方程,
  • 自由度机械臂SolidWorks模型转换为URDF模型一、工具下载1.1 下载SolidWorks1.2 下载URDF插件、建立机械臂坐标系2.1 概要2.2 建立坐标系三、转换URDF文件3.1 加载URDF插件3.2 配置机械臂参数3.3 修改各连杆颜色...

    一、工具下载

    1.1 下载SolidWorks

    本文所使用的建模软件为SolidWorks 2018,软件(含破解器)下载地址如下:

    https://pan.baidu.com/s/1JnU5cXiXVAL58RfCxjCN3w 
    提取码:dfu2
    压缩包解压密码:0daydown
    

    1.2 下载URDF插件

    • URDF插件【官方下载】地址如下:

       http://wiki.ros.org/sw_urdf_exporter
      
    • URDF插件【网盘下载】地址如下:

       链接:https://pan.baidu.com/s/1OjHHbYYVexuZKGd3veA33w 
       提取码:j59t
      

    二、建立机械臂坐标系

    2.1 概要

    在ROS中使用MoveIt!和Rviz对机械臂进行运动学求解,机械臂各个关节的坐标系建立可以直接在SolidWorks内完成。针对机械臂每一个关节,通常将基准轴与关节坐标系的Z轴重合,如下图所示。
    在这里插入图片描述

    2.2 建立坐标系

    • 】若建立的机械臂各关节能直接添加基准轴,可省略第2步。
    1. 在SolidWorks功能区,点击【参考几何体】。
    2. 选择【点】,在机械臂基座上点击一个圆柱面(该圆柱面中心通常与第一个关节旋转轴在同一条线上);在第一个关节位置,点击电机旋转轴所对应圆柱面,建立了第二个点;以此类推,直到机械臂第六个关节旋转轴的点建立完成;综上,一共建立了7个点。
    3. 选择【基准轴】,由于机械臂基座为静止状态,没有旋转运动,因此无需建立基准轴。从第一个关节开始,依次点击第2步所建立的点,完成基准轴的建立;综上,一共建立了6个基准轴。
    4. 选择【坐标系】,点击机械臂基座的“点”,将Z轴方向设成垂直机械臂基座朝上(Z轴参考面选择一个与底座平行的任意面即可),为便于区分,将基座标系命名为坐标系0,X轴方向和Y轴方向根据自身需求设置;从第一个关节到第六个关节,Z轴的方向统一设置成与第3步建立的基准轴重合 ,X轴方向和Y轴方向根据自身需求设置,坐标系命名为:坐标系1、坐标系2…坐标系6;综上,一共建立了7个坐标系。

    三、转换URDF文件

    3.1 加载URDF插件

    在SolidWorks顶部菜单栏点击【工具】,点击最下方三角形图案,往下翻阅,点击【File】→【Export as URDF】,如下图所示。
    在这里插入图片描述

    3.2 配置机械臂参数

    第一步:

    • 【Link Name】:填写机械臂的基座名称,通常命名为base_link。
    • 【Global Origin Coordinate System】:机械臂基座所对应参考坐标系,选择坐标系0。
    • 【Link Components】:选择组成机械臂基座(base_link)的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与机械臂基座(base_link)连接的子连杆数量,此处为1个。
    • 点击base_link下的Empty_Link。

    在这里插入图片描述
    第二步:

    • 【Link Name】:填写机械臂基座的子连杆名称,便于区分,将该子连杆命名为second。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:base_link和second,便于区分,将该关节命名为joint_base_second。
    • 【Reference Coordinate System】:连杆second所对应的坐标系,选择坐标系1。
    • 【Reference Axis】:连杆second的旋转轴,选择基准轴1。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆second的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆second连接的子连杆数量,此处为1个。
    • 点击second下的Empty_Link

    在这里插入图片描述
    第三步:

    • 【Link Name】:填写连杆second的子连杆名称,将该子连杆命名为third。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:连杆second和连杆third,将该关节命名为joint_second_third。
    • 【Reference Coordinate System】:连杆third所对应的坐标系,选择坐标系2。
    • 【Reference Axis】:连杆third的旋转轴,选择基准轴2。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆third的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆third连接的子连杆数量,此处为1个。
    • 点击third下的Empty_Link。

    在这里插入图片描述
    第四步:

    • 【Link Name】:填写连杆third的子连杆名称,将该子连杆命名为fourth。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:连杆third和连杆fourth,将该关节命名为joint_third_fourth。
    • 【Reference Coordinate System】:连杆fourth所对应的坐标系,选择坐标系3。
    • 【Reference Axis】:连杆fourth的旋转轴,选择基准轴3。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆fourth的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆fourth连接的子连杆数量,此处为1个。
    • 点击fourth下的Empty_Link。

    在这里插入图片描述
    第五步:

    • 【Link Name】:填写连杆fourth的子连杆名称,将该子连杆命名为fifth。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:连杆fourth和连杆fifth,将该关节命名为joint_fourth_fifth。
    • 【Reference Coordinate System】:连杆fifth所对应的坐标系,选择坐标系4。
    • 【Reference Axis】:连杆fifth的旋转轴,选择基准轴4。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆fifth的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆fifth连接的子连杆数量,此处为1个。
    • 点击fifth下的Empty_Link。

    在这里插入图片描述
    第六步:

    • 【Link Name】:填写连杆fifth的子连杆名称,将该子连杆命名为sixth。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:连杆fifth和连杆sixth,将该关节命名为joint_fifth_sixth。
    • 【Reference Coordinate System】:连杆sixth所对应的坐标系,选择坐标系5。
    • 【Reference Axis】:连杆sixth的旋转轴,选择基准轴5。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆sixth的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆sixth连接的子连杆数量,此处为1个。
    • 点击sixth下的Empty_Link。

    在这里插入图片描述
    第七步:

    • 【Link Name】:填写连杆sixth的子连杆名称(也即末端执行器的名称),将该子连杆命名为seventh。
    • 【Joint Name】:填写关节名称,该关节连接的2个连杆为:连杆sixth和连杆seventh,将该关节命名为joint_sixth_seventh。
    • 【Reference Coordinate System】:连杆seventh所对应的坐标系,选择坐标系6。
    • 【Reference Axis】:连杆seventh的旋转轴,选择基准轴6。
    • 【Joint Type】:关节类型,选择旋转类型。
    • 【Link Components】:选择组成连杆seventh的所有零件,如下图所示蓝色部分。
    • 【Number of child links】:与连杆seventh连接的子连杆数量,此处为0个。

    在这里插入图片描述
    第八步:

    • 点击【Preview and Export】,显示下图关节属性配置界面,界面左端显示了六自由度机械臂的六个关节名称。
    • 依次点击六个关节,在界面右端【Limit*】选项中设置关节极限角度范围,通常设为±90°,也即下界-1.57弧度,上界1.57弧度。
    • 其余选项默认即可,点击界面右下角【Next】。

    在这里插入图片描述

    3.3 修改各连杆颜色

    • 通过设定RGB颜色通道和Alpha透明度通道,将六自由度机械臂各个连杆设为不同颜色,便于后续修改机械臂参数以及在ROS环境中更直观模拟机械臂运动。

    • 机械臂基座(base_link):RGB通道设为111,Alpha透明度通道设为1,如下图所示,以此类推。
      在这里插入图片描述

    • 连杆second:RGB通道设为000,Alpha透明度通道设为1。

    • 连杆third:RGB通道设为001,Alpha透明度通道设为1。

    • 连杆fourth:RGB通道设为011,Alpha透明度通道设为1。

    • 连杆fifth:RGB通道设为111,Alpha透明度通道设为1。

    • 连杆sixth:RGB通道设为110,Alpha透明度通道设为1。

    • 连杆seventh:RGB通道设为101,Alpha透明度通道设为1。

    • 点击界面右下角【Export URDF and Meshes】导出URDF文件包,选择保存路径,并将文件包命名为arm(小写)。

    3.4 修正URDF文件

    进入上述导出的文件夹arm,需做如下两处改动:

    • 进入launch文件夹,以文本方式打开display.launch,将第6行代码中False改为True。
    <launch>
      <arg
        name="model" />
      <arg
        name="gui"
        default="False" />
      <param
        name="robot_description"
        textfile="$(find arm)/urdf/arm.urdf" />
      <param
        name="use_gui"
        value="$(arg gui)" />
      <node
        name="joint_state_publisher"
        pkg="joint_state_publisher"
        type="joint_state_publisher" />
      <node
        name="robot_state_publisher"
        pkg="robot_state_publisher"
        type="state_publisher" />
      <node
        name="rviz"
        pkg="rviz"
        type="rviz"
        args="-d $(find arm)/urdf.rviz" />
    </launch>
    
    • 在arm文件夹,以文本方式打开package.xml文件,将第10行代码email的内容修改为自己的邮箱。
    <package format="2">
      <name>arm</name>
      <version>1.0.0</version>
      <description>
        <p>URDF Description package for arm</p>
        <p>This package contains configuration data, 3D models and launch files
    for arm robot</p>
      </description>
      <author>TODO</author>
      <maintainer email="TODO@email.com" />
      <license>BSD</license>
      <buildtool_depend>catkin</buildtool_depend>
      <depend>roslaunch</depend>
      <depend>robot_state_publisher</depend>
      <depend>rviz</depend>
      <depend>joint_state_publisher</depend>
      <depend>gazebo</depend>
      <export>
        <architecture_independent />
      </export>
    </package>
    
    展开全文
  • 机器人学之动力学笔记【10】—— 双旋转自由度机械臂1. conditions2. Velocity and acceleration propagations(上行运算)2.1 计算第一杆件2.2 计算第杆件3. Force and torque propagations(下行计算)4. Joint ...
  • 这是选自国外杂志“CircuitCellar”上的一篇技术文章,介绍如何用Matlab和Arduino制作一个4自由度机械臂。这是文章的第一部分,第部分会在后续的文章中再做介绍。Using MATLAB and ArduinoWhen it comes to ...
  • 听说用并联的方式,也可以让机械臂达到六自由度,而且还能避免奇异情况的产生。 但现在还是研究串联机器人的奇异情况吧。 一是 极限奇异 是内部奇异 工具——雅可比矩阵。 判断方法:雅可比矩阵(6X6)的秩小于其...
  • 针对机械臂运动轨迹控制中存在的跟踪...对二自由度机械臂进行仿真,结果表明,使用该控制方案对机械臂进行轨迹跟踪控制具有较高的控制精度,且因采用EC-RBF学习算法使网络具有更快的训练速度,从而使得控制过程较迅速。
  • 随着工业生产技术的迅猛发展,机械臂在制造业中的地位越来越突出。...然后,针对二自由度机械臂设计PD控制器,并基于S函数在MATLAB中搭建Simulink控制图,对采用PD控制的机械臂的轨迹跟踪性能进行了仿真研究。
  • 来自国外某大学机器人课程的课后作业,以二自由度机械臂为例,详细描述了动力学辨识的基本过程,涉及到软件硬件的设计,对于刚入门机器人动力学的同学具有蛮大参考意义的!

空空如也

空空如也

1 2 3 4
收藏数 70
精华内容 28
关键字:

二自由度机械臂