纯手工Codesys的6轴机械手控制程序记录

目录

一、概述

二、程序的架构

 3.1 截图

 3.2 架构介绍

 3.3 代码的展示

         3.3.1机械手正解

         3.3.2机械手逆解

         3.3.3主程序

         3.3.4运动主程序

         3.3.4.1直线子运动程序

         3.3.4.2阿基米德旋线+球面子运动程序

三、总结


一、概述

 

        一直想学习6轴机械手的运动学,一直没有空闲。趁着12月疫情,闭门研究6轴机械手的运动并且用codesys简单实现。

        机械手的控制我自己的理解是,机械手末端的位置和方向 -> 逆解出对应各关节的角度 ->控制关节旋转至目标角度。机械手的执行的运动品质,就转变为 机械手末端的位置和方向的大量的插补点,而且要考虑这些插补点逆解出的各关节的角度的运动趋势。特别是 各关节的运动趋势,因为运动趋势是最终关节电机如何运转和能不能运转的关键点。

        本文利用codesys实现和验证6轴机械手的正解和逆解和简易的6轴机械手的插补运动(直线、阿基米德旋线+球面)。其中直线运动子程序,做了简单的梯形加减速,是固定方向下的位置直线运动轨迹;而阿基米德旋线+球面则只做了位置插补控制,做了变方向和变位置的运动轨迹。

二、程序的架构

3.1 截图

 3.2 架构介绍

程序主要分了3个task。

MainTask(100ms,优先级8) := 按钮和界面的触发和更新。

Motion_Task(50ms,优先级2) := 运动的插补运算。

Servo_Task(1ms,优先级1) := 关节伺服运动控制功能块。(虽然是虚拟轴)

 3.3 代码的展示

         3.3.1机械手正解


//DirectKinematics 机械手正解函数,作者:AlongWu


FUNCTION DirectKinematics : HandJointPos
VAR_INPUT
	

HJointAg			:HandJointAg;										//机械手关节数据

END_VAR
VAR
	
T_Htf_Arr			:ARRAY[0..5] OF ARRAY[0..3,0..3] OF LREAL;			//齐次矩阵数组
T_Arr				:ARRAY[0..5] OF ARRAY[0..3,0..3] OF LREAL;			//机械手关节映射矩阵

i					:INT;




END_VAR



//DH表格thet角赋值

GVL.DHTable.ItemArr[0].Thet  := HJointAg.J1;
GVL.DHTable.ItemArr[1].Thet  := HJointAg.J2;
GVL.DHTable.ItemArr[2].Thet  := HJointAg.J3;
GVL.DHTable.ItemArr[3].Thet  := HJointAg.J4;
GVL.DHTable.ItemArr[4].Thet  := HJointAg.J5;
GVL.DHTable.ItemArr[5].Thet  := HJointAg.J6;


//DH表格转齐次矩阵


FOR i:=0 TO 5 BY 1 DO 
	T_Htf_Arr[i] := HTF_MatrixSTD(GVL.DHTable.ItemArr[i]);
END_FOR

//右乘,获得全部关节的映射矩阵
T_Arr[0] := T_Htf_Arr[0];
T_Arr[1] :=Matrix44PointMul(T_Arr[0],T_Htf_Arr[1]);
T_Arr[2] :=Matrix44PointMul(T_Arr[1],T_Htf_Arr[2]);
T_Arr[3] :=Matrix44PointMul(T_Arr[2],T_Htf_Arr[3]);
T_Arr[4] :=Matrix44PointMul(T_Arr[3],T_Htf_Arr[4]);
T_Arr[5] :=Matrix44PointMul(T_Arr[4],T_Htf_Arr[5]);


//通过映射矩阵获取各关节的位置信息

DirectKinematics.J1 := RotationMatrix2pos(T_Arr[0]);
DirectKinematics.J2 := RotationMatrix2pos(T_Arr[1]);
DirectKinematics.J3 := RotationMatrix2pos(T_Arr[2]);
DirectKinematics.J4 := RotationMatrix2pos(T_Arr[3]);
DirectKinematics.J5 := RotationMatrix2pos(T_Arr[4]);
DirectKinematics.J6 := RotationMatrix2pos(T_Arr[5]);




         3.3.2机械手逆解

//机械手逆解 InverseKinematics  ,作者:AlongWu
FUNCTION InverseKinematics : HandJointResult

VAR CONSTANT
EmptyJointStru				:ARRAY[0..3] OF HandJointAg:=[															
															(J1Gap:=0,J2Gap:=0,J3Gap:=0,J4Gap:=0,J5Gap:=0,J6Gap:=0,J1:=0,J2:=0,J3:=0,J4:=0,J5:=0,J6:=0),
															(J1Gap:=0,J2Gap:=0,J3Gap:=0,J4Gap:=0,J5Gap:=0,J6Gap:=0,J1:=0,J2:=0,J3:=0,J4:=0,J5:=0,J6:=0),
															(J1Gap:=0,J2Gap:=0,J3Gap:=0,J4Gap:=0,J5Gap:=0,J6Gap:=0,J1:=0,J2:=0,J3:=0,J4:=0,J5:=0,J6:=0),
															(J1Gap:=0,J2Gap:=0,J3Gap:=0,J4Gap:=0,J5Gap:=0,J6Gap:=0,J1:=0,J2:=0,J3:=0,J4:=0,J5:=0,J6:=0)
														 ];

														 
