C# 六自由度机械臂正反解计算

目录

一、数学建模

1、连杆定义

2、建立连杆坐标系的步骤和举例

(1)步骤说明

(2)举例说明

3、PUMA560串联机械臂建模

(1)各关节和坐标系建立

(2)正解计算各关节矩阵如下

(3)逆解计算如下

二、C#程序实现

1、软件界面

2、关键功能代码实现

(1)UI中绘制机械臂

(2)正解计算

[① MDH参数](#① MDH参数)

②正解计算

(3)逆解计算

[① MDH参数](#① MDH参数)

②逆解计算

(4)UI中应用

三、工程下载链接


一、数学建模

(1)Matlab中PUMA560六自由度机械臂正反解,博客连接:https://blog.csdn.net/panjinliang066333/article/details/121681602?spm=1011.2415.3001.5331

(2)机械臂动画示意图,见博客:https://blog.csdn.net/panjinliang066333/article/details/141264547?spm=1011.2415.3001.5331

(3)PUMA560六自由度机械臂,关节示意图说明

模型示意图

实物示意图

1、连杆定义

备注:文档介绍说明,引用教材《机器人学》-蔡自兴

2、建立连杆坐标系的步骤和举例

(1)步骤说明

(2)举例说明

3、PUMA560串联机械臂建模

(1)各关节和坐标系建立

(2)正解计算各关节矩阵如下

则末关节矩阵:

(3)逆解计算如下

多个解情况

二、C#程序实现

1、软件界面

主界面-初始

主界面-运行

参数设置

2、关键功能代码实现

(1)UI中绘制机械臂

机械臂如上面主界面图片所示,使用SharpGL库文件在Winform中,绘制具有工业风格的六轴机械臂

代码

复制代码
public void DrawRobot1(ref OpenGL gl, float[] angle, float[] yLength, bool isPuma560_Six)
        {
            // 开启深度测试和光照
            gl.Enable(OpenGL.GL_DEPTH_TEST);
            gl.Enable(OpenGL.GL_LIGHTING);
            gl.Enable(OpenGL.GL_LIGHT0);
            gl.Enable(OpenGL.GL_LIGHT1);
            gl.ShadeModel(OpenGL.GL_SMOOTH);

            // 设置更强的光源
            float[] light0Pos = { 20.0f, 30.0f, 40.0f, 1.0f };
            float[] light0Ambient = { 0.4f, 0.4f, 0.4f, 1.0f };
            float[] light0Diffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
            float[] light0Specular = { 1.0f, 1.0f, 1.0f, 1.0f };

            float[] light1Pos = { -20.0f, 20.0f, -30.0f, 1.0f };
            float[] light1Ambient = { 0.2f, 0.2f, 0.2f, 1.0f };
            float[] light1Diffuse = { 0.6f, 0.6f, 0.6f, 1.0f };

            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, light0Pos);
            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, light0Ambient);
            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, light0Diffuse);
            gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, light0Specular);

            gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, light1Pos);
            gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_AMBIENT, light1Ambient);
            gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, light1Diffuse);

            // 设置材质属性
            gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, matAmbient);
            gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, matDiffuse);
            gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, matSpecular);
            gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, matShininess);

            // 绘制机器人底座
            DrawBase(ref gl, yLength[0]);

            // 在基座左下角绘制固定的世界坐标系
            DrawFixedWorldCoordinateSystem(ref gl, yLength[0]);

            // 绘制底座与第一关节的连接器
            gl.PushMatrix();
            gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
            DrawBaseToJoint1Connector(ref gl);
            gl.PopMatrix();

            // 第一关节 - 绕Y轴旋转
            gl.PushMatrix();
            {
                gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                DrawJoint1(ref gl, yLength[0]);
            }
            gl.PopMatrix();

            // 第二关节 - 绕X轴旋转
            gl.PushMatrix();
            {
                gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                gl.Translate(2.5f, yLength[0] / 2, 0.0f);
                gl.Rotate(angle[1], 1.0f, 0.0f, 0.0f);
                // 注意:关节2现在从关节1的圆柱末端开始
                DrawJoint2(ref gl, yLength[1]);
            }
            gl.PopMatrix();

            // 第三关节 - 绕X轴旋转
            gl.PushMatrix();
            {
                gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                gl.Translate(2.5f, yLength[0] / 2, 0.0f);
                gl.Rotate(angle[1], 1.0f, 0.0f, 0.0f);
                gl.Translate(0.0f, yLength[1], 0.0f);
                gl.Rotate(angle[2], 1.0f, 0.0f, 0.0f);
                DrawJoint3(ref gl, yLength[2]);
            }
            gl.PopMatrix();

            // 第四关节 - 绕Y轴旋转
            gl.PushMatrix();
            {
                gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                gl.Translate(2.5f, yLength[0] / 2, 0.0f);
                gl.Rotate(angle[1], 1.0f, 0.0f, 0.0f);
                gl.Translate(0.0f, yLength[1], 0.0f);
                gl.Rotate(angle[2], 1.0f, 0.0f, 0.0f);
                gl.Translate(0.0f, yLength[2], 0.0f);
                gl.Rotate(angle[3], 0.0f, 1.0f, 0.0f);
                DrawJoint4(ref gl, yLength[3]);
            }
            gl.PopMatrix();

            if (isPuma560_Six)
            {
                // 第五关节 - 绕X轴旋转
                gl.PushMatrix();
                {
                    gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                    gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                    gl.Translate(2.5f, yLength[0] / 2, 0.0f);
                    gl.Rotate(angle[1], 1.0f, 0.0f, 0.0f);
                    gl.Translate(0.0f, yLength[1], 0.0f);
                    gl.Rotate(angle[2], 1.0f, 0.0f, 0.0f);
                    gl.Translate(0.0f, yLength[2], 0.0f);
                    gl.Rotate(angle[3], 0.0f, 1.0f, 0.0f);
                    gl.Translate(0.0f, yLength[3], 0.0f);
                    gl.Rotate(angle[4], 1.0f, 0.0f, 0.0f);
                    // 注意:关节5现在从关节4的球体顶部开始
                    DrawJoint5(ref gl, yLength[4]);
                }
                gl.PopMatrix();

                // 第六关节 - 绕Y轴旋转 + 末端坐标系
                gl.PushMatrix();
                {
                    gl.Translate(0.0f, -yLength[0] / 2, 0.0f);
                    gl.Rotate(angle[0], 0.0f, 1.0f, 0.0f);
                    gl.Translate(2.5f, yLength[0] / 2, 0.0f);
                    gl.Rotate(angle[1], 1.0f, 0.0f, 0.0f);
                    gl.Translate(0.0f, yLength[1], 0.0f);
                    gl.Rotate(angle[2], 1.0f, 0.0f, 0.0f);
                    gl.Translate(0.0f, yLength[2], 0.0f);
                    gl.Rotate(angle[3], 0.0f, 1.0f, 0.0f);
                    gl.Translate(0.0f, yLength[3], 0.0f);
                    gl.Rotate(angle[4], 1.0f, 0.0f, 0.0f);
                    gl.Translate(0.0f, yLength[4], 0.0f);
                    gl.Rotate(angle[5], 0.0f, 1.0f, 0.0f);
                    DrawJoint6(ref gl, yLength[5]);
                }
                gl.PopMatrix();
            }

            // 绘制世界坐标系
            DrawWorldCoordinateSystem(ref gl);

            gl.Disable(OpenGL.GL_LIGHTING);
        }

