{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![Titlepic](Images/xyzabc_trsrn_machine.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction\n",
"\n",
"\n",
"This paper describes how to derive the kinematic model for a 6-axis machine tool in an XYZABC configuration with Tool side A,B rotation of the spindle and work side C (table) rotation . In this example the B axis is a primary (rotating around the Y) and the A axis is the secondary (45° nutating) rotary axis. The primary being independent of the secondary axis. The model presented here will be named 'XYZABC-TRSRN'.\n",
"\n",
"The method used here will be a step by step approach. Starting with a working kinematic model for a single rotary axis all the required elements will be added to build a complete kinematic model for the machine.\n",
"\n",
"The final model includes tool-length compensation, compensation for mechanical offsets between the two rotational axis A and B and compensation for setups where the machine reference point is not located in the rotation-point of the C rotary table. \n",
"\n",
"In this document only basic mathematical functions are used so the kinematic models derived can be used directly in the 'userkins.comp' template file provided with the LinuxCNC installation. All calculations can be done whithout the use of any computer algebra systems, however the use of computer assistance like 'sage' will make the process of matrix multiplication much less error prone.\n",
"\n",
"Note that there are other and potentially more computationally efficient ways to build custom kinematics using built in libraries like 'posemath'. Posemath provides many functions for efficient matrix manipulation and also offers functions for the use of quaternions. However the use of such a library would require a more indepth understanding of the mathematical theory that is beyond the scope of this presentation. Furthermore importing a library like 'posemath' into the 'userkins.comp' template would require substantially more programming skills than using the method applied in this paper. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import display, Image\n",
"\n",
"# only used to display equations out of code blocks\n",
"from IPython.display import Math, display\n",
"\n",
"# joint position vector P \n",
"var('Px','Py','Pz')\n",
"P_=matrix([[Px],\n",
" [Py],\n",
" [Pz],\n",
" [1]])\n",
"\n",
"# cartesian position vector Q\n",
"var('Qx','Qy','Qz')\n",
"Q_=matrix([[Qx],\n",
" [Qy],\n",
" [Qz],\n",
" [1]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A custom kinematic model in LinuxCNC is used to calculate cartesian coordinates from given machine joint positions (forward kinematics) and also to calculate the required machine joint positions to reach a given coordinate position (inverse kinematics). In the following description we will use vectors as mathematical representations of the two positions: \n",
"\n",
"\n",
"\\begin{equation}\n",
"Q ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" Qx \\\\\n",
" Qy \\\\\n",
" Qz \\\\\n",
" 1\n",
"\\end{array}\\right)\n",
"~\n",
"Cartesian~position\n",
"~~~~~~~~~~~~\n",
"P ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" Px \\\\\n",
" Py \\\\\n",
" Pz \\\\\n",
" 1\n",
"\\end{array}\\right)\n",
"~\n",
"Joint~position\n",
"\\end{equation}\n",
"\n",
"Note that the fourth row is added to be able to multiply the vectors with a 4x4 transformation matrix. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# TCP Kinematic model\n",
"For the tool to follow a point on the work piece we need a model that calculates where a given position on the work piece moves to when the rotary joints are rotated. In our example configuration the work piece will be mounted on the C rotary table and it is therefore here where we start to build our forward kinematic model.
\n",
"Note that in matrix multiplication the order is important that is $A\\cdot B$ is generally not equal to $B \\cdot A$. \n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"## Table Rotary C\n",
"### Forward transformation\n",
"\n",
"We start with the basic rotation around the C axis. In this case our forward transformation matrix $^QA_P$ is equal to a rotation around the z-axis:\n",
"\n",
"\\begin{equation}\n",
" ^QA_P=~ R_c \n",
"\\end{equation}\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle R_a =~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cc} & -\\mathit{Sc} & 0 & 0 \\\\\n",
"\\mathit{Sc} & \\mathit{Cc} & 0 & 0 \\\\\n",
"0 & 0 & 1 & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# define rotation matrix for axis C (ie rotation around z-axis)\n",
"var('Cc','Sc')\n",
"Rc=matrix([[ Cc, -Sc, 0, 0],\n",
" [ Sc, Cc, 0, 0],\n",
" [ 0 , 0 , 1, 0],\n",
" [ 0, 0 , 0, 1]])\n",
"display(Math(rf'R_a =~'+latex(Rc)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"with $~~Sc = sin(\\theta_c),~~~ Cc = cos(\\theta_c)$\n",
"and $\\theta_c$ being the angle of rotation of joint C"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To derive the coordinate position $Q(Qx,Qy,Qz)$ we now need to multiply the joint position vector $P(Px,Py,Pz)$ with our forward transformation matrix $^QA_P$.
\n",
"Note that the input values $P$ to our model need to be on the right hand side of the matrix multiplication.\n",
"\n",
"$$ Q=~^QA_P \\cdot P $$"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle Q =~ \\left(\\begin{array}{r}\n",
"\\mathit{Qx} \\\\\n",
"\\mathit{Qy} \\\\\n",
"\\mathit{Qz} \\\\\n",
"1\n",
"\\end{array}\\right) =~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cc} & -\\mathit{Sc} & 0 & 0 \\\\\n",
"\\mathit{Sc} & \\mathit{Cc} & 0 & 0 \\\\\n",
"0 & 0 & 1 & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right) \\cdot \\left(\\begin{array}{r}\n",
"\\mathit{Px} \\\\\n",
"\\mathit{Py} \\\\\n",
"\\mathit{Pz} \\\\\n",
"1\n",
"\\end{array}\\right) =~ \\left(\\begin{array}{r}\n",
"\\mathit{Cc} \\mathit{Px} - \\mathit{Py} \\mathit{Sc} \\\\\n",
"\\mathit{Cc} \\mathit{Py} + \\mathit{Px} \\mathit{Sc} \\\\\n",
"\\mathit{Pz} \\\\\n",
"1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# calculate forward kinematic\n",
"Q_out=Rc*P_\n",
"display(Math(rf'Q =~'+latex(Q_)+rf'=~'+latex(Rc)+rf'\\cdot'+latex(P_)+rf'=~'+latex(Q_out)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-------\n",
"This shows the section of the TCP forward kinematics calculation in the file 'xyzab_tdr_kins.comp'. \n",
"Note that P(px,py,pz) is equal to the joint position (j[0],j[1],j[2],) while $Q(Qx,Qy,Qz)$ is output to the coordinate position (pos->tran.x, pos->tran.y, pos->tran.z).\n",
"The values 'cc', 'sc' are calculated and stored in the respective variables earlier in the component file."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
" case 1: // ========================= TCP kinematics FORWARD\n",
" px = j[0];\n",
" py = j[1];\n",
" pz = j[2];\n",
"\n",
" pos->tran.x = cc*px - sc*py;\n",
" pos->tran.y = cc*py + sc*px;\n",
" pos->tran.z = pz;\n",
"\n",
" pos->a = j[3];\n",
" pos->b = j[4];\n",
" pos->c = j[5];\n",
" \n",
" break;\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Inverse transformation\n",
"\n",
"To calculate the joint position $P$ from given coordinate positions $Q$ we need to 'unrotate' joint C. Mathematically this means we need to transpose the rotation part in our transformation matrix.\n",
"\n",
"\\begin{equation}\n",
" ^PA_Q=~ R_c^T \n",
"\\end{equation}\n",
"\n",
"To derive the joint position $P$ we then multiply the coordinate position vector $Q$ from the right:\n",
"\n",
"\\begin{equation}\n",
" P=~^PA_Q \\cdot Q\n",
"\\end{equation}"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle P =~ \\left(\\begin{array}{r}\n",
"\\mathit{Px} \\\\\n",
"\\mathit{Py} \\\\\n",
"\\mathit{Pz} \\\\\n",
"1\n",
"\\end{array}\\right) =~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cc} & \\mathit{Sc} & 0 & 0 \\\\\n",
"-\\mathit{Sc} & \\mathit{Cc} & 0 & 0 \\\\\n",
"0 & 0 & 1 & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right) \\cdot \\left(\\begin{array}{r}\n",
"\\mathit{Qx} \\\\\n",
"\\mathit{Qy} \\\\\n",
"\\mathit{Qz} \\\\\n",
"1\n",
"\\end{array}\\right) =~ \\left(\\begin{array}{r}\n",
"\\mathit{Cc} \\mathit{Qx} + \\mathit{Qy} \\mathit{Sc} \\\\\n",
"\\mathit{Cc} \\mathit{Qy} - \\mathit{Qx} \\mathit{Sc} \\\\\n",
"\\mathit{Qz} \\\\\n",
"1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# calculate inverse kinematic\n",
"P_out=Rc.transpose()*Q_\n",
"display(Math(rf'P =~'+latex(P_)+rf'=~'+latex(Rc.transpose())+rf'\\cdot'+latex(Q_)+rf'=~'+latex(P_out)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-------\n",
"This shows the section of the TCP inverse kinematics calculation in the file 'xyzab_tdr_kins.comp'. \n",
"Note that $Q(qx,qy,qz)$ is equal to the coordinate position (pos->tran.x, pos->tran.y, pos->tran.z) while $P(Px,Py,Pz)$ is output to the joint position (j[0],j[1],j[2]).\n",
"The values 'cc', 'sc' are calculated and stored in the respective variables earlier in the component file."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 1: // ========================= TCP kinematics INVERSE \n",
" qx = pos->tran.x;\n",
" qy = pos->tran.y;\n",
" qz = pos->tran.z;\n",
"\n",
" j[0] = cc*qx + sc*qy;\n",
"\n",
" j[1] = cc*qy - sc*qx;\n",
"\n",
" j[2] = qz;\n",
"\n",
" j[3] = pos->a;\n",
" j[4] = pos->b;\n",
" j[5] = pos->c;\n",
" \n",
" break;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Testing in LinuxCNC shows a working TCP kinematic for rotations around C"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"\n",
"#display(Image(filename=\"Pictures/Ra_gui.png\",\n",
"# height=300, width=300))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## Rotary B and C\n",
"### Forward transformation\n",
"\n",
"Because the linear (x,y,z)-axis motion or tool-translation $P(Px, Py, Pz)$ happens between the table rotation C and the spindle rotation B we need to insert that tool-translation as a tranformation matrix in between the rotation C and the rotation B."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To add the B rotation we expand $~^QA_P$ by multiplying first the tool-translation $T_p$ from the right and then the rotation matrix $R_b$ also from the right: \n",
"\n",
"\\begin{equation}\n",
" ^QA_P=~ R_c \\cdot T_p \\cdot R_b\n",
"\\end{equation}\n",
"\n",
"Note how the transformation matrix $~^QA_P$ is constructed from left to right. The first operation is on the left and the last operation is on the right as we work our way from the work side to the spindle side of the kinematic chain."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle T_p =~ \\left(\\begin{array}{rrrr}\n",
"1 & 0 & 0 & \\mathit{Px} \\\\\n",
"0 & 1 & 0 & \\mathit{Py} \\\\\n",
"0 & 0 & 1 & \\mathit{Pz} \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right) ~~ R_b =~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cb} & 0 & \\mathit{Sb} & 0 \\\\\n",
"0 & 1 & 0 & 0 \\\\\n",
"-\\mathit{Sb} & 0 & \\mathit{Cb} & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# define joint position matrix\n",
"var('Px','Py','Pz')\n",
"Tp=matrix([[ 1, 0, 0, Px ],\n",
" [ 0, 1, 0, Py],\n",
" [ 0, 0, 1, Pz],\n",
" [ 0, 0, 0, 1 ]])\n",
"\n",
"#define rotation matrix for joint B\n",
"var('Cb','Sb')\n",
"Rb=matrix([[Cb, 0, Sb, 0],\n",
" [ 0, 1, 0, 0],\n",
" [-Sb, 0, Cb, 0],\n",
" [ 0, 0, 0, 1]])\n",
"\n",
"display(Math(rf'T_p =~'+latex(Tp)+'~~'+rf'R_b =~'+latex(Rb)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"with $~~Sb = sin(\\theta_b),~~~ Cb = cos(\\theta_b)$\n",
"and $\\theta_b$ being the angle of rotation of joint B "
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/latex": [
"$\\displaystyle ^QA_P =~~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cc} & -\\mathit{Sc} & 0 & 0 \\\\\n",
"\\mathit{Sc} & \\mathit{Cc} & 0 & 0 \\\\\n",
"0 & 0 & 1 & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right) \\cdot \\left(\\begin{array}{rrrr}\n",
"1 & 0 & 0 & \\mathit{Px} \\\\\n",
"0 & 1 & 0 & \\mathit{Py} \\\\\n",
"0 & 0 & 1 & \\mathit{Pz} \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right) \\cdot \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cb} & 0 & \\mathit{Sb} & 0 \\\\\n",
"0 & 1 & 0 & 0 \\\\\n",
"-\\mathit{Sb} & 0 & \\mathit{Cb} & 0 \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/latex": [
"$\\displaystyle ^QA_P =~~ \\left(\\begin{array}{rrrr}\n",
"\\mathit{Cb} \\mathit{Cc} & -\\mathit{Sc} & \\mathit{Cc} \\mathit{Sb} & \\mathit{Cc} \\mathit{Px} - \\mathit{Py} \\mathit{Sc} \\\\\n",
"\\mathit{Cb} \\mathit{Sc} & \\mathit{Cc} & \\mathit{Sb} \\mathit{Sc} & \\mathit{Cc} \\mathit{Py} + \\mathit{Px} \\mathit{Sc} \\\\\n",
"-\\mathit{Sb} & 0 & \\mathit{Cb} & \\mathit{Pz} \\\\\n",
"0 & 0 & 0 & 1\n",
"\\end{array}\\right)$"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# calculate the forward transformation matrix\n",
"qAp=Rc*Tp*Rb\n",
"display(Math(rf'^QA_P =~~'+latex(Rc)+rf'\\cdot'+latex(Tp)+rf'\\cdot'+latex(Rb)))\n",
"display(Math(rf'^QA_P =~~'+latex(qAp)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that the tool-position (ie the vector in the 4th column of our transformation matrix $^QA_P$) does not contain the B-rotation in any form. Only the tool-orientation (ie columns 1,2 and 3) is influenced by the addition of the rotation B. This may seem wrong but we have to remember that after the B-rotation ($Rb$) we have not yet built any offset (tool-length or pivot-length) into our model (ie there is no translation matrix on the right side of $Rb$). Hence for now a rotation around B simply means that a tool of lenght zero (ie a point) rotates with joint B and does not change it's position. So in the current form it is really not very interesting to test the effect of the added rotation around B so we will move on along the kinematic chain and add the geometric offsets ($D_x,D_z$) , the rotation A ($R_a$) and the pivot-lengths ($Ly,Lz$) to arrive at the spindle nose."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A closer look at the spindle rotary assembly\n",
"\n",
"To get a better understanding of these offsets in the spindle rotary assembly of our example machine we need to have a look at how the rotary axes A and B are arranged in the spindle body.\n",
"This shows our spindle assembly in its home position ($\\theta_a =0$, $\\theta_b=0$). To get a clearer idea of the situation a tool (purple cylinder) is shown protruding from the spindle nose.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 300,
"width": 300
}
},
"output_type": "display_data"
}
],
"source": [
"display(Image(filename=\"Images/spindle_head_outside.png\",\n",
" height=300, width=300))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pivot-point and pivot-length of the spindle head\n",
"Because the rotation axes A,B and offsets in question are all hidden inside the spindle body we simplify the model to the relevant kinematic components. The while cross with the arrow pointing up shows the intersection of the rotary axes A and B (yellow sphere).\n",
"This point will be refered to as the 'pivot-point' of the spindle rotary assembly.\n",
"The rotary axis B (yellow) extends horizontally from the pivot-point in the machine-y direction.\n",
"The slanted rotary axis A (yellow) extends from the pivot-point to the spindle nose where the tool (purple) attaches.\n",
"The 'y-pivot-length' is the distance in the y-direction between the pivot-point and the spindle nose and shown in green.\n",
"The 'z-pivot-length' is the distance in the z-direction between the pivot-point and the spindle nose and shown in blue.\n",
"Note that in this image there is no geometric offset between the two rotary axes and they intersect at the pivot-point. \n",
"
\n",
"We define the values for the offsets in the example image as *y-pivot = 200* and *z-pivot = 200*. In our kinematic model this represents the situation where, starting from the spindle nose, we need to travel 200 in the positive z-direction and 200 in the positive y-direction to reach the pivot-point.\n",
"
\n",
"Note that the direction of travel when defining these offsets is arbitrary so in our case the offset situation in the image could also be defined as -200 in y and -200 in z. However once the definition is made we must keep it throughout the entire process of building the kinematic model."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 300,
"width": 300
}
},
"output_type": "display_data"
}
],
"source": [
"display(Image(filename=\"Images/spindle_head_inside.png\",\n",
" height=300, width=300))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To be able to add these (y,z)-pivot values to our kinematic model we need to create a transformation matrix that has the effect of a translation. \n",
"Note that the direction chosen for these offsets is important here as our translation is basically a vector and we have chosen that this 'pivot-length' vector points from the spindle nose to the pivot-point. Since we are building the forward kinematic model we are travelling against this vector and thus its components ($L_y,L_z$) need to be entered in the negative."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define translation matrix for the pivot lengths\n",
"var('Ly','Lz')\n",
"Tl=matrix([[ 1, 0, 0, 0 ],\n",
" [ 0, 1, 0, -Ly],\n",
" [ 0, 0, 1, -Lz],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T_l =~'+latex(Tl)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Offsets in the spindle rotary assembly\n",
"The rotational axes of a rotary assembly like the A/B spindle type discussed here will always have an intended or unintented offset from one rotational axis to the other. In our case this is an offset in the x direction (red indicator) and in the z direction (light blue indicator).
\n",
"We define the values for the offsets in the example image as *x-offset = -20* and *z-offset = 40*. In our kinematic model this represents the situation where, starting from the rotary A axis, we need to travel 40 in the positive z-direction and 20 in the negative x-direction to reach the pivot-point.\n",
"\n",
"Note that the direction of travel when defining these offsets is arbitrary so in our case the offset situation in the image could also be defined as +20 in x and -40 in z. However once the definition is made we must keep it throughout the entire process of building the kinematic model."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 300,
"width": 300
}
},
"output_type": "display_data"
}
],
"source": [
"display(Image(filename=\"Images/spindle_head_dxdz.png\",\n",
" height=300, width=300))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Also note that it might be tempting to simplify things by just adding the *z-offset* (light blue) to the *z-pivot* (dark blue). However if we consider the situation with a rotation in A then it's clear that the two values need to be handled sparately in our kinematic model. "
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {
"image/png": {
"height": 300,
"width": 300
}
},
"output_type": "display_data"
}
],
"source": [
"\n",
"display(Image(filename=\"Images/spindle_head_dxdz_rot.png\",\n",
" height=300, width=300))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To be able to add these (x,z)-offset values to our kinematic model we need to create a transformation matrix that has the effect of a translation. \n",
"Note that the direction chosen for these offsets is important here as our translation is basically a vector and we have chosen that this 'offset' vector points from the A rotation axis to the pivot-point. Since we are building the forward kinematic model we are travelling against this vector and thus its components ($D_x,D_z$) need to be entered in the negative."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define translation matrix for the pivot lengths\n",
"var('Dx','Dz')\n",
"# offsets in A,B axis\n",
"Td=matrix([[ 1, 0, 0, -Dx],\n",
" [ 0, 1, 0, 0 ],\n",
" [ 0, 0, 1, -Dz],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T_d =~'+latex(Td)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## Rotary A\n",
"### How to derive a rotation matrix for a nutating joint\n",
"\n",
"The machine in our example has a 'nutating' rotary joint A. Nutating meaning here that the rotational axis of A is not parallel to one of the axes (x, y or z) of our machine coordinate system and hence we cannot use any of the basic rotation matrices to model it in our kinematic. The basic rotation matrices are, as we will see, only three particular solutions of a general formula to describe rotations around any given vector $V(v_x, v_y, v_z$) in space. \n",
"So to construct the rotation matrix $R_a$ for the slanted joint A of our machine we can use the ‘Rodrigues' rotation formula. (Note that there is also an 'Euler-Rodrigues' formula that can be used for the same purpose but uses different parameters.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\\begin{equation}\n",
"R_a ~=~ I ~+~ sin\\theta \\cdot V + (2sin²{\\theta \\over2}) \\cdot V²\n",
"\\label{eq:rod_rot}\n",
"\\end{equation}\n",
"With:\n",
"\\begin{equation}\n",
"I ~=~ \n",
"\\left(\\begin{array}{rrrr}\n",
"1 & 0 & 0 \\\\\n",
"0 & 1 & 0 \\\\\n",
"0 & 0 & 1 \n",
"\\end{array}\\right)\n",
"~~\n",
"V ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" 0 & -v_z & v_y \\\\\n",
" v_z & 0 & -v_x \\\\\n",
"-v_y & v_x & 0 \n",
"\\end{array}\\right)\n",
"\\label{eq:mat_I_V}\n",
"\\end{equation}\n",
"\n",
"For the matrix $V$ we need a unit vector that represents the rotational axis of the 45° joint:\n",
"\n",
"$$\\vec{v}=(v_x,v_y,v_z)$$\n",
"\n",
"with the components:\n",
"\n",
"\\begin{equation}\n",
"v_x=0 ~~~~ v_y = {1\\over{\\sqrt2}} ~~~~ v_z = {1\\over{\\sqrt2}}\n",
"\\label{eq:vector_v}\n",
"\\end{equation}\n",
"\n",
"Using $\\eqref{eq:rod_rot}$ and $\\eqref{eq:mat_I_V}$, $R_A $ can thus be written as:\n",
"\n",
"\\begin{equation}\n",
"R_a ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" cos\\theta+v²_x(1-cos\\theta) & v_x v_y(1-cos\\theta)-v_z sin\\theta & v_x v_z(1-cos\\theta)+v_y sin\\theta \\\\\n",
" v_x v_y(1-cos\\theta)+v_z sin\\theta & cos\\theta+v²_y(1-cos\\theta) & v_y v_z(1-cos\\theta)-v_x sin\\theta \\\\\n",
" v_x v_z(1-cos\\theta)-v_y sin\\theta & v_y v_z(1-cos\\theta)+v_x sin\\theta & cos\\theta+v²_z(1-cos\\theta) \n",
"\\end{array}\\right)\n",
"\\end{equation}\n",
"\n",
"\n",
"using $\\eqref{eq:vector_v}$ and the substitution \n",
"$$ sin\\theta = Sa ~~~ cos\\theta = Ca$$\n",
"\n",
"$R_a $ becomes\n",
"\n",
"\\begin{equation}\n",
"R_a ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" Ca & -{1\\over{\\sqrt2}}Sa & {1\\over{\\sqrt2}}Sa \\\\\n",
" {1\\over{\\sqrt2}}Sa & {1\\over2}(1+Ca) & {1\\over2}(1-Ca) \\\\\n",
" -{1\\over{\\sqrt2}}Sa & {1\\over2}(1-Ca) & {1\\over2}(1+Ca)\n",
"\\end{array}\\right)\n",
"\\end{equation}\n",
"\n",
"and by expanding it to a 4x4 matrix we have now the required transformation matrix\n",
"\n",
"\\begin{equation}\n",
"R_a ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
" Ca & -{1\\over{\\sqrt2}}Sa & {1\\over{\\sqrt2}}Sa & 0 \\\\\n",
" {1\\over{\\sqrt2}}Sa & {1\\over2}(1+Ca) & {1\\over2}(1-Ca) & 0 \\\\\n",
" -{1\\over{\\sqrt2}}Sa & {1\\over2}(1-Ca) & {1\\over2}(1+Ca) & 0 \\\\\n",
" 0 & 0 & 0 & 1\n",
"\\end{array}\\right)\n",
"\\end{equation}\n",
"\n",
"\n",
"To make the matrix multiplications more manageable we substitute \n",
"\n",
"\\begin{equation}\n",
"{1\\over{\\sqrt2}}Sa ~=~ t ~~~~~~~~ {1\\over2}(1+Ca)~=~ u ~~~~~~~~ {1\\over2}(1-Ca)~=~ v \n",
"\\end{equation}\n",
"\n",
"and get the transformation matrix in it's final form\n",
"\n",
"\\begin{equation}\n",
"R_a ~=~\n",
"\\left(\\begin{array}{rrrr}\n",
"Ca & -t & t & 0 \\\\\n",
" t & u & v & 0 \\\\\n",
"-t & v & u & 0 \\\\\n",
" 0 & 0 & 0 & 1\n",
"\\end{array}\\right)\n",
"\\end{equation}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define rotation matrix for joint A\n",
"var('Ca','t','u','v')\n",
"Ra=matrix([[Ca, -t, t, 0],\n",
" [ t, u, v, 0],\n",
" [-t, v, u, 0],\n",
" [ 0, 0, 0, 1]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tool length offset (TLO)\n",
"\n",
"As we could see above in our inspection of the spindle rotary assembly the tool length offset ($Dt$) has the same direction as the z-component of the pivot-offset ($Lz$) that is, a positive $Dt$ value points in the negative machine-z direction.\n",
"So as a building block in our forward kinematic transformation the value of $Dt$ needs to be in the negative:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define translation matrix for the tool length\n",
"var('Dt')\n",
"Tt=matrix([[ 1, 0, 0, 0],\n",
" [ 0, 1, 0, 0 ],\n",
" [ 0, 0, 1, -Dt],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T_t =~'+latex(Tt)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Tz=Tl*Tt\n",
"display(Math(rf'T_lt =~'+latex(Tl)+rf'\\cdot'+latex(Tt)+rf'=~'+latex(Tz)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## Rotary C, B and A with offsets\n",
"Now that we have all the building blocks we can put everything together:\n",
"\n",
"$$ ^QA_P = R_c \\cdot T_p \\cdot R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# calculate the forward transformation matrix\n",
"# note: the brackets *(Tl*Tt) are only used to get a cosmetically\n",
"# prettier result since it seems to keep \"(Dt+Lz)\"\n",
"qAp=Rc*Tp*Rb*Td*Ra*(Tl*Tt)\n",
"display(Math(rf'^Q A_P =~'+latex(qAp)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# calculate the forward kinematic\n",
"display(Math(rf'Q =~'+latex(Q_)+rf'=~^QA_P \\cdot P'))\n",
"display(Math(rf'Q =~'+latex(Q_)+rf'=~'+latex(qAp)+rf'\\cdot'+latex(P_)))\n",
"display(Math(rf'Q =~'+latex(Q_)+rf'=~'+latex(Q_out)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Extract the tool-position vector Q (FORWARD KINEMATICS) from\n",
"# the fourth column of the forward transformation matrix\n",
"Qx=qAp[0][3]\n",
"Qy=qAp[1][3]\n",
"Qz=qAp[2][3]\n",
"\n",
"display(Math(latex(Q_[0][0]) + rf'~=~' + latex(Qx)))\n",
"display(Math(latex(Q_[1][0]) + rf'~=~' + latex(Qy)))\n",
"display(Math(latex(Q_[2][0]) + rf'~=~' + latex(Qz)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So with this as our core forward transformation matrix we can now check the output of this model for the home position $$P(Px,Py,Pz)=(0,0,0)$$ and $$\\theta_a = \\theta_b = \\theta_c =0$$ $$(Ca=Cb=Cc=1, ~~Sa=Sb=Sc=0)$$\n",
"\n",
"and taking our substitution in $R_a$ into account:\n",
"\n",
"\\begin{equation}\n",
"{1\\over{\\sqrt2}}Sa ~=~ t ~~~~~~~~ {1\\over2}(1+Ca)~=~ u ~~~~~~~~ {1\\over2}(1-Ca)~=~ v \n",
"\\end{equation}\n",
"\n",
"which for $\\theta_a = 0$ reduces to:\n",
"\n",
"\\begin{equation}\n",
"t~=~ 0 ~~~~~~~~ u=~ 1 ~~~~~~~~ v~=~ 0 \n",
"\\end{equation}\n",
"\n",
"\n",
"we get the coordinates:\n",
"$$Qx= -Dx $$\n",
"$$Qy= -Ly $$\n",
"$$Qz= -(Dt + Lz) -Dz$$\n",
"\n",
"\\begin{equation}\n",
"Q ~=~\n",
"\\left(\\begin{array}{r}\n",
" Qx \\\\\n",
" Qy \\\\\n",
" Qz \\\\\n",
" 1\n",
"\\end{array}\\right)\n",
"~=~\n",
"\\left(\\begin{array}{r}\n",
" -Dx \\\\\n",
" -Ly \\\\\n",
" -Dt-Lz-Dz \\\\\n",
" 1\n",
"\\end{array}\\right)\n",
"\\end{equation}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get the expected coordinate position $Q(Qx,Qy,Qz)=(0,0,0)$ we need to compensate for these offsets by subtracting them from the result of our core transformation matrix. \n",
"Note subtracting a vector is the same as adding its inverse vector \n",
"We define the transformation:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Tc=matrix([[ 1, 0, 0, Dx],\n",
" [ 0, 1, 0, Ly ],\n",
" [ 0, 0, 1, (Dt+Lz+Dz)],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T_c =~'+latex(Tc)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that this is a translation of the result of our forward transformation matrix $^QA_P$ it needs to be multiplied from the left:\n",
"\n",
"$$^QA_P = T_c \\cdot R_c \\cdot T_p \\cdot R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t$$"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# calculate the forward transformation matrix\n",
"# note: the brackets *(Tl*Tt) are only used to get a cosmetically\n",
"# prettier result since it seems to keep \"(Dt+Lz)\"\n",
"qAp=Tc*Rc*Tp*Rb*Td*Ra*(Tl*Tt)\n",
"display(Math(rf'^Q A_P =~'+latex(qAp)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Extract the tool-position vector Q (FORWARD KINEMATICS) from\n",
"# the fourth column of the forward transformation matrix\n",
"Qx=qAp[0][3]\n",
"Qy=qAp[1][3]\n",
"Qz=qAp[2][3]\n",
"\n",
"display(Math(latex(Q_[0][0]) + rf'~=~' + latex(Qx)))\n",
"display(Math(latex(Q_[1][0]) + rf'~=~' + latex(Qy)))\n",
"display(Math(latex(Q_[2][0]) + rf'~=~' + latex(Qz)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"Note that in the kinematic component $Dt$ is added to the z-pivot value *Lz* in the line
* Lz = Lz + Dt;*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 1: // ========================= TCP kinematics FORWARD \n",
" Lz = Lz + Dt;\n",
" // onLy used to be consistent with math documentation\n",
" Px = j[0];\n",
" Py = j[1];\n",
" Pz = j[2];\n",
"\n",
" pos->tran.x = - Cb*Cc*Dx - Cc*Dz*Sb \n",
" + (Cb*Cc*t - Cc*Sb*v + Sc*u)*Ly \n",
" - (Cb*Cc*t + Cc*Sb*u - Sc*v)*Lz \n",
" + Cc*Px - Py*Sc\n",
" + Dx;\n",
"\n",
" pos->tran.y = - Cb*Dx*Sc - Dz*Sb*Sc \n",
" + (Cb*Sc*t - Sb*Sc*v - Cc*u)*Ly \n",
" - (Cb*Sc*t + Sb*Sc*u + Cc*v)*Lz \n",
" + Cc*Py + Px*Sc\n",
" + Ly;\n",
"\n",
" pos->tran.z = - Cb*Dz \n",
" - (Sb*t + Cb*v)*Ly \n",
" + (Sb*t - Cb*u)*Lz \n",
" + Dx*Sb \n",
" + Pz\n",
" + Lz\n",
" + Dz \n",
" ; \n",
"\n",
" pos->a = j[3];\n",
" pos->b = j[4];\n",
" pos->c = j[5];\n",
"\n",
" break;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Position offset of the rotary table to the machine reference point\n",
" \n",
"Up to this point we have assumed that the machine reference point coincides with the rotational axis of our rotary C table.\n",
"However, it is somewhat unlikely that a machines home position is exactly in the rotational axis of the rotary table and this will need to be taken into account in the kinematic model.
\n",
"If we assume that we have a setup with no TLO ($Dt=0$), no geometric offset ($Dx=Dz=0$), no pivot-length ($Ly=Dz=0$) and no offset of the rotation-axis of the rotary C to the machine reference point.
\n",
"A tool positioned in rotational axis of C would have a joint-position of $P(0,0,P_z)$ and that would give the expected resulting coordinate position of $Q(0,0,P_z)$.
\n",
"If we now assume that the rotation-axis of our rotary C table is offset from the machine reference point by $(ra_x,ra_y)$ then our joint-position would be equal to $P(ra_x,ra_y,P_z)$ which would give us a result of $Q(ra_x, ra_y, P_z)$.
\n",
"So we need to subtract the offset $(ra_x,ra_y)$ from the joint-position $P=(P_x-ra_x, P_y-ra_y, P_z)$ before we input it into our transformation matrix. In other words we need to translate the joint-position vector in the opposite direction along the offset vector with the components $(ra_x,ra_y,0)$. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"var('Drax','Dray')\n",
"# define offset translation matrix for the rotary c taböe\n",
"Tr=matrix([[ 1, 0, 0, -Drax],\n",
" [ 0, 1, 0, -Dray],\n",
" [ 0, 0, 1, 0],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'Tr =~'+latex(Tr)))\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that this is a translation $T_r$ of the input $T_p$ of our forward transformation matrix $^QA_P$ so it needs to be multiplied to $T_p$ from the right :"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Tpr=Tp*Tr\n",
"display(Math(rf'T_p \\cdot T_r =~'+latex(Tp)+rf'\\cdot'+latex(Tr)+rf'=~'+latex(Tpr)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Thus we get:\n",
"\n",
"$$^QA_P = T_c \\cdot R_c \\cdot T_p \\cdot T_r \\cdot R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t$$\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However some more consideration is needed for now a joint position of $P(ra_x,ra_y,P_z)$ will result in a coordinate position of $Q(0,0,P_z)$ which is of course not the value we can hand back to LinuxCNC because if the rotation axis is offset from machine home position then the coordinate position would need to be $Q(rp_x,rp_y,P_z)$.
\n",
"So to be consistent we need to add the offset values back to the results of our calculations which we do again by multiplying a vector translation to the left of our forward kinematic:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Tnr=matrix([[ 1, 0, 0, Drax],\n",
" [ 0, 1, 0, Dray],\n",
" [ 0, 0, 1, 0 ],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T._r =~'+latex(Tnr)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Thus we get our final forward transformation matrix:\n",
"\n",
"$$^QA_P = T_{-r} \\cdot T_c \\cdot R_c \\cdot T_p \\cdot T_r \\cdot R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t$$\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'Tnr' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;31m# prettier result since it seems to keep \"(Dt+Lz)\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# similar for (Tp*Tr)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mqAp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTnr\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mTc\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mRc\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mTp\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mTr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mRb\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mTd\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mRa\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mTl\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mTt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mMath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mrf'^Q A_P =~'\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mlatex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqAp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mNameError\u001b[0m: name 'Tnr' is not defined"
]
}
],
"source": [
"# calculate the forward transformation matrix\n",
"# note: the brackets *(Tl*Tt) are only used to get a cosmetically\n",
"# prettier result since it seems to keep \"(Dt+Lz)\"\n",
"# similar for (Tp*Tr)\n",
"qAp=Tnr*Tc*Rc*(Tp*Tr)*Rb*Td*Ra*(Tl*Tt)\n",
"display(Math(rf'^Q A_P =~'+latex(qAp)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Extract the tool-position vector Q (FORWARD KINEMATICS) from\n",
"# the fourth column of the forward transformation matrix\n",
"Qx=qAp[0][3]\n",
"Qy=qAp[1][3]\n",
"Qz=qAp[2][3]\n",
"\n",
"display(Math(latex(Q_[0][0]) + rf'~=~' + latex(Qx)))\n",
"display(Math(latex(Q_[1][0]) + rf'~=~' + latex(Qy)))\n",
"display(Math(latex(Q_[2][0]) + rf'~=~' + latex(Qz)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# expressions as used in xyzabc_trsrn.comp\n",
"print('TCP kinematics FORWARD')\n",
"print('Qx = ', Qx)\n",
"print('Qy = ', Qy)\n",
"print('Qz = ', Qz)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 1: // ========================= TCP kinematics FORWARD \n",
" // onLy used to be consistent with math in documentation\n",
" Px = j[0];\n",
" Py = j[1];\n",
" Pz = j[2];\n",
"\n",
" pos->tran.x = - Cb*Cc*Dx \n",
" - Cc*Dz*Sb \n",
" - Cc*(Drax - Px) \n",
" - (Cb*Cc*t + Cc*Sb*u - Sc*v)*(Dt + Lz) \n",
" + (Cb*Cc*t - Cc*Sb*v + Sc*u)*Ly \n",
" + (Dray - Py)*Sc \n",
" + Drax \n",
" + Dx;\n",
"\n",
" pos->tran.y = - Cb*Dx*Sc \n",
" - Dz*Sb*Sc \n",
" - Cc*(Dray - Py) \n",
" - (Cb*Sc*t + Sb*Sc*u + Cc*v)*(Dt + Lz) \n",
" + (Cb*Sc*t - Sb*Sc*v - Cc*u)*Ly \n",
" - (Drax - Px)*Sc \n",
" + Dray \n",
" + Ly\n",
" ;\n",
"\n",
" pos->tran.z = (Sb*t - Cb*u)*(Dt + Lz)\n",
" - Cb*Dz \n",
" - (Sb*t + Cb*v)*Ly \n",
" + Dx*Sb \n",
" + Dt \n",
" + Dz \n",
" + Lz \n",
" + Pz; \n",
"\n",
" pos->a = j[3];\n",
" pos->b = j[4];\n",
" pos->c = j[5];\n",
"\n",
" break;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Inverse transformation\n",
"\n",
"To calculate the joint position $P$ from a given coordinate position $Q$ we need to follow the kinematic chain in the opposite direction from the spindle to work piece. We can invert our forward kinematic transformation and build it backwards, using the inverted rotations and the inverted translations.
\n",
"Note that the input matrix $T_p$ with its translation {$T_p \\cdot T_r$} for the offset of the rotation axis and the output translation $[T_{-r} \\cdot T_c]$ need to be handled with some consideration: \n",
"\n",
"$$^QA_P = [T_{-r} \\cdot T_c] \\cdot R_c \\cdot \\{T_p \\cdot T_r\\} \\cdot (R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t)$$\n",
"$$^QA_P = [offset_{fo}] \\cdot R_c \\cdot \\{input+offset_{fi}\\} \\cdot (R_b \\cdot T_d \\cdot R_a \\cdot T_l \\cdot T_t)$$\n",
"\n",
"\n",
"\n",
"In the inverse transformation the input is $T_q$ and it's 'offset' is the inverse of the 'output offset' of the forward transformation.\n",
"Basically what we added to the result of the forward transformation needs to be removed from the input to the inverse transformation and vice versa. On the right side we additionally need to rotate the inverted spindle offsets by the rotation of the rotary C:\n",
"\n",
"$$^QA_P = [-offset_{fi}] \\cdot R^T_c \\cdot \\{input-offset_{fo}\\} \\cdot (R_c \\cdot R_b \\cdot T_{-d} \\cdot R_a \\cdot T_{-l} \\cdot T_{-t})$$\n",
"\n",
"\n",
"$$^PA_Q = \\{ T_{-r}\\} \\cdot R^T_c \\cdot T_q \\cdot [T_{r} \\cdot T_{-c}] \\cdot(R_c \\cdot R_b \\cdot T_{-d} \\cdot R_a \\cdot T_{-l} \\cdot T_{-t})$$\n",
"\n",
"Note that the brackets in the above expressions are only used to highlight the different parts of the kinematic model and are not mathematically required. \n",
"\n",
"This being the inverse transformation our input matrix has changed to $T_q$:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# define coordinate position matrix\n",
"var('Qx','Qy','Qz')\n",
"Tq=matrix([[ 1, 0, 0, Qx ],\n",
" [ 0, 1, 0, Qy],\n",
" [ 0, 0, 1, Qz],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T_q =~'+latex(Tq)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#define inverse translation matrices\n",
"Tnc=matrix([[ 1, 0, 0, -Dx],\n",
" [ 0, 1, 0, -Ly ],\n",
" [ 0, 0, 1, -(Dt+Lz+Dz)],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T._c =~'+latex(Tnc)))\n",
"\n",
"Tnt=matrix([[ 1, 0, 0, 0],\n",
" [ 0, 1, 0, 0 ],\n",
" [ 0, 0, 1, Dt],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T._t =~'+latex(Tnt)))\n",
"Tnd=matrix([[ 1, 0, 0, Dx],\n",
" [ 0, 1, 0, 0 ],\n",
" [ 0, 0, 1, Dz],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T._d =~'+latex(Tnd)))\n",
"Tnl=matrix([[ 1, 0, 0, 0 ],\n",
" [ 0, 1, 0, Ly],\n",
" [ 0, 0, 1, Lz],\n",
" [ 0, 0, 0, 1 ]])\n",
"display(Math(rf'T._l =~'+latex(Tnl)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#qAp=Tnr*Tc*Rc*(Tp*Tr)*Rb*Td*Ra*(Tl*Tt)\n",
"pAq=Tnr*Rc.transpose()*(Tq*Tr*Tnc)*Rc*Rb*Tnd*Ra*(Tnl*Tnt)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(Math(latex(Tq)+rf'\\cdot'+latex(Rb.transpose())+rf'\\cdot'+latex(Ra.transpose())+rf'\\cdot'+latex(Tnl)))\n",
"display(Math(rf'^PA_Q=~'+latex(pAq)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Extract the joint-position vector P (INVERSE KINEMATICS) from\n",
"# the fourth column of the inverse transformation matrix\n",
"Px=pAq[0][3]\n",
"Py=pAq[1][3]\n",
"Pz=pAq[2][3]\n",
"\n",
"display(Math(latex(P_[0][0]) + rf'~=~' + latex(Px)))\n",
"display(Math(latex(P_[1][0]) + rf'~=~' + latex(Py)))\n",
"display(Math(latex(P_[2][0]) + rf'~=~' + latex(Pz)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# expressions as used in xyzabc_trsrn.comp\n",
"print('TCP kinematics INVERSE')\n",
"print('Px = ', Px)\n",
"print('Py = ', Py)\n",
"print('Pz = ', Pz)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that $$cos²\\theta+sin²\\theta=1$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 1: // ========================= TCP kinematics INVERSE \n",
" Qx = pos->tran.x;\n",
" Qy = pos->tran.y; \n",
" Qz = pos->tran.z; \n",
"\n",
" j[0] = Cb*Dx \n",
" + Dz*Sb \n",
" - Cc*(Drax + Dx - Qx) \n",
" + (Cb*t + Sb*u)*(Dt + Lz) \n",
" - (Cb*t - Sb*v)*Ly \n",
" - (Dray + Ly - Qy)*Sc \n",
" + Drax;\n",
"\n",
" j[1] = Ly*u \n",
" + (Dt + Lz)*v \n",
" - Cc*(Dray + Ly - Qy) \n",
" + (Drax + Dx - Qx)*Sc \n",
" + Dray;\n",
"\n",
" j[2] = - (Sb*t - Cb*u)*(Dt + Lz) \n",
" + Cb*Dz + (Sb*t + Cb*v)*Ly \n",
" - Dx*Sb \n",
" - Dt \n",
" - Dz \n",
" - Lz \n",
" + Qz;\n",
"\n",
" j[3] = pos->a;\n",
" j[4] = pos->b;\n",
" j[5] = pos->c;\n",
"\n",
" break;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Also note that with this kinematic model the offset of the rotational axis of the rotary C is the vector $(Dra_x,Dra_y,0)$ going from the pivot-point of the spindle assembly to the rotatio-axis of C with the machine in the home position.\n",
"In other words if the C-rotary-axis has the absolute machine coordinates $(Rot_x,Rot_y,0)$ then $$Dra_x = Rot_x - Dx$$ $$Dra_y = Rot_y - Ly$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the kinematic comp this is handled in the variable declaration:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" // geometric offsets of the universal spindle head as defined in \n",
" double Ly = *haldata->y_pivot;\n",
" double Lz = *haldata->z_pivot;\n",
" double Dx = *haldata->x_offset;\n",
" double Dz = *haldata->z_offset;\n",
" double Drax = *haldata->x_rot_axis - Dx;\n",
" double Dray = *haldata->y_rot_axis - Ly;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This concludes the TCP kinematic\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-----"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tool Kinematic model\n",
"Many, if not most, applications for 5-axis milling do not require TCP kinematics where all five axes are moving simultaneously but only need the work piece to be oriented at certain angles to the tool in between 'conventional' three axis (x,y,z) milling operations. In these operations the tool is not reoriented while cutting the material. This is what is called '3+2' mode.
\n",
"In 3+2 mode the machine operator can use the familiar built in cycles the machine controller offers for 3d (x,y,z) use and does not necessarily require CAM/CAD software to machine a part. The ability to move the tool in a plane perpendicular to its rotational axis also allows the use of probes for job setup.
\n",
"Machines with work side rotation (eg the C rotary in our example, or the 'table-rotary-tilting' examples included in LinuxCNC simulation configs) can orient the work piece to the tool by simply rotating the work to the required orientation as the tool always remains oriented along the machine z-axis. A drilling operation on such a machine will thus still only require the machine z-axis to be moved no matter how the part is oriented. In this case no special kinematic is required.
\n",
"Machines with tool side rotation like the one presented here with A and B spindle rotations however retain the directions of the unrotated machine coordinate system when moving in IDENTITY and TCP kinematics while the tool orientation changes in respect to the machine coordinate system. A drilling operation on such a machine will require the machine to move in a complex manner that may include all three (x,y,z) axes depending on how tool is oriented.
\n",
"Commercial 5-axis machine controllers offer built in functionality that allow the operator to define work plane orientation using Gcode commands like G68.2 or similar; automatically adjusting the kinematic model to the type of kinematic of the machine. To implement such a feature for our example machine using LinuxCNC we need to create another kind of kinematic model which we will call the TOOL kinematic. \n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Forward transformation\n",
"\n",
"For the TOOL kinematic start at the tool tip and work towards the work piece omitting the C axis rotation. So in our example case our forward transformation matrix $^QA_P$ is :\n",
"\n",
"$$^QA_P = R^T_{tc} \\cdot T_l \\cdot R^T_a \\cdot T_d \\cdot R^T_b\\ \\cdot ( T_p \\cdot T_{-d} \\cdot T_{-l})$$\n",
"\n",
"Where $R_{tc}$ is a virtual rotation around the rotational axis of the tool. This is required because we need a way to define the orientation of the tool-x (and -y). Without this the tool-coordinate system will be 'fixed' to the spindle head orientation which we do certainly want for the tool-z orientation but not for the x,y-directions. This will be important when developing the 'Tilted Work Plane' features later.\n",
"\n",
"Tool-length offset is applied automatically in LinuxCNC by subtracting the tool length value stored in the tool table from the z-axis coordinate position while the joint position remains unchanged. This built in compensation also work in our custom tool kinematic because the tool coordinate system is always aligned with the rotational axis of the tool.\n",
"\n",
"Note that the brackets in the above expressions are only used to highlight the different parts of the kinematic model and are not mathematically required. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# add c rotation of tool coordinate system\n",
"var('Ctc','Stc')\n",
"Rtc=matrix([[ Ctc, -Stc, 0, 0],\n",
" [ Stc, Ctc, 0, 0],\n",
" [ 0 , 0 , 1, 0],\n",
" [ 0, 0 , 0, 1]])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"display(Math(\n",
" latex(Rtc.transpose())+rf'\\cdot'\n",
" +latex(Tl) +rf'\\cdot'\n",
" +latex(Ra.transpose()) +rf'\\cdot'\n",
" +latex(Td) +rf'\\cdot'\n",
" +latex(Rb.transpose()) +rf'\\cdot'\n",
" +latex(Tp) +rf'\\cdot'\n",
" +latex(Tnd) +rf'\\cdot'\n",
" +latex(Tnl) \n",
" )) "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# calculate the forward kinematic \n",
"# brackets are only used for cosmetically adjust the resulting formula\n",
"qAp=Rtc.transpose()*(Tl)*Ra.transpose()*Td*Rb.transpose()*(Tp*Tnd*Tnl) \n",
"display(Math(rf'^Q A_P =~'+latex(qAp)))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Extract the tool-position vector Q (FORWARD KINEMATICS) from\n",
"# the fourth column of the forward transformation matrix\n",
"Qx=qAp[0][3]\n",
"Qy=qAp[1][3]\n",
"Qz=qAp[2][3]\n",
"\n",
"display(Math(latex(Q_[0][0]) + rf'~=~' + latex(Qx)))\n",
"display(Math(latex(Q_[1][0]) + rf'~=~' + latex(Qy)))\n",
"display(Math(latex(Q_[2][0]) + rf'~=~' + latex(Qz)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# expressions as used in xyzabc_trsrn.comp\n",
"print('TOOL kinematics FORWARD')\n",
"print('Qx = ', Qx)\n",
"print('Qy = ', Qy)\n",
"print('Qz = ', Qz)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In preparation for the 'Tilted work plane' (TWP) feature we introduce a custom offset *Twp-(x,y,z)*. This will be the offset from the current work offset (eg G54) to the origin of the TWP as defined by the (X,Y,Z) words in the Gcode (eg G68.2).
\n",
"Example:
\n",
"*G68.2 X10 Y20 Z30 ...* (the other words are not relevant here)\n",
"will define a TWP with an offset of (10,20,30) to the active work origin (0,0,0).\n",
"
\n",
"\n",
"\n",
"This custom offset can be built into our forward kinematic as a translation of the result of the forward transformation:
\n",
"$$Q~=~ T_{-twp} \\cdot~ ^QA_P \\cdot P$$\n",
"Which means simply adding a subtraction to the respective formula in our kinematic component: "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 3: // ========================= TOOL kinematics FORWARD \n",
" // onLy used to be consistent with math in documentation\n",
" Px = j[0];\n",
" Py = j[1];\n",
" Pz = j[2];\n",
"\n",
" pos->tran.x = ((Ca*Ctc - Stc*t)*Cb \n",
" - (Ctc*t - Stc*v)*Sb)*(Dx + Px) \n",
" - (Ca*Ctc - Stc*t)*Dx \n",
" - ((Ctc*t - Stc*v)*Cb \n",
" + (Ca*Ctc - Stc*t)*Sb)*(Dz + Lz + Pz) \n",
" + (Ctc*t - Stc*v)*Dz \n",
" + (Ctc*t + Stc*u)*(Ly + Py) \n",
" - Ly*Stc\n",
" - Twp_x;\n",
"\n",
"\n",
" pos->tran.y = - ((Ca*Stc + Ctc*t)*Cb - (Stc*t \n",
" + Ctc*v)*Sb)*(Dx + Px) \n",
" + (Ca*Stc + Ctc*t)*Dx \n",
" + ((Stc*t + Ctc*v)*Cb + (Ca*Stc \n",
" + Ctc*t)*Sb)*(Dz + Lz + Pz) \n",
" - (Stc*t + Ctc*v)*Dz \n",
" - (Stc*t - Ctc*u)*(Ly + Py) \n",
" - Ctc*Ly\n",
" - Twp_y;\n",
"\n",
"\n",
" pos->tran.z = (Cb*t + Sb*u)*(Dx + Px) \n",
" - (Sb*t - Cb*u)*(Dz + Lz + Pz)\n",
" - Dx*t \n",
" - Dz*u\n",
" + (Ly + Py)*v\n",
" - Lz\n",
" - Twp_z;\n",
"\n",
" pos->a = j[3];\n",
" pos->b = j[4];\n",
" pos->c = j[5];\n",
"\n",
" break;"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Inverse transformation\n",
"\n",
"For the inverse kinematic we need to move from the machine coordinates to the tool coordinates starting with the inverted offset translations $(T_d \\cdot T_l )$ for the offsets we used on the input of the forward kinematic $T_{-d} \\cdot T_{-l}$:\n",
"\n",
"$$^QA_P = R^T_{tc} \\cdot T_l \\cdot R^T_a \\cdot T_d \\cdot R^T_b\\ \\cdot ( T_p \\cdot T_{-d} \\cdot T_{-l})$$\n",
"\n",
"$$^PA_Q = (T_d \\cdot T_l ) \\cdot R_b \\cdot T_{-d} \\cdot R_a \\cdot T_{-l} \\cdot R_{tc}\\cdot T_q $$\n",
"\n",
"Note that the brackets in the above expressions are only used to highlight the different parts of the kinematic model and are not mathematically required. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# calculate the inverse kinematic \n",
"# brackets are only used for cosmetically adjust the resulting formula\n",
"#qAp=Rtc.transpose()*(Tnl)*Ra.transpose()*Tnd*Rb.transpose()*(Tp*Td*Tl) \n",
"pAq=(Td*Tl)*Rb*Tnd*Ra*Tnl*Rtc*Tq\n",
"\n",
"display(Math(rf'^PA_Q =~'+latex(pAq)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# Extract the joint-position vector P (INVERSE KINEMATICS) from\n",
"# the fourth column of the inverse transformation matrix\n",
"Px=pAq[0][3]\n",
"Py=pAq[1][3]\n",
"Pz=pAq[2][3]\n",
"\n",
"display(Math(latex(P_[0][0]) + rf'~=~' + latex(Px)))\n",
"display(Math(latex(P_[1][0]) + rf'~=~' + latex(Py)))\n",
"display(Math(latex(P_[2][0]) + rf'~=~' + latex(Pz)))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# expressions as used in xyzabc_trsrn.comp\n",
"print('TCP kinematics INVERSE')\n",
"print('Px = ', Px)\n",
"print('Py = ', Py)\n",
"print('Pz = ', Pz)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The custom TWP-offset we built into our forward kinematic as a translation of the result needs to be reflected in our inverse kinematic by an inverse translation of the input:
\n",
"$$P~=~ ^PA_Q \\cdot Q \\cdot T_{twp}~$$\n",
"In the kinematic component this is done in lines:
\n",
"\n",
"*Qx = pos->tran.x + Twp_x;*
\n",
"*Qy = pos->tran.y + Twp_y;*
\n",
"*Qz = pos->tran.z + Twp_z;*\n",
" \n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" case 3: // ========================= TOOL kinematics INVERSE\n",
" Qx = pos->tran.x + Twp_x;\n",
" Qy = pos->tran.y + Twp_y;\n",
" Qz = pos->tran.z + Twp_z;\n",
"\n",
" j[0] = Cb*Dx \n",
" - (Cb*t - Sb*v)*Ly \n",
" + (Cb*t + Sb*u)*Lz \n",
" + ((Ca*Cb - Sb*t)*Ctc - (Cb*t - Sb*v)*Stc)*Qx \n",
" - ((Cb*t - Sb*v)*Ctc + (Ca*Cb - Sb*t)*Stc)*Qy \n",
" + (Cb*t + Sb*u)*Qz + Dz*Sb - Dx\n",
" ;\n",
"\n",
" j[1] = (Ctc*t + Stc*u)*Qx \n",
" - (Stc*t - Ctc*u)*Qy \n",
" + Ly*u \n",
" + Lz*v \n",
" + Qz*v \n",
" - Ly\n",
" ;\n",
"\n",
"\n",
" j[2] = Cb*Dz \n",
" + (Sb*t + Cb*v)*Ly \n",
" - (Sb*t - Cb*u)*Lz \n",
" - ((Ca*Sb + Cb*t)*Ctc - (Sb*t + Cb*v)*Stc)*Qx \n",
" + ((Sb*t + Cb*v)*Ctc + (Ca*Sb + Cb*t)*Stc)*Qy \n",
" - (Sb*t - Cb*u)*Qz \n",
" - Dx*Sb \n",
" - Dz \n",
" - Lz\n",
" ;\n",
"\n",
" j[3] = pos->a;\n",
" j[4] = pos->b;\n",
" j[5] = pos->c;\n",
"\n",
" break;"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This completes the TOOL kinematic and we can move on to our implementation of the 'Tilted Work Plane' feature\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"-----"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 'Tilted Work Plane' (TWP)\n",
"\n",
"Commercial 5-axis machine controllers offer built in functionality that allow the operator to define work plane orientation using Gcode commands like G68.2 or similar. With the new custom TOOL kinematic we can now explore a possible implementation of TWP in LinuxCNC.
\n",
"For the purpose of this example we choose the already mentioned 'G68.2' and the associated Gcodes as used by Fanuc:
\n",
"\n",
"* G68.2 - Set custom coordinate system using five different modes\n",
"* G68.3 - Set custom coordinate system using current tool orientation\n",
"* G68.4 - Same as 68.2 but as an incremental reorientation of an existing TWP\n",
"\n",
"* G53.1 - Orient the tool to the TWP using non-TCP joint rotation \n",
"* G53.6 - Orient the tool to the TWP using TCP joint rotation\n",
"\n",
"* G69 - Cancel the TWP setting\n",
"\n",
"The TWP feature always requires a TWP-definition (G68.x) before an orientation command (G53.x) can be issued.
\n",
"G68.x commands do not cause any machine movement while G53 command will cause immediate machine movement.\n",
"At the end of a TWP operation a G69 command is used to return to machine coordinates.
\n",
"\n",
"In this presentation we use a 'Pure Python Remap' to make the above Gcode commands available to the machine operator. As a consequence our depth of integration is quite limited in the sense that the LinuxCNC Gcode interpreter will be totally ignorant of our 'TWP'-mode."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Implementation outline\n",
"\n",
"This should give a rough overview of how TWP functionality is to be achieved with a python remap:\n",
"\n",
"\n",
"The idea of TWP is to give the machine operator a mechanism to define a virtual work plane that is rotated (and optionally offset) in respect to the machine coordinate system. As mentioned above this is fairly trivial in a machine with work side rotation as its tool spindle is fixed to be aligned with the machine z-axis. In such a machine the tool coordinate system (TCS) remains in the orientation of the machine coordinate system (MCS)
\n",
"In a machine with tool side rotation however the TCS rotates in respect to the MCS as the rotary joints move. The orientation of the TCS for a given joint rotation can be calculated using the rotational part of the transformation matrix as derived above. Furthermore we can also solve for the inverse case of finding the joint positions that will orient the tool to a given orientation.\n",
"Note here that this is very much dependent on the specific kinematic of the machine at hand and will require careful analysis of the kinematic model to find any ambiguities in the solution as some specific tool orientations might be achieved in multiple joint positions. \n",
"\n",
"So to define the TWP means to define a target TCS in respect to the MCS and for this the use of a transformation matrix is well suited as it contains the rotation as well as the (optional) translation. \n",
"\n",
"\n",
"### Step 1: Definition of the TWP\n",
"The TWP is defined by a rotation (I,J,K) and optional offset (X,Y,Z) of the TCS in respect to the MCS. The optional offset is calulated from the current work offset position at the time when the G68.2 or G68.3 command is issued. \n",
"\n",
"The operator can choose from different methods to define the rotation of the custom work plane:\n",
"\n",
"* G68.2 ... P0 ... - 'True Euler'-angles (this is the default if the P word is omitted)\n",
"\n",
"* G68.2 ... P1 ... - 'Pitch-Roll-Yaw'-angles\n",
"\n",
"* G68.2 ... P2 ... - 3 points in the plane (3 points define two vectors)\n",
"\n",
"* G68.2 ... P3 ... - 2 vectors (tool-x and tool-z)\n",
"\n",
"* G68.2 ... P4 ... - projected angles (A around machine-x, B around machine-y)\n",
"\n",
"For the above methods we create the transformation matrix either by using euler-rotations of the 4x4 identity matrix or by the vector information passed by the command using the cross product to calculate the y-vector. For the projected angles we use basich trigonometry to calculate the vector components.\n",
"These calculations are independent from the specific machine kinematics.\n",
"\n",
"* G68.3 ...... - define TWP perpendicular to the current tool orientation \n",
"\n",
"Here we need to use the specific machine kinematic model to calculate the transformation matrix of the current TCS. \n",
"\n",
"* G68.4 ...... - same as G68.2 but relative to the current TWP\n",
"\n",
"Here we need to multiply the current TWP transformation matrix with the one built from the command words.\n",
"\n",
"* G69 - cancel TWP\n",
"\n",
"Here we reset the TWP transformation matrix to the 4x4 identity matrix (ie no rotation, no translation).\n",
"\n",
"Note that, contrary to some other controllers, moves commanded between G68.x and G53.x will be in the MCS.\n",
"\n",
"### Step 2: Orientation of the tool to the TWP\n",
"Once the TWP has been defined the machine spindle can be oriented by moving the rotary joints to the appropriate positions. \n",
"\n",
"The operator can choose whether the rotation is done in IDENTITY kinematics (only the rotary joints move) or in TCP kinematics where all joint may move to keep the tool center point in position:\n",
"\n",
"* G53.1 - orient the tool to the TWP without using TCP\n",
"\n",
"* G53.6 - orient the tool to the TWP with using TCP\n",
"\n",
"### Handling work offsets when switching to TWP\n",
"The TWP is defined relative to the work offset (eg G54) active when issuing a G62.x command. With the execution of the following G53.x command and the reorientation of the spindle the kinematic is switched to the TOOL kinematics and the coordinate position in the DRO will then reflect the coordinate position in the rotated TCS. If the work offset values were to be used without adjusting for the kinematic switch then the physical position of the work offset would be rotated out of place and the reference to the work coordinate system (WCS) would be lost. So in order to find the reference on the work piece in the TCS we need to use the TWP transformation matrix to transform the work offset values set in WCS to the new TCS.
\n",
"Note that the original work offset will have to be restored when the TWP is canceled with a G69 command.
\n",
"\n",
"### Caveats of TOOL kinematics (TOOL kinematics is NOT TCP!)\n",
"Any machine operator using TOOL kinematics should be well aware of its behavior before issuing motion commands!
\n",
"It must be stressed that, while TOOL kinematics are active, any movement of a rotary joint will cause the machine to rotate the TCS around the machine reference point. Because the magnitude of the resulting movement increases with the distance between the tool position and the machine reference point the effect may seem unpreditable to the inexperienced operator.\n",
"Note here that it is possible to set the rotation point in TOOL kinematics in much the same way as we offset the rotation-point from machine zero in TCP kinematics. This however would further complicate work offset transformation and there seems to be no immediate benefit to it. \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"authors": [
{
"name": "David Mueller"
}
],
"celltoolbar": "Tags",
"hide_input": false,
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
},
"title": "XYZAB_TDR kinematics for LinuxCNC"
},
"nbformat": 4,
"nbformat_minor": 4
}