IptAgMax					:LREAL:=500;			//插补角度间距允许值													
END_VAR


VAR_INPUT
	
KeyPosStru					:PosStru;					//目标坐标数据元
SortType					:INT;						//逆解结果的决策要求(0=总关节移动量最少,1=每个关节移动量不能大于"插补角度间距允许值")

END_VAR
VAR
	

i							:INT;
j							:INT;
//k							:INT;

T							:ARRAY[0..3,0..3] OF LREAL;							//目标坐标元的旋转矩阵
T_Arr						:ARRAY[0..5] OF ARRAY[0..3,0..3] OF LREAL;			//机械手关节映射矩阵

nx							:LREAL;
ny							:LREAL;
nz							:LREAL;
ox							:LREAL;
oy							:LREAL;
oz							:LREAL;
ax							:LREAL;
ay							:LREAL;
az							:LREAL;
px							:LREAL;
py							:LREAL;
pz							:LREAL;

tmp1						:LREAL;
tmp2						:LREAL;
tmp3						:LREAL;
tmp4						:LREAL;
tmp5						:LREAL;
tmp6						:LREAL;
d1							:LREAL;
d4							:LREAL;
d5							:LREAL;
d6							:LREAL;
a2							:LREAL;
a3							:LREAL;
sin1						:LREAL;
cos1						:LREAL;
sin2						:LREAL;
cos2						:LREAL;
sin3						:LREAL;
cos3						:LREAL;
sin4						:LREAL;
cos4						:LREAL;
sin5						:LREAL;
cos5						:LREAL;
sin6						:LREAL;
cos6						:LREAL;
th1							:LREAL;
th2							:LREAL;
th3							:LREAL;
th4							:LREAL;
th5							:LREAL;
th6							:LREAL;
				
m							:LREAL;
n							:LREAL;

c							:BOOL;

Thet1						:ARRAY[0..1] OF Thet1Result;				//逆解角度结果缓存

tmpJointStru				:ARRAY[0..3] OF HandJointAg;				//逆解结果缓存

tmpJointStruLen				:INT;

tmpJs						:HandJointAg;

CheckHandJointPos			:HandJointPos;

END_VAR



//isNull的标志复位
InverseKinematics.IsNull :=FALSE;


//目标坐标数据元转旋转矩阵
T :=PosStur2RotationMatrix(KeyPosStru);


//旋转矩阵的值定位
nx:=T[0,0];
ny:=T[1,0];
nz:=T[2,0];
ox:=T[0,1];
oy:=T[1,1];
oz:=T[2,1];
ax:=T[0,2];
ay:=T[1,2];
az:=T[2,2];
px:=T[0,3];
py:=T[1,3];
pz:=T[2,3];


//DH表格赋值
d6 := GVL.DHTable.ItemArr[5].ZGap;
d4 := GVL.DHTable.ItemArr[3].ZGap;
d1 := GVL.DHTable.ItemArr[0].ZGap;
d5 := GVL.DHTable.ItemArr[4].ZGap;

a2 := GVL.DHTable.ItemArr[1].Bridge;
a3 := GVL.DHTable.ItemArr[2].Bridge;


//开始计算


tmp1 := d6 * ay - py;
tmp2 := px - d6 * ax;
tmp1 := tmp1 * tmp1 + tmp2 * tmp2;

IF tmp1 = 0 THEN
	tmp2 :=0;
ELSE
	tmp2 := d4 / SQRT(tmp1);
END_IF
 
IF tmp2 > 1 OR tmp2 <-1 THEN
	//目标姿态不能实现
	InverseKinematics.IsNull :=TRUE;
	RETURN;
END_IF   

tmp4 := ACOS(tmp2) * 180 / GVL.PI;
tmp5 := MATH.Atan2(px - d6 * ax, d6 * ay - py) * 180 / GVL.PI;


//thet1的2个解
Thet1[0].thet1 := tmp5 - tmp4;
Thet1[1].thet1 := tmp5 + tmp4;