(2)正解计算

① MDH参数
复制代码
/// <summary>
        /// 构造函数,内部默认MDH参数
        /// </summary>
        public Puma560FK()
        {
            _mdhParams = new double[,]
            {
              //[  d,          a,              alpha]
                {  0,          0,              0               },
                {  0,          0.180,          -Math.PI/2      },
                {  0,          0.600,          0               },
                {  0.630,      0.130,          -Math.PI/2      },
                {  0,          0,              Math.PI/2       },
                {  0,          0,              -Math.PI/2      }
            };
        }

        /// <summary>
        /// 构造函数,外部输入MDH参数
        /// </summary>
        /// <param name="mdhParameters"></param>
        public Puma560FK(double[,] mdhParameters)
        {
            if (mdhParameters == null)
                throw new ArgumentNullException("mdhParameters");

            if (mdhParameters.GetLength(0) != 6 || mdhParameters.GetLength(1) != 3)
                throw new ArgumentException("MDH参数必须是6x4矩阵");

            _mdhParams = (double[,])mdhParameters.Clone();
        }
②正解计算
复制代码
        /// <summary>
        /// 计算机器人末端位姿(齐次变换矩阵)
        /// </summary>
        /// <param name="theta">6个关节角度数组(弧度)</param>
        /// <returns>4x4齐次变换矩阵</returns>
        public double[,] ForwardKinematics(double[] theta)
        {
            if (theta == null || theta.Length != 6)
                throw new ArgumentException("需要6个关节角度参数");

            // 计算各关节变换矩阵
            double[,] T01 = CalculateTransform(theta[0] * Math.PI / 180.0f, _mdhParams[0, 0], _mdhParams[0, 1], _mdhParams[0, 2]);
            double[,] T12 = CalculateTransform(theta[1] * Math.PI / 180.0f, _mdhParams[1, 0], _mdhParams[1, 1], _mdhParams[1, 2]);
            double[,] T23 = CalculateTransform(theta[2] * Math.PI / 180.0f, _mdhParams[2, 0], _mdhParams[2, 1], _mdhParams[2, 2]);
            double[,] T34 = CalculateTransform(theta[3] * Math.PI / 180.0f, _mdhParams[3, 0], _mdhParams[3, 1], _mdhParams[3, 2]);
            double[,] T45 = CalculateTransform(theta[4] * Math.PI / 180.0f, _mdhParams[4, 0], _mdhParams[4, 1], _mdhParams[4, 2]);
            double[,] T56 = CalculateTransform(theta[5] * Math.PI / 180.0f, _mdhParams[5, 0], _mdhParams[5, 1], _mdhParams[5, 2]);

            // 级联变换矩阵
            double[,] T02 = MatrixMultiply(T01, T12);
            double[,] T03 = MatrixMultiply(T02, T23);
            double[,] T04 = MatrixMultiply(T03, T34);
            double[,] T05 = MatrixMultiply(T04, T45);
            double[,] T06 = MatrixMultiply(T05, T56);

            return T06;
        }

(3)逆解计算

① MDH参数
复制代码
        MDH = new double[,]
            {
              //[  d,          a-1,            alpha-1         ]
                {  0,          0,              0               },
                {  0,          0.180,          -Math.PI/2      },
                {  0,          0.600,          0               },
                {  0.630,      0.130,          -Math.PI/2      },
                {  0,          0,              Math.PI/2       },
                {  0,          0,              -Math.PI/2      }
            };
        }

        /// <summary>
        /// 构造函数,外部输入MDH参数
        /// </summary>
        /// <param name="mdhParameters"></param>
        public Puma560IK(double[,] mdhParameters)
        {
            if (mdhParameters == null)
                throw new ArgumentNullException("mdhParameters");

            if (mdhParameters.GetLength(0) != 6 || mdhParameters.GetLength(1) != 3)
                throw new ArgumentException("MDH参数必须是6x3矩阵");

            MDH = (double[,])mdhParameters.Clone();
        }
②逆解计算
复制代码
        public double[,] ComputeIK(double[,] Tbe)
        {
            // Extract position and orientation components from transformation matrix
            double nx = Tbe[0, 0], ny = Tbe[1, 0], nz = Tbe[2, 0];
            double ox = Tbe[0, 1], oy = Tbe[1, 1], oz = Tbe[2, 1];
            double ax = Tbe[0, 2], ay = Tbe[1, 2], az = Tbe[2, 2];
            double px = Tbe[0, 3], py = Tbe[1, 3], pz = Tbe[2, 3];

            //// Extract MDH parameters from the 6x3 matrix
            //double d4 = MDH[3, 0];
            //double a1 = MDH[1, 1];
            //double a2 = MDH[2, 1];
            //double a3 = MDH[3, 1];
            //double d2 = 0, d3 = 0; // These are zero in the MDH table

            //// Extract alpha angles from MDH
            //double f1 = MDH[1, 2];  // alpha1
            //double f3 = MDH[3, 2];  // alpha3
            //double f4 = MDH[4, 2];  // alpha4
            //double f5 = MDH[5, 2];  // alpha5

            double d1 = MDH[0, 0];
            double d2 = MDH[1, 0];
            double d3 = MDH[2, 0];
            double d4 = MDH[3, 0];
            double d5 = MDH[4, 0];
            double d6 = MDH[5, 0];

            double a1 = MDH[1, 1];
            double a2 = MDH[2, 1];
            double a3 = MDH[3, 1];
            double a4 = MDH[4, 1];
            double a5 = MDH[5, 1];

            double f1 = MDH[1, 2];  // alpha1
            double f2 = MDH[2, 2];  // alpha2
            double f3 = MDH[3, 2];  // alpha3
            double f4 = MDH[4, 2];  // alpha4
            double f5 = MDH[5, 2];  // alpha5

            // Calculate theta1 solutions
            double t11 = -Math.Atan2(-py, px) + Math.Atan2((d2 - d3) / Math.Sin(f1),
                Math.Sqrt(Math.Pow(px * Math.Sin(f1), 2) + Math.Pow(py * Math.Sin(f1), 2) - Math.Pow(d2 - d3, 2)));

            double t12 = -Math.Atan2(-py, px) + Math.Atan2((d2 - d3) / Math.Sin(f1),
                -Math.Sqrt(Math.Pow(px * Math.Sin(f1), 2) + Math.Pow(py * Math.Sin(f1), 2) - Math.Pow(d2 - d3, 2)));

            // Calculate intermediate values for theta3
            double m3_1 = pz * Math.Sin(f1);
            double n3_1 = a1 - px * Math.Cos(t11) - py * Math.Sin(t11);

            double m3_2 = pz * Math.Sin(f1);
            double n3_2 = a1 - px * Math.Cos(t12) - py * Math.Sin(t12);

            // Calculate theta3 solutions
            double t31 = -Math.Atan2(a2 * a3 / Math.Sin(f3), a2 * d4) +
                Math.Atan2((Math.Pow(m3_1, 2) + Math.Pow(n3_1, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2)) / Math.Sin(f3),
                Math.Sqrt(Math.Pow(2 * a2 * d4 * Math.Sin(f3), 2) + Math.Pow(2 * a2 * a3, 2) -
                Math.Pow(Math.Pow(m3_1, 2) + Math.Pow(n3_1, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2), 2)));

            double t32 = -Math.Atan2(a2 * a3 / Math.Sin(f3), a2 * d4) +
                Math.Atan2((Math.Pow(m3_1, 2) + Math.Pow(n3_1, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2)) / Math.Sin(f3),
                -Math.Sqrt(Math.Pow(2 * a2 * d4 * Math.Sin(f3), 2) + Math.Pow(2 * a2 * a3, 2) -
                Math.Pow(Math.Pow(m3_1, 2) + Math.Pow(n3_1, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2), 2)));

            double t33 = -Math.Atan2(a2 * a3 / Math.Sin(f3), a2 * d4) +
                Math.Atan2((Math.Pow(m3_2, 2) + Math.Pow(n3_2, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2)) / Math.Sin(f3),
                Math.Sqrt(Math.Pow(2 * a2 * d4 * Math.Sin(f3), 2) + Math.Pow(2 * a2 * a3, 2) -
                Math.Pow(Math.Pow(m3_2, 2) + Math.Pow(n3_2, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2), 2)));

            double t34 = -Math.Atan2(a2 * a3 / Math.Sin(f3), a2 * d4) +
                Math.Atan2((Math.Pow(m3_2, 2) + Math.Pow(n3_2, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2)) / Math.Sin(f3),
                -Math.Sqrt(Math.Pow(2 * a2 * d4 * Math.Sin(f3), 2) + Math.Pow(2 * a2 * a3, 2) -
                Math.Pow(Math.Pow(m3_2, 2) + Math.Pow(n3_2, 2) - Math.Pow(a2, 2) - Math.Pow(a3, 2) - Math.Pow(d4, 2), 2)));

            // Calculate intermediate values for theta2
            double m2_1 = a2 + a3 * Math.Cos(t31) + d4 * Math.Sin(f3) * Math.Sin(t31);
            double n2_1 = a3 * Math.Sin(t31) - d4 * Math.Sin(f3) * Math.Cos(t31);

            double m2_2 = a2 + a3 * Math.Cos(t32) + d4 * Math.Sin(f3) * Math.Sin(t32);
            double n2_2 = a3 * Math.Sin(t32) - d4 * Math.Sin(f3) * Math.Cos(t32);

            double m2_3 = a2 + a3 * Math.Cos(t33) + d4 * Math.Sin(f3) * Math.Sin(t33);
            double n2_3 = a3 * Math.Sin(t33) - d4 * Math.Sin(f3) * Math.Cos(t33);

            double m2_4 = a2 + a3 * Math.Cos(t34) + d4 * Math.Sin(f3) * Math.Sin(t34);
            double n2_4 = a3 * Math.Sin(t34) - d4 * Math.Sin(f3) * Math.Cos(t34);

            // Calculate theta2 solutions
            double t21 = Math.Atan2(m3_1 * m2_1 + n2_1 * n3_1, m3_1 * n2_1 - m2_1 * n3_1);
            double t22 = Math.Atan2(m3_1 * m2_2 + n2_2 * n3_1, m3_1 * n2_2 - m2_2 * n3_1);
            double t23 = Math.Atan2(m3_2 * m2_3 + n2_3 * n3_2, m3_2 * n2_3 - m2_3 * n3_2);
            double t24 = Math.Atan2(m3_2 * m2_4 + n2_4 * n3_2, m3_2 * n2_4 - m2_4 * n3_2);

            // Calculate intermediate values for theta5
            double m5_1 = -Math.Sin(f5) * (ax * Math.Cos(t11) * Math.Cos(t21) + ay * Math.Sin(t11) * Math.Cos(t21) + az * Math.Sin(f1) * Math.Sin(t21));
            double n5_1 = Math.Sin(f5) * (ax * Math.Cos(t11) * Math.Sin(t21) + ay * Math.Sin(t11) * Math.Sin(t21) - az * Math.Sin(f1) * Math.Cos(t21));

            double m5_2 = -Math.Sin(f5) * (ax * Math.Cos(t11) * Math.Cos(t22) + ay * Math.Sin(t11) * Math.Cos(t22) + az * Math.Sin(f1) * Math.Sin(t22));
            double n5_2 = Math.Sin(f5) * (ax * Math.Cos(t11) * Math.Sin(t22) + ay * Math.Sin(t11) * Math.Sin(t22) - az * Math.Sin(f1) * Math.Cos(t22));

            double m5_3 = -Math.Sin(f5) * (ax * Math.Cos(t12) * Math.Cos(t23) + ay * Math.Sin(t12) * Math.Cos(t23) + az * Math.Sin(f1) * Math.Sin(t23));
            double n5_3 = Math.Sin(f5) * (ax * Math.Cos(t12) * Math.Sin(t23) + ay * Math.Sin(t12) * Math.Sin(t23) - az * Math.Sin(f1) * Math.Cos(t23));

            double m5_4 = -Math.Sin(f5) * (ax * Math.Cos(t12) * Math.Cos(t24) + ay * Math.Sin(t12) * Math.Cos(t24) + az * Math.Sin(f1) * Math.Sin(t24));
            double n5_4 = Math.Sin(f5) * (ax * Math.Cos(t12) * Math.Sin(t24) + ay * Math.Sin(t12) * Math.Sin(t24) - az * Math.Sin(f1) * Math.Cos(t24));

            // Calculate theta5 solutions
            double t51 = Math.Atan2(Math.Sqrt(Math.Pow(ay * Math.Cos(t11) - ax * Math.Sin(t11), 2) +
                Math.Pow(m5_1 * Math.Cos(t31) + n5_1 * Math.Sin(t31), 2)),
                (m5_1 * Math.Sin(t31) - n5_1 * Math.Cos(t31)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t52 = Math.Atan2(-Math.Sqrt(Math.Pow(ay * Math.Cos(t11) - ax * Math.Sin(t11), 2) +
                Math.Pow(m5_1 * Math.Cos(t31) + n5_1 * Math.Sin(t31), 2)),
                (m5_1 * Math.Sin(t31) - n5_1 * Math.Cos(t31)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t53 = Math.Atan2(Math.Sqrt(Math.Pow(ay * Math.Cos(t11) - ax * Math.Sin(t11), 2) +
                Math.Pow(m5_2 * Math.Cos(t32) + n5_2 * Math.Sin(t32), 2)),
                (m5_2 * Math.Sin(t32) - n5_2 * Math.Cos(t32)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t54 = Math.Atan2(-Math.Sqrt(Math.Pow(ay * Math.Cos(t11) - ax * Math.Sin(t11), 2) +
                Math.Pow(m5_2 * Math.Cos(t32) + n5_2 * Math.Sin(t32), 2)),
                (m5_2 * Math.Sin(t32) - n5_2 * Math.Cos(t32)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t55 = Math.Atan2(Math.Sqrt(Math.Pow(ay * Math.Cos(t12) - ax * Math.Sin(t12), 2) +
                Math.Pow(m5_3 * Math.Cos(t33) + n5_3 * Math.Sin(t33), 2)),
                (m5_3 * Math.Sin(t33) - n5_3 * Math.Cos(t33)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t56 = Math.Atan2(-Math.Sqrt(Math.Pow(ay * Math.Cos(t12) - ax * Math.Sin(t12), 2) +
                Math.Pow(m5_3 * Math.Cos(t33) + n5_3 * Math.Sin(t33), 2)),
                (m5_3 * Math.Sin(t33) - n5_3 * Math.Cos(t33)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t57 = Math.Atan2(Math.Sqrt(Math.Pow(ay * Math.Cos(t12) - ax * Math.Sin(t12), 2) +
                Math.Pow(m5_4 * Math.Cos(t34) + n5_4 * Math.Sin(t34), 2)),
                (m5_4 * Math.Sin(t34) - n5_4 * Math.Cos(t34)) / (Math.Sin(f3) * Math.Sin(f4)));

            double t58 = Math.Atan2(-Math.Sqrt(Math.Pow(ay * Math.Cos(t12) - ax * Math.Sin(t12), 2) +
                Math.Pow(m5_4 * Math.Cos(t34) + n5_4 * Math.Sin(t34), 2)),
                (m5_4 * Math.Sin(t34) - n5_4 * Math.Cos(t34)) / (Math.Sin(f3) * Math.Sin(f4)));

            // Calculate theta4 solutions
            double t41 = (Math.Sin(t51) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t11) - ax * Math.Sin(t11)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t51) * Math.Sin(f3)),
                (-m5_1 * Math.Cos(t31) - n5_1 * Math.Sin(t31)) / Math.Sin(t51));

            double t42 = (Math.Sin(t52) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t11) - ax * Math.Sin(t11)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t52) * Math.Sin(f3)),
                (-m5_1 * Math.Cos(t31) - n5_1 * Math.Sin(t31)) / Math.Sin(t52));

            double t43 = (Math.Sin(t53) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t11) - ax * Math.Sin(t11)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t53) * Math.Sin(f3)),
                (-m5_2 * Math.Cos(t32) - n5_2 * Math.Sin(t32)) / Math.Sin(t53));

            double t44 = (Math.Sin(t54) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t11) - ax * Math.Sin(t11)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t54) * Math.Sin(f3)),
                (-m5_2 * Math.Cos(t32) - n5_2 * Math.Sin(t32)) / Math.Sin(t54));

            double t45 = (Math.Sin(t55) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t12) - ax * Math.Sin(t12)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t55) * Math.Sin(f3)),
                (-m5_3 * Math.Cos(t33) - n5_3 * Math.Sin(t33)) / Math.Sin(t55));

            double t46 = (Math.Sin(t56) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t12) - ax * Math.Sin(t12)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t56) * Math.Sin(f3)),
                (-m5_3 * Math.Cos(t33) - n5_3 * Math.Sin(t33)) / Math.Sin(t56));

            double t47 = (Math.Sin(t57) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t12) - ax * Math.Sin(t12)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t57) * Math.Sin(f3)),
                (-m5_4 * Math.Cos(t34) - n5_4 * Math.Sin(t34)) / Math.Sin(t57));

            double t48 = (Math.Sin(t58) == 0) ? 0 : Math.Atan2(
                ((ay * Math.Cos(t12) - ax * Math.Sin(t12)) * Math.Sin(f1) * Math.Sin(f5)) / (-Math.Sin(t58) * Math.Sin(f3)),
                (-m5_4 * Math.Cos(t34) - n5_4 * Math.Sin(t34)) / Math.Sin(t58));

            // Calculate theta6 solutions
            double e1 = nx * Math.Sin(t11) - ny * Math.Cos(t11);
            double f1_val = ox * Math.Sin(t11) - oy * Math.Cos(t11);

            double t61 = Math.Atan2(
                Math.Cos(t41) * e1 - Math.Cos(t51) * Math.Sin(t41) * f1_val,
                Math.Cos(t41) * f1_val + Math.Cos(t51) * Math.Sin(t41) * e1);

            double t62 = Math.Atan2(
                Math.Cos(t42) * e1 - Math.Cos(t52) * Math.Sin(t42) * f1_val,
                Math.Cos(t42) * f1_val + Math.Cos(t52) * Math.Sin(t42) * e1);

            double t63 = Math.Atan2(
                Math.Cos(t43) * e1 - Math.Cos(t53) * Math.Sin(t43) * f1_val,
                Math.Cos(t43) * f1_val + Math.Cos(t53) * Math.Sin(t43) * e1);

            double t64 = Math.Atan2(
                Math.Cos(t44) * e1 - Math.Cos(t54) * Math.Sin(t44) * f1_val,
                Math.Cos(t44) * f1_val + Math.Cos(t54) * Math.Sin(t44) * e1);

            double e2 = nx * Math.Sin(t12) - ny * Math.Cos(t12);
            double f2_val = ox * Math.Sin(t12) - oy * Math.Cos(t12);

            double t65 = Math.Atan2(
                Math.Cos(t45) * e2 - Math.Cos(t55) * Math.Sin(t45) * f2_val,
                Math.Cos(t45) * f2_val + Math.Cos(t55) * Math.Sin(t45) * e2);

            double t66 = Math.Atan2(
                Math.Cos(t46) * e2 - Math.Cos(t56) * Math.Sin(t46) * f2_val,
                Math.Cos(t46) * f2_val + Math.Cos(t56) * Math.Sin(t46) * e2);

            double t67 = Math.Atan2(
                Math.Cos(t47) * e2 - Math.Cos(t57) * Math.Sin(t47) * f2_val,
                Math.Cos(t47) * f2_val + Math.Cos(t57) * Math.Sin(t47) * e2);

            double t68 = Math.Atan2(
                Math.Cos(t48) * e2 - Math.Cos(t58) * Math.Sin(t48) * f2_val,
                Math.Cos(t48) * f2_val + Math.Cos(t58) * Math.Sin(t48) * e2);

            // Combine all solutions into a 8x6 matrix
            double[,] ikine_t = new double[8, 6]
            {
                {t11, t21, t31, t41, t51, t61},
                {t11, t21, t31, t42, t52, t62},
                {t11, t22, t32, t43, t53, t63},
                {t11, t22, t32, t44, t54, t64},
                {t12, t23, t33, t45, t55, t65},
                {t12, t23, t33, t46, t56, t66},
                {t12, t24, t34, t47, t57, t67},
                {t12, t24, t34, t48, t58, t68}
            };

            return ikine_t;

        }