FOR i:=0 TO 1 BY 1 DO
	
	th1 := 	Thet1[i].thet1;
	sin1 := SinAg(th1);
	cos1 := CosAg(th1);
	
	//计算th6
	tmp1 := sin1*ox - cos1*oy;
	tmp2 := sin1*nx - cos1*ny;

	th6 := MATH.atan2(-tmp1,tmp2)*180 / GVL.PI;	
	sin6 := SinAg(th6);
	cos6 := CosAg(th6);
	
	Thet1[i].thet6 :=th6;
	
	
	//计算th5
	tmp1 := (sin1 * nx - cos1 * ny)*cos6-(sin1*ox-cos1*oy)*sin6;

	IF tmp1>1 OR tmp1<-1 THEN
		结束本次循环,进入下一个循环
		CONTINUE;
	END_IF 
              
    tmp2 := ASIN(tmp1) ;
    tmp3 := sin1 * ax - cos1 * ay;

	IF tmp3>1 OR tmp3<-1 THEN
		结束本次循环,进入下一个循环
		CONTINUE;
	END_IF 
            
    tmp4 := ACOS(tmp3) * 180 / Math.PI;

	IF tmp2>=0 THEN
		Thet1[i].thet5 := tmp4;
	ELSE		
	 	Thet1[i].thet5 := -tmp4;
	END_IF
              
    th5 := Thet1[i].thet5;
    sin5 := SinAg(th5);
    cos5 := CosAg(th5);

	//计算th3
  	m := d5 * ((cos1 * nx + sin1 * ny) * sin6 + (cos1 * ox + sin1 * oy) * cos6) - d6 * (cos1 * ax + sin1 * ay) + cos1 * px + sin1 * py;
    n := d5 * (nz * sin6 + oz * cos6) - d6 * az + pz - d1;

   	tmp1 := 2 * a2 * a3;
	
	IF tmp1 = 0 THEN
		tmp3 := 0;
	ELSE
		 tmp3 := (m * m + n * n - a2 * a2 - a3 * a3) / tmp1;
	END_IF
                  

    IF tmp3 > 1 OR tmp3 < -1 THEN
		//结束本次循环,进入下一个循环
		CONTINUE;
	END_IF
                  
   	tmp4 := ACOS(tmp3) * 180 / GVL.PI;

    Thet1[i].thet3r[0].thet3 := -tmp4;
    Thet1[i].thet3r[1].thet3 := tmp4;
	
	FOR j:=0 TO 1 BY 1 DO
		
		th3 := Thet1[i].thet3r[j].thet3;                  
       	cos3 := CosAg(th3);
        sin3 := SinAg(th3);
		
		//计算 th2
        tmp1 := n * (a2 + a3 * cos3) - m * a3 * sin3;
        tmp2 := m * (a2 + a3 * cos3) + n * a3 * sin3;

        th2 := (MATH.Atan2(tmp1, tmp2) * 180 / Math.PI);
                    
        Thet1[i].thet3r[j].thet2 := th2;
		
		//计算 th4
        tmp1 := (nz * cos6 - oz * sin6) * cos5 - az * sin5;
        tmp2 := ((cos1 * nx + sin1 * ny) * cos6 - (cos1 * ox + sin1 * oy) * sin6) * cos5 - (cos1 * ax + sin1 * ay) * sin5;

        tmp3 := MATH.Atan2(tmp1, tmp2) * 180 / Math.PI;

        th4 := tmp3 - th2 - th3;
        Thet1[i].thet3r[j].thet4 := th4;

	END_FOR

END_FOR


//角度超限检查并且 留下合规的解

//复位逆解结果缓存
tmpJointStru := EmptyJointStru;
tmpJointStruLen :=0;

FOR i:=0 TO 1 BY 1 DO
	FOR j:=0 TO 1 BY 1 DO
		c := LrealZooCmp(GVL.JointParamArr[1].maxAg,GVL.JointParamArr[1].minAg,Thet1[i].thet3r[j].thet2);
		IF c THEN
			c := LrealZooCmp(GVL.JointParamArr[2].maxAg,GVL.JointParamArr[2].minAg,Thet1[i].thet3r[j].thet3);
			IF c THEN
				c := LrealZooCmp(GVL.JointParamArr[3].maxAg,GVL.JointParamArr[3].minAg,Thet1[i].thet3r[j].thet4);
				IF c THEN
					c := LrealZooCmp(GVL.JointParamArr[4].maxAg,GVL.JointParamArr[4].minAg,Thet1[i].thet5);
					IF c THEN
						c := LrealZooCmp(GVL.JointParamArr[5].maxAg,GVL.JointParamArr[5].minAg,Thet1[i].thet6);
						IF c THEN
							tmpJointStru[tmpJointStruLen].J1 := Thet1[i].thet1;							
							tmpJointStru[tmpJointStruLen].J1Gap := Thet1[i].thet1 - GVL.LR_Hand_J1;
							
							tmpJointStru[tmpJointStruLen].J2 := Thet1[i].thet3r[j].thet2;							
							tmpJointStru[tmpJointStruLen].J2Gap := Thet1[i].thet3r[j].thet2 - GVL.LR_Hand_J2;
							
							tmpJointStru[tmpJointStruLen].J3 := Thet1[i].thet3r[j].thet3;							
							tmpJointStru[tmpJointStruLen].J3Gap := Thet1[i].thet3r[j].thet3 - GVL.LR_Hand_J3;
							
							tmpJointStru[tmpJointStruLen].J4 := Thet1[i].thet3r[j].thet4;						
							tmpJointStru[tmpJointStruLen].J4Gap := Thet1[i].thet3r[j].thet4 - GVL.LR_Hand_J4;
							
							tmpJointStru[tmpJointStruLen].J5 := Thet1[i].thet5;							
							tmpJointStru[tmpJointStruLen].J5Gap := Thet1[i].thet5 - GVL.LR_Hand_J5;
								
							tmpJointStru[tmpJointStruLen].J6 := Thet1[i].thet6;							
							tmpJointStru[tmpJointStruLen].J6Gap := Thet1[i].thet6 - GVL.LR_Hand_J6;
							
							tmpJointStruLen := tmpJointStruLen +1;
						END_IF
					END_IF
				END_IF
			END_IF
		END_IF
	END_FOR