(4)UI中应用

正解

复制代码
/// <summary>
        /// 正解计算
        /// </summary>
        /// <param name="jointAngles"></param>
        void RunDemoFK(double[] jointAngles)
        {
            
            //for (int i = 0; i < jointAngles.Length;i++ )
            //{
            //    jointAngles[i] = jointAngles[i] * Math.PI / 180.0f;
            //}
            bDataModel = false;
            try
            {
                var fk = new Puma560FK(MDH);
                double[] jointAnglesTemp = new double[6];
                for (int i = 0; i < jointAnglesTemp.Length; i++)
                {
                    jointAnglesTemp[i] = jointAngles[i] ;
                }
                // 计算正运动学
                double[,] T06 = fk.ForwardKinematics(jointAnglesTemp);
                // 打印变换矩阵
                for (int i = 0; i < 4; i++)
                {
                    for (int j = 0; j < 4; j++)
                    {
                        //Console.Write(string.Format("{0,12:F6}", T06[i, j]));
                    }
                }
                txtMatrixFK_1_1.Text = T06[0, 0].ToString("f2");
                txtMatrixFK_1_2.Text = T06[0, 1].ToString("f2");
                txtMatrixFK_1_3.Text = T06[0, 2].ToString("f2");
                txtMatrixFK_1_4.Text = T06[0, 3].ToString("f2");
                txtMatrixFK_2_1.Text = T06[1, 0].ToString("f2");
                txtMatrixFK_2_2.Text = T06[1, 1].ToString("f2");
                txtMatrixFK_2_3.Text = T06[1, 2].ToString("f2");
                txtMatrixFK_2_4.Text = T06[1, 3].ToString("f2");
                txtMatrixFK_3_1.Text = T06[2, 0].ToString("f2");
                txtMatrixFK_3_2.Text = T06[2, 1].ToString("f2");
                txtMatrixFK_3_3.Text = T06[2, 2].ToString("f2");
                txtMatrixFK_3_4.Text = T06[2, 3].ToString("f2");
                txtMatrixFK_4_1.Text = T06[3, 0].ToString("f2");
                txtMatrixFK_4_2.Text = T06[3, 1].ToString("f2");
                txtMatrixFK_4_3.Text = T06[3, 2].ToString("f2");
                txtMatrixFK_4_4.Text = T06[3, 3].ToString("f2");
                // 打印末关节位置
                double[] position = fk.GetPosition(T06);
                txtPostionAbsX_FK.Text = position[0].ToString("f4");
                txtPostionAbsY_FK.Text = position[1].ToString("f4");
                txtPostionAbsZ_FK.Text = position[2].ToString("f4");

                // 打印末关节欧拉角
                double[] euler = fk.GetEulerAngles(T06);
                //转换为角度
                double[] eulerDeg = new double[3];
                for (int i = 0; i < 3; i++)
                {
                    eulerDeg[i] = euler[i] * 180 / Math.PI;                   
                }
                txtPostionAbsA_FK.Text = eulerDeg[0].ToString("f4");     //Roll
                txtPostionAbsB_FK.Text = eulerDeg[1].ToString("f4");     //Pitch
                txtPostionAbsC_FK.Text = eulerDeg[2].ToString("f4");     //Yaw
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }

            txtJoinActualTheta1.Text = jointAngles[0].ToString("f4");
            txtJoinActualTheta2.Text = jointAngles[1].ToString("f4");
            txtJoinActualTheta3.Text = jointAngles[2].ToString("f4");
            txtJoinActualTheta4.Text = jointAngles[3].ToString("f4");
            txtJoinActualTheta5.Text = jointAngles[4].ToString("f4");
            txtJoinActualTheta6.Text = jointAngles[5].ToString("f4");


            //逆解
            txtPostionAbsX_IK.Text = txtPostionAbsX_FK.Text;
            txtPostionAbsY_IK.Text = txtPostionAbsY_FK.Text;
            txtPostionAbsZ_IK.Text = txtPostionAbsZ_FK.Text;
            txtPostionAbsA_IK.Text = txtPostionAbsA_FK.Text;
            txtPostionAbsB_IK.Text = txtPostionAbsB_FK.Text;
            txtPostionAbsC_IK.Text = txtPostionAbsC_FK.Text;
            x = Convert.ToDouble(txtPostionAbsX_IK.Text);
            y = Convert.ToDouble(txtPostionAbsY_IK.Text);
            z = Convert.ToDouble(txtPostionAbsZ_IK.Text);
            A = Convert.ToDouble(txtPostionAbsA_IK.Text);
            B = Convert.ToDouble(txtPostionAbsB_IK.Text);
            C = Convert.ToDouble(txtPostionAbsC_IK.Text);
            RunDemoIK();
        }

逆解

复制代码
void RunDemoIK()
        {
            bDataModel = true;
            txtPostionAbsX_IK.Text = x.ToString("f4");
            txtPostionAbsY_IK.Text = y.ToString("f4");
            txtPostionAbsZ_IK.Text = z.ToString("f4");
            txtPostionAbsA_IK.Text = A.ToString("f4");
            txtPostionAbsB_IK.Text = B.ToString("f4");
            txtPostionAbsC_IK.Text = C.ToString("f4");            

            try
            {
                Puma560IK solver = new Puma560IK(MDH);

                // 测试用例1:标准位置
                double[,] testPose1 = Puma560IK.CreateTransformMatrix(x, y, z, A, B, C);
                Double[,] Solves_IK = solver.ComputeIK(testPose1);          //角度值
                //转换为角度值
                for (int i = 0; i < Solves_IK.GetLength(0); i++)
                {
                    for (int j = 0; j < Solves_IK.GetLength(1); j++)
                    {
                        Solves_IK[i, j] = Solves_IK[i, j] * 180 / Math.PI;    
                        if(double.IsNaN(Solves_IK[i, j]))
                        {
                            throw new Exception("当前逆解计算不存在解!!!");
                        }
                    }
                }
                
                //第一组解
                txtSolveIK_1_1.Text = Solves_IK[0, 0].ToString("f2");
                txtSolveIK_1_2.Text = Solves_IK[0, 1].ToString("f2");
                txtSolveIK_1_3.Text = Solves_IK[0, 2].ToString("f2");
                txtSolveIK_1_4.Text = Solves_IK[0, 3].ToString("f2");
                txtSolveIK_1_5.Text = Solves_IK[0, 4].ToString("f2");
                txtSolveIK_1_6.Text = Solves_IK[0, 5].ToString("f2");

                //第二组解
                txtSolveIK_2_1.Text = Solves_IK[1, 0].ToString("f2");
                txtSolveIK_2_2.Text = Solves_IK[1, 1].ToString("f2");
                txtSolveIK_2_3.Text = Solves_IK[1, 2].ToString("f2");
                txtSolveIK_2_4.Text = Solves_IK[1, 3].ToString("f2");
                txtSolveIK_2_5.Text = Solves_IK[1, 4].ToString("f2");
                txtSolveIK_2_6.Text = Solves_IK[1, 5].ToString("f2");

                //第三组解
                txtSolveIK_3_1.Text = Solves_IK[2, 0].ToString("f2");
                txtSolveIK_3_2.Text = Solves_IK[2, 1].ToString("f2");
                txtSolveIK_3_3.Text = Solves_IK[2, 2].ToString("f2");
                txtSolveIK_3_4.Text = Solves_IK[2, 3].ToString("f2");
                txtSolveIK_3_5.Text = Solves_IK[2, 4].ToString("f2");
                txtSolveIK_3_6.Text = Solves_IK[2, 5].ToString("f2");

                //第四组解
                txtSolveIK_4_1.Text = Solves_IK[3, 0].ToString("f2");
                txtSolveIK_4_2.Text = Solves_IK[3, 1].ToString("f2");
                txtSolveIK_4_3.Text = Solves_IK[3, 2].ToString("f2");
                txtSolveIK_4_4.Text = Solves_IK[3, 3].ToString("f2");
                txtSolveIK_4_5.Text = Solves_IK[3, 4].ToString("f2");
                txtSolveIK_4_6.Text = Solves_IK[3, 5].ToString("f2");

                //第五组解
                txtSolveIK_5_1.Text = Solves_IK[4, 0].ToString("f2");
                txtSolveIK_5_2.Text = Solves_IK[4, 1].ToString("f2");
                txtSolveIK_5_3.Text = Solves_IK[4, 2].ToString("f2");
                txtSolveIK_5_4.Text = Solves_IK[4, 3].ToString("f2");
                txtSolveIK_5_5.Text = Solves_IK[4, 4].ToString("f2");
                txtSolveIK_5_6.Text = Solves_IK[4, 5].ToString("f2");

                //第六组解
                txtSolveIK_6_1.Text = Solves_IK[5, 0].ToString("f2");
                txtSolveIK_6_2.Text = Solves_IK[5, 1].ToString("f2");
                txtSolveIK_6_3.Text = Solves_IK[5, 2].ToString("f2");
                txtSolveIK_6_4.Text = Solves_IK[5, 3].ToString("f2");
                txtSolveIK_6_5.Text = Solves_IK[5, 4].ToString("f2");
                txtSolveIK_6_6.Text = Solves_IK[5, 5].ToString("f2");

                //第七组解
                txtSolveIK_7_1.Text = Solves_IK[6, 0].ToString("f2");
                txtSolveIK_7_2.Text = Solves_IK[6, 1].ToString("f2");
                txtSolveIK_7_3.Text = Solves_IK[6, 2].ToString("f2");
                txtSolveIK_7_4.Text = Solves_IK[6, 3].ToString("f2");
                txtSolveIK_7_5.Text = Solves_IK[6, 4].ToString("f2");
                txtSolveIK_7_6.Text = Solves_IK[6, 5].ToString("f2");

                //第八组解
                txtSolveIK_8_1.Text = Solves_IK[7, 0].ToString("f2");
                txtSolveIK_8_2.Text = Solves_IK[7, 1].ToString("f2");
                txtSolveIK_8_3.Text = Solves_IK[7, 2].ToString("f2");
                txtSolveIK_8_4.Text = Solves_IK[7, 3].ToString("f2");
                txtSolveIK_8_5.Text = Solves_IK[7, 4].ToString("f2");
                txtSolveIK_8_6.Text = Solves_IK[7, 5].ToString("f2");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error: " + ex.Message);
            }

            
        }