END_FOR


//根据 SortType 挑选最优解并输出

IF tmpJointStruLen > 0 THEN
	
//先赋值第一个解

	tmpJs :=tmpJointStru[0];

	tmp1 := ABS(tmpJs.J1Gap)+ ABS(tmpJs.J2Gap)+ ABS(tmpJs.J3Gap)+ ABS(tmpJs.J4Gap)+ ABS(tmpJs.J5Gap)+ ABS(tmpJs.J6Gap);
	
	//正解 逆解的结果 ,获取各关节的位置
	CheckHandJointPos := DirectKinematics(tmpJs);
	
	//区域限制
	//各关节 不能低于 底面50
	IF CheckHandJointPos.J1.pz <50 OR  CheckHandJointPos.J2.pz <50 OR  CheckHandJointPos.J3.pz <50 OR  CheckHandJointPos.J4.pz <50 OR  CheckHandJointPos.J5.pz <50 OR  CheckHandJointPos.J6.pz <50 THEN
		//不符合条件,剔除
		InverseKinematics.IsNull := TRUE;
		
	ELSE
		//判断SortType
		
		IF SortType =1 THEN 
			
			IF ABS(tmpJs.J1Gap)>IptAgMax OR ABS(tmpJs.J2Gap)>IptAgMax OR ABS(tmpJs.J3Gap)>IptAgMax OR ABS(tmpJs.J4Gap)>IptAgMax OR ABS(tmpJs.J5Gap)>IptAgMax OR ABS(tmpJs.J6Gap)>IptAgMax  THEN
				//不符合条件,剔除
				InverseKinematics.IsNull := TRUE;
				
			ELSE
				//符合条件,赋值
				InverseKinematics.HandJoint := tmpJs;
				
			END_IF	
		ELSE
			//符合条件,赋值
			InverseKinematics.HandJoint := tmpJs;
		END_IF
	
	
	END_IF
	
	IF tmpJointStruLen > 1 THEN
		IF InverseKinematics.IsNull THEN
			tmp1:= 1000000;
		END_IF
		
		FOR i:=1 TO (tmpJointStruLen-1) BY 1 DO
			
			tmpJs :=tmpJointStru[i];
		
			tmp2 := ABS(tmpJs.J1Gap)+ ABS(tmpJs.J2Gap)+ ABS(tmpJs.J3Gap)+ ABS(tmpJs.J4Gap)+ ABS(tmpJs.J5Gap)+ ABS(tmpJs.J6Gap);
	
			//正解 逆解的结果 ,获取各关节的位置
			CheckHandJointPos := DirectKinematics(tmpJs);
			//区域限制
			//各关节 不能低于 底面50
			IF CheckHandJointPos.J1.pz <50 OR  CheckHandJointPos.J2.pz <50 OR  CheckHandJointPos.J3.pz <50 OR  CheckHandJointPos.J4.pz <50 OR  CheckHandJointPos.J5.pz <50 OR  CheckHandJointPos.J6.pz <50 THEN
				//不符合条件,剔除
				CONTINUE;			
			ELSE
				//判断SortType
				
				IF SortType =1 THEN 
					
					IF ABS(tmpJs.J1Gap)>IptAgMax OR ABS(tmpJs.J2Gap)>IptAgMax OR ABS(tmpJs.J3Gap)>IptAgMax OR ABS(tmpJs.J4Gap)>IptAgMax OR ABS(tmpJs.J5Gap)>IptAgMax OR ABS(tmpJs.J6Gap)>IptAgMax  THEN
						//不符合条件,剔除
						CONTINUE;						
					ELSE
						//符合条件,赋值
						InverseKinematics.IsNull := FALSE;
						
					END_IF	
				ELSE
					//符合条件,赋值
					InverseKinematics.IsNull := FALSE;
				END_IF
					
			END_IF
		
			//关节移动量最小
			IF tmp1 > tmp2 THEN
				tmp1 := tmp2;
				InverseKinematics.HandJoint := tmpJs;
			END_IF
		
		END_FOR

	END_IF

ELSE
	//tmpJointStruLen 为0 ,没有解
	InverseKinematics.IsNull := TRUE;
						
END_IF





        3.3.3主程序

//作者:AlongWu

PROGRAM PLC_PRG
VAR


Point1_R_Trig				:R_TRIG;			//point1按钮上升沿
Point2_R_Trig				:R_TRIG;			//point2按钮上升沿

Line_R_Trig					:R_TRIG;			//Line按钮上升沿
Ball_R_Trig					:R_TRIG;			//Ball按钮上升沿


HJA							:HandJointAg;
END_VAR


//按钮上升沿
Point1_R_Trig(CLK:=GVL.B_Point1Bt , Q=> );
Point2_R_Trig(CLK:=GVL.B_Point2Bt , Q=> );

Line_R_Trig(CLK:=GVL.B_LineBt , Q=> );
Ball_R_Trig(CLK:=GVL.B_BallBt , Q=> );


//point1运动触发
IF Point1_R_Trig.Q AND GVL.B_MotionAct = FALSE THEN
	
	GVL.LR_TargetPX := 100;
	GVL.LR_TargetPY := -100;
	GVL.LR_TargetPZ := 100;
	GVL.LR_TargetRX := 180;
	GVL.LR_TargetRY := 0;
	GVL.LR_TargetRZ := 0;

	GVL.MotionType := MotionType_Enum.Point;
	GVL.B_MotionAct := TRUE;
END_IF

//point2运动触发
IF Point2_R_Trig.Q AND GVL.B_MotionAct = FALSE THEN
	
	GVL.LR_TargetPX := 200;
	GVL.LR_TargetPY := -150;
	GVL.LR_TargetPZ := 150;
	GVL.LR_TargetRX := 90;
	GVL.LR_TargetRY := 90;
	GVL.LR_TargetRZ := 90;

	GVL.MotionType := MotionType_Enum.Point;
	GVL.B_MotionAct := TRUE;
END_IF



//line运动触发
IF Line_R_Trig.Q AND GVL.B_MotionAct = FALSE THEN
	
	GVL.LR_TargetPX := 100;
	GVL.LR_TargetPY := -100;
	GVL.LR_TargetPZ := 100;
	GVL.LR_TargetRX := 180;
	GVL.LR_TargetRY := 0;
	GVL.LR_TargetRZ := 0;


	GVL.MotionType := MotionType_Enum.Line;
	GVL.B_MotionAct := TRUE;
END_IF

//ball运动触发
IF Ball_R_Trig.Q AND GVL.B_MotionAct = FALSE THEN
	
		GVL.LR_Ball_R :=10;
		GVL.LR_Ball_Cx :=100;
		GVL.LR_Ball_Cy :=-200;
		GVL.LR_Ball_Cz :=200;


	GVL.MotionType := MotionType_Enum.Ball;
	GVL.B_MotionAct := TRUE;
END_IF


//计算所有关节的坐标
HJA.J1 := GVL.LR_Hand_J1;
HJA.J2 := GVL.LR_Hand_J2;
HJA.J3 := GVL.LR_Hand_J3;
HJA.J4 := GVL.LR_Hand_J4;
HJA.J5 := GVL.LR_Hand_J5;
HJA.J6 := GVL.LR_Hand_J6;

//正解,更新各关节的坐标
GVL.CurrentJointPos := DirectKinematics(HJointAg:= HJA);


        3.3.4运动主程序

//作者:AlongWu

PROGRAM Motion_PRG
VAR
	
i 	:INT;
Act_R_Trig					:R_TRIG;			//Act上升沿

END_VAR


Act_R_Trig(CLK:=GVL.B_MotionAct , Q=> );			//Act上升沿

IF Act_R_Trig.Q THEN
	//Act 上升沿,复位步骤和状态值
	
	GVL.I_PointM_Step:=0;
	GVL.I_LineM_Step:=0;
	GVL.I_BallM_Step:=0;
	GVL.B_InverseKineFalure :=FALSE;
END_IF


IF GVL.B_MotionAct THEN	
	CASE GVL.MotionType OF
		MotionType_Enum.Point:
		//启用 PointMotion的PRG
					PointMotion_PRG();
		MotionType_Enum.Line:
		//启用 LineMotion的PRG
					LineMotion_PRG();
		MotionType_Enum.Ball:
		//启用 BallMotion的PRG
					BallMotion_PRG();
		ELSE
			//motion结束
			GVL.B_MotionAct :=FALSE;
				
	END_CASE
END_IF

         3.3.4.1直线子运动程序

//作者:AlongWu

PROGRAM LineMotion_PRG
VAR
	


tmpPs				:PosStru;			//目标坐标数据元
tmpHandJR			:HandJointResult;	//逆解结果缓存

HandJR				:HandJointResult;	//逆解结果

LR_MoveLen			:LREAL;				//机械手末端移动距离 mm
LR_MovingTime		:LREAL;				//运动时长 s


tmp1				:LREAL;
tmp2				:LREAL;
tmp3				:LREAL;
tmp4				:LREAL;

ALen				:LREAL;				//加速距离长 mm
ATm					:LREAL;				//加速时长 s
SpdLowPos			:LREAL;				//减速位置 mm

Xg					:LREAL;
Yg					:LREAL;
Zg					:LREAL;

Xk					:LREAL;
Yk					:LREAL;
Zk					:LREAL;


Xb					:LREAL;
Yb					:LREAL;
Zb					:LREAL;




//6关节的 一元一次方程的 b 和 k。
j1b					:LREAL;
j1k					:LREAL;
j2b					:LREAL;
j2k					:LREAL;
j3b					:LREAL;
j3k					:LREAL;
j4b					:LREAL;
j4k					:LREAL;
j5b					:LREAL;
j5k					:LREAL;
j6b					:LREAL;
j6k					:LREAL;

slen				:LREAL;
v					:LREAL;
t					:LREAL;

END_VAR