三、工程下载链接

https://download.csdn.net/download/panjinliang066333/92233890

相关推荐
我不是程序猿儿7 小时前
【C#】XtraMessageBox(DevExpress)与MessageBox(WinForms 标准库)的区别
开发语言·c#
偶尔的鼠标人7 小时前
Avalonia/WPF 打开子窗口,并且跨页面传值
c#·wpf·mvvm·avalonia
玖笙&7 小时前
✨WPF编程进阶【6.1】:图形原则(附源码)
c++·c#·wpf·visual studio
huoshan123457 小时前
给旧版 .NET 也开一扇“私有之门”——ILAccess.Fody 实现原理与设计
c#·.net·fody·il·mono.cecil
纸照片8 小时前
WPF中为Button设置IsMouseOver和IsPressed事件中改变背景颜色不起作用
c#·.net·wpf
关关长语9 小时前
Dotnet使用System.Xml.Serialization处理Xml序列化
xml·c#·.net
歪歪10012 小时前
在C#中除了按属性排序,集合可视化器还有哪些辅助筛选的方法?
开发语言·前端·ide·c#·visual studio
weixin_3077791312 小时前
C#程序实现将Teradata的存储过程转换为Snowflake的sql的存储过程
数据库·数据仓库·c#·云计算·迁移学习
李高钢12 小时前
c#获取当前程序所在目录避坑
开发语言·数据库·c#