CASE GVL.I_LineM_Step OF
	0:
		
		//坐标赋值
		tmpPs.px :=GVL.LR_TargetPX;
		tmpPs.py :=GVL.LR_TargetPY;
		tmpPs.pz :=GVL.LR_TargetPZ;
		tmpPs.rx :=GVL.LR_TargetRX;
		tmpPs.ry :=GVL.LR_TargetRY;
		tmpPs.rz :=GVL.LR_TargetRZ;
	
		GVL.I_LineM_Step := GVL.I_LineM_Step +1;

	1:
		//检查目标坐标是否有解
		//逆解
		tmpHandJR :=InverseKinematics(KeyPosStru:=tmpPs , SortType:=0 );
	
		IF tmpHandJR.IsNull THEN
			GVL.B_InverseKineFalure :=TRUE;
			GVL.I_LineM_Step := 99;			
			RETURN;
		ELSE
			GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;
		
		END_IF
	2:
		//把角度摆好,再直线插补
		//坐标赋值
		tmpPs.px :=GVL.CurrentJointPos.J6.px;
		tmpPs.py :=GVL.CurrentJointPos.J6.py;
		tmpPs.pz :=GVL.CurrentJointPos.J6.pz;
		tmpPs.rx :=GVL.LR_TargetRX;
		tmpPs.ry :=GVL.LR_TargetRY;
		tmpPs.rz :=GVL.LR_TargetRZ;
	
		GVL.I_LineM_Step := GVL.I_LineM_Step +1;

	3:
			//逆解
		HandJR :=InverseKinematics(KeyPosStru:=tmpPs , SortType:=0 );
	
		IF HandJR.IsNull THEN
			GVL.B_InverseKineFalure :=TRUE;
			GVL.I_LineM_Step := 99;			
		ELSE
			GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;
		END_IF	
	4:
		//按固定时长,机械手末端先移动至目标角度。
		LR_MovingTime := 5;
		
	 	j1b := GVL.LR_Hand_J1;
        j2b := GVL.LR_Hand_J2;
        j3b := GVL.LR_Hand_J3;
        j4b := GVL.LR_Hand_J4;
        j5b := GVL.LR_Hand_J5;
        j6b := GVL.LR_Hand_J6;

        j1k := HandJR.HandJoint.J1Gap / LR_MovingTime;
        j2k := HandJR.HandJoint.J2Gap / LR_MovingTime;
        j3k := HandJR.HandJoint.J3Gap / LR_MovingTime;
        j4k := HandJR.HandJoint.J4Gap / LR_MovingTime;
        j5k := HandJR.HandJoint.J5Gap / LR_MovingTime;
        j6k := HandJR.HandJoint.J6Gap / LR_MovingTime;
		
		t :=0;
		GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;
	5:
		
		GVL.LR_Hand_J1 := j1b + j1k * t;	
		GVL.LR_Hand_J2 := j2b + j2k * t;
		GVL.LR_Hand_J3 := j3b + j3k * t;
		GVL.LR_Hand_J4 := j4b + j4k * t;
		GVL.LR_Hand_J5 := j5b + j5k * t;
		GVL.LR_Hand_J6 := j6b + j6k * t;
		t := t+ 0.05;
		IF t > LR_MovingTime THEN
			GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;
		END_IF
	6:
		GVL.LR_Hand_J1:=HandJR.HandJoint.J1;
        GVL.LR_Hand_J2:=HandJR.HandJoint.J2;
        GVL.LR_Hand_J3:=HandJR.HandJoint.J3;
        GVL.LR_Hand_J4:=HandJR.HandJoint.J4;
        GVL.LR_Hand_J5:=HandJR.HandJoint.J5;
        GVL.LR_Hand_J6:=HandJR.HandJoint.J6;
		
		GVL.I_LineM_Step :=10;

	10:
		
		IF GVL.LR_MovingSpd = 0 OR GVL.LR_MovingAsp=0 THEN
			GVL.I_LineM_Step := 99;	
			RETURN;
		END_IF

		//计算当前坐标到目标坐标的距离和时长
		tmp1 :=GVL.LR_TargetPX - GVL.CurrentJointPos.J6.px;
		tmp2 :=GVL.LR_TargetPY - GVL.CurrentJointPos.J6.py;
		tmp3 :=GVL.LR_TargetPZ - GVL.CurrentJointPos.J6.pz;
	
		LR_MoveLen := SQRT(tmp1*tmp1+tmp2*tmp2+tmp3*tmp3);	
		
		ATm := GVL.LR_MovingSpd / GVL.LR_MovingAsp;
		
		ALen := GVL.LR_MovingAsp * ATm *ATm;
		
		IF ALen > LR_MoveLen THEN
			//未达到目标数独,已开始减速
			LR_MovingTime := SQRT(LR_MoveLen/GVL.LR_MovingAsp);
			SpdLowPos := LR_MoveLen * 0.5;
		ELSE
			LR_MovingTime :=ATm*2 + (LR_MoveLen-ALen)/GVL.LR_MovingSpd;
			SpdLowPos := LR_MoveLen - ALen*0.5;
		END_IF
			
		IF LR_MovingTime = 0 THEN
			GVL.I_LineM_Step := 99;	
			RETURN;
		END_IF
		
		//计算位移插补的一元一次方程参数, k 和 b。
		Xg := GVL.LR_TargetPX -GVL.CurrentJointPos.J6.px;
		Yg := GVL.LR_TargetPY -GVL.CurrentJointPos.J6.py;
		Zg := GVL.LR_TargetPZ -GVL.CurrentJointPos.J6.pz;
		
		
		Xk := Xg / LR_MoveLen;
		Yk := Yg / LR_MoveLen;
		Zk := Zg / LR_MoveLen;
		
		Xb := GVL.CurrentJointPos.J6.px;
		Yb := GVL.CurrentJointPos.J6.py;
		Zb := GVL.CurrentJointPos.J6.pz;
		
		slen :=0;
		v :=0;
		GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;
	11:
		IF slen < LR_MoveLen THEN
			IF slen < SpdLowPos THEN
				IF v < GVL.LR_MovingSpd THEN
					//加速阶段
					v := v + GVL.LR_MovingAsp*0.05;
					slen := slen + v*0.05;
				ELSE
					//匀速阶段
					slen := slen + v*0.05;
				END_IF
			ELSE
				//减速阶段
				IF v<0.1 THEN
					v :=0.1;
				ELSE
					v := v - GVL.LR_MovingAsp*0.05;
				END_IF
				slen := slen + v*0.05;
			END_IF			
			
			tmpPs.px :=Xb + Xk*slen;
			tmpPs.py :=Yb + Yk*slen;
			tmpPs.pz :=Zb + Zk*slen;
		
			//逆解
			HandJR :=InverseKinematics(KeyPosStru:=tmpPs , SortType:=0 );
		
			IF HandJR.IsNull THEN
				GVL.B_InverseKineFalure :=TRUE;
				GVL.I_LineM_Step := 99;				
				RETURN;
			END_IF
			
			GVL.LR_Hand_J1:=HandJR.HandJoint.J1;
			GVL.LR_Hand_J2:=HandJR.HandJoint.J2;
			GVL.LR_Hand_J3:=HandJR.HandJoint.J3;
			GVL.LR_Hand_J4:=HandJR.HandJoint.J4;
			GVL.LR_Hand_J5:=HandJR.HandJoint.J5;
			GVL.LR_Hand_J6:=HandJR.HandJoint.J6;
							
			
		ELSE			
			GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;					
		END_IF
	
	12:
		GVL.LR_Hand_J1:=tmpHandJR.HandJoint.J1;
		GVL.LR_Hand_J2:=tmpHandJR.HandJoint.J2;
		GVL.LR_Hand_J3:=tmpHandJR.HandJoint.J3;
		GVL.LR_Hand_J4:=tmpHandJR.HandJoint.J4;
		GVL.LR_Hand_J5:=tmpHandJR.HandJoint.J5;
		GVL.LR_Hand_J6:=tmpHandJR.HandJoint.J6;
			
		GVL.I_LineM_Step :=GVL.I_LineM_Step + 1;					
ELSE
		GVL.MotionType := 3;
END_CASE


        3.3.4.2阿基米德旋线+球面子运动程序

//作者:AlongWu

PROGRAM BallMotion_PRG
VAR
	
tmpPs				:PosStru;			//目标坐标数据元
tmpHandJR			:HandJointResult;	//逆解结果缓存

HandJR				:HandJointResult;	//逆解结果

LR_MoveLen			:LREAL;				//机械手末端移动距离 mm
LR_MovingTime		:LREAL;				//运动时长 s



BallR				:LREAL;
BallCx				:LREAL;
BallCy				:LREAL;
BallCz				:LREAL;



//6关节的 一元一次方程的 b 和 k。
j1b					:LREAL;
j1k					:LREAL;
j2b					:LREAL;
j2k					:LREAL;
j3b					:LREAL;
j3k					:LREAL;
j4b					:LREAL;
j4k					:LREAL;
j5b					:LREAL;
j5k					:LREAL;
j6b					:LREAL;
j6k					:LREAL;

t					:LREAL;
Arch_B				:LREAL;				//螺旋线间距系数
tmpR				:LREAL;				//当前直径长度 mm
ag					:LREAL;				//旋线的极角度
Z					:LREAL;				//当前Z位置 mm
v3d					:Vector3d;			//3维向量
eg					:EulerAngleStru;	//欧拉角组




END_VAR




  (*
    球的方程  
      x = x0+R*sinA*cosB
      y = y0+R*sinA*sinB
      z = z0+R*cosA
  
    阿基米德螺线方程
     
      r = a+bB;
   
    螺线距离 = 2* PI * b;
      
    本动作的目标是 用阿基米德螺线包住球。 同时 每个点的J6的z方向 指向 球心。
	
	
	 测试过程,运动过程,会出现 多个 非连续姿态(下一个姿态的1个或多个关节,的跨度过大。)
      
     测试用例:R=20, CX=100.CY=-200,CZ=100

  *)




CASE GVL.I_BallM_Step OF
	0:
		//参数赋值
		BallR :=GVL.LR_Ball_R;
		BallCx :=GVL.LR_Ball_Cx;
		BallCy :=GVL.LR_Ball_Cy;
		BallCz :=GVL.LR_Ball_Cz;	
		GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;		
	1:
		//移动至球顶部,并调整姿态
		//坐标赋值
		tmpPs.px :=BallCx;
		tmpPs.py :=BallCy;
		tmpPs.pz :=BallCz + BallR;
		tmpPs.rx :=0;
		tmpPs.ry :=-180;
		tmpPs.rz :=-180;
	
		GVL.I_BallM_Step := GVL.I_BallM_Step +1;

	2:
			//逆解
		HandJR :=InverseKinematics(KeyPosStru:=tmpPs , SortType:=0 );
	
		IF HandJR.IsNull THEN
			GVL.B_InverseKineFalure :=TRUE;
			GVL.I_BallM_Step := 99;			
			RETURN;
		ELSE
			GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;
		END_IF	
	3:
		//按固定时长,机械手末端先移动至目标角度。
		LR_MovingTime := 5;
		
	 	j1b := GVL.LR_Hand_J1;
        j2b := GVL.LR_Hand_J2;
        j3b := GVL.LR_Hand_J3;
        j4b := GVL.LR_Hand_J4;
        j5b := GVL.LR_Hand_J5;
        j6b := GVL.LR_Hand_J6;

        j1k := HandJR.HandJoint.J1Gap / LR_MovingTime;
        j2k := HandJR.HandJoint.J2Gap / LR_MovingTime;
        j3k := HandJR.HandJoint.J3Gap / LR_MovingTime;
        j4k := HandJR.HandJoint.J4Gap / LR_MovingTime;
        j5k := HandJR.HandJoint.J5Gap / LR_MovingTime;
        j6k := HandJR.HandJoint.J6Gap / LR_MovingTime;
		
		t :=0;
		GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;
	4:
		
		GVL.LR_Hand_J1 := j1b + j1k * t;	
		GVL.LR_Hand_J2 := j2b + j2k * t;
		GVL.LR_Hand_J3 := j3b + j3k * t;
		GVL.LR_Hand_J4 := j4b + j4k * t;
		GVL.LR_Hand_J5 := j5b + j5k * t;
		GVL.LR_Hand_J6 := j6b + j6k * t;
		t := t+ 0.05;
		IF t > LR_MovingTime THEN
			GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;
		END_IF
	5:
		GVL.LR_Hand_J1:=HandJR.HandJoint.J1;
        GVL.LR_Hand_J2:=HandJR.HandJoint.J2;
        GVL.LR_Hand_J3:=HandJR.HandJoint.J3;
        GVL.LR_Hand_J4:=HandJR.HandJoint.J4;
        GVL.LR_Hand_J5:=HandJR.HandJoint.J5;
        GVL.LR_Hand_J6:=HandJR.HandJoint.J6;
		
		GVL.I_BallM_Step :=10;

	10:
		Arch_B :=(BallR / 360) /3;				//螺旋线间距系数,3圈直径为目标直径
		tmpR :=0;
		ag :=0.1;
		GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;
	11:
		IF tmpR < BallR THEN
			
			// 计算旋线的半径
			tmpR := Arch_B * ag;
			
			// 计算当前球面的坐标
			tmpPs.px :=BallCx + tmpR*CosAg(ag);
			tmpPs.py :=BallCy + tmpR*SinAg(ag);
			Z := BallCz + BallR - tmpR;
			tmpPs.pz := Z;
			
			//计算当前的机械手末端指向的向量, 从球面指向球心
			v3d.px := BallCx - tmpPs.px;				
			v3d.py := BallCy - tmpPs.py;				
			v3d.pz := BallCz - tmpPs.pz;				
			
			//计算当前的机械手末端指向的欧拉角
			eg := v3d2Euler(v3d);

			tmpPs.rx := eg.rx;
			tmpPs.ry := eg.ry;
			tmpPs.rz := eg.rz;
			
			

			//逆解各关节角度
			HandJR :=InverseKinematics(KeyPosStru:=tmpPs , SortType:=1 );
	
			IF HandJR.IsNull THEN
				GVL.B_InverseKineFalure :=TRUE;
				GVL.I_BallM_Step := 99;			
				RETURN;
			END_IF	
			
			GVL.LR_Hand_J1:=HandJR.HandJoint.J1;
			GVL.LR_Hand_J2:=HandJR.HandJoint.J2;
			GVL.LR_Hand_J3:=HandJR.HandJoint.J3;
			GVL.LR_Hand_J4:=HandJR.HandJoint.J4;
			GVL.LR_Hand_J5:=HandJR.HandJoint.J5;
			GVL.LR_Hand_J6:=HandJR.HandJoint.J6;
			
			//插补,增加角度
			ag :=ag + 0.3;
		ELSE
			GVL.I_BallM_Step :=GVL.I_BallM_Step + 1;	
		END_IF
	
	
ELSE
	GVL.MotionType := 3;
END_CASE

三、总结

        整个程序比较简单,没有用到特殊的函数或者功能块,只用了四则运算来裸写相关的计算。轴控制就不贴代码了,也没啥特点。只调用了MC_Power和SMC_ControlAxisByPos。由于机械手关节的电机控制是位置控制模式,只要把相应位置数值给到fSetPosition,轴即会根据速度参数自己转到位。

物联沃分享整理
物联沃-IOTWORD物联网 » 纯手工Codesys的6轴机械手控制程序记录

发表评论