1 #include "RotatableDeltaSolution.h"
3 #include "ConfigValue.h"
4 #include "ConfigCache.h"
5 #include "libs/Kernel.h"
6 #include "libs/nuts_bolts.h"
7 #include "libs/Config.h"
8 #include "libs/utils.h"
9 #include "StreamOutputPool.h"
12 #define delta_e_checksum CHECKSUM("delta_e_checksum")
13 #define delta_f_checksum CHECKSUM("delta_f_checksum")
14 #define delta_re_checksum CHECKSUM("delta_re_checksum")
15 #define delta_rf_checksum CHECKSUM("delta_rf_checksum")
16 #define delta_z_offset_checksum CHECKSUM("delta_z_offset_checksum")
18 #define delta_ee_offs_checksum CHECKSUM("delta_ee_offs_checksum")
19 #define tool_offset_checksum CHECKSUM("tool_offset_checksum")
21 #define z_home_angle_checksum CHECKSUM("z_home_angle_checksum")
23 #define delta_printable_radius_checksum CHECKSUM("delta_printable_radius_checksum")
25 #define xyz_full_steps_per_rotation_checksum CHECKSUM("xyz_full_steps_per_rotation_checksum")
26 #define xyz_microsteps_checksum CHECKSUM("xyz_microsteps_checksum")
27 #define small_pulley_teeth_checksum CHECKSUM("small_pulley_teeth_checksum")
28 #define big_pulley_teeth_checksum CHECKSUM("big_pulley_teeth_checksum")
30 #ifndef alpha_steps_per_mm_checksum
31 #define alpha_steps_per_mm_checksum CHECKSUM("alpha_steps_per_mm_checksum");
34 #ifndef beta_steps_per_mm_checksum
35 #define beta_steps_per_mm_checksum CHECKSUM("beta_steps_per_mm_checksum");
38 #ifndef gamma_steps_per_mm_checksum
39 #define gamma_steps_per_mm_checksum CHECKSUM("gamma_steps_per_mm_checksum");
42 const float pi
= 3.14159265358979323846; // PI
43 const float two_pi
= 2 * pi
;
44 const float sin120
= 0.86602540378443864676372317075294; //sqrt3/2.0
45 const float cos120
= -0.5;
46 const float tan60
= 1.7320508075688772935274463415059; //sqrt3;
47 const float sin30
= 0.5;
48 const float tan30
= 0.57735026918962576450914878050196; //1/sqrt3
50 RotatableDeltaSolution::RotatableDeltaSolution(Config
* config
)
52 // End effector length
53 delta_e
= config
->value((unsigned int)delta_e_checksum
)->by_default(131.636F
)->as_number();
56 delta_f
= config
->value((unsigned int)delta_f_checksum
)->by_default(190.526F
)->as_number();
59 delta_re
= config
->value((unsigned int)delta_re_checksum
)->by_default(270.000F
)->as_number();
62 delta_rf
= config
->value((unsigned int)delta_rf_checksum
)->by_default(90.000F
)->as_number();
64 // Distance from delta 8mm rod/pulley to table/bed,
65 // NOTE: For OpenPnP, set the zero to be about 25mm above the bed..
66 delta_z_offset
= config
->value((unsigned int)delta_z_offset_checksum
)->by_default(290.700F
)->as_number();
68 // Ball joint plane to bottom of end effector surface
69 delta_ee_offs
= config
->value((unsigned int)delta_ee_offs_checksum
)->by_default(15.000F
)->as_number();
71 // Distance between end effector ball joint plane and tip of tool (PnP)
72 tool_offset
= config
->value((unsigned int)tool_offset_checksum
)->by_default(30.500F
)->as_number();
74 // This is the angle where the arms hit the endstop sensor
75 z_home_angle
= config
->value((unsigned int)z_home_angle_checksum
)->by_default(-60.000F
)->as_number();
77 // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers).
78 delta_printable_radius
= config
->value((unsigned int)delta_printable_radius_checksum
)->by_default(150.0F
)->as_number();
84 // helper functions, calculates angle theta1 (for YZ-pane)
85 int RotatableDeltaSolution::delta_calcAngleYZ(float x0
, float y0
, float z0
, float &theta
)
87 float y1
= -0.5F
* tan30
* delta_f
; // f/2 * tan 30
88 y0
-= 0.5F
* tan30
* delta_e
; // shift center to edge
90 float a
= (x0
*x0
+ y0
*y0
+ z0
*z0
+ delta_rf
*delta_rf
- delta_re
*delta_re
- y1
*y1
) / (2.0F
*z0
);
93 float d
= -(a
+b
*y1
)*(a
+b
*y1
) + delta_rf
*(b
*b
*delta_rf
+delta_rf
); // discriminant
94 if (d
< 0.0F
) return -1; // non-existing point
96 float yj
= (y1
- a
*b
- sqrtf(d
))/(b
*b
+ 1.0F
); // choosing outer point
99 theta
= 180.0F
*atanf(-zj
/(y1
- yj
))/pi
+ ((yj
>y1
)?180.0F
:0.0F
);
103 // forward kinematics: (theta1, theta2, theta3) -> (x0, y0, z0)
104 // returned status: 0=OK, -1=non-existing position
105 int RotatableDeltaSolution::delta_calcForward(float theta1
, float theta2
, float theta3
, float &x0
, float &y0
, float &z0
)
107 float t
= (delta_f
-delta_e
)*tan30
/2.0F
;
108 float degrees_to_radians
= pi
/180.0F
;
110 theta1
*= degrees_to_radians
;
111 theta2
*= degrees_to_radians
;
112 theta3
*= degrees_to_radians
;
114 float y1
= -(t
+ delta_rf
*cosf(theta1
));
115 float z1
= -delta_rf
*sinf(theta1
);
117 float y2
= (t
+ delta_rf
*cosf(theta2
))*sin30
;
119 float z2
= -delta_rf
*sinf(theta2
);
121 float y3
= (t
+ delta_rf
*cosf(theta3
))*sin30
;
122 float x3
= -y3
*tan60
;
123 float z3
= -delta_rf
*sinf(theta3
);
125 float dnm
= (y2
-y1
)*x3
-(y3
-y1
)*x2
;
127 float w1
= y1
*y1
+ z1
*z1
;
128 float w2
= x2
*x2
+ y2
*y2
+ z2
*z2
;
129 float w3
= x3
*x3
+ y3
*y3
+ z3
*z3
;
131 // x = (a1*z + b1)/dnm
132 float a1
= (z2
-z1
)*(y3
-y1
)-(z3
-z1
)*(y2
-y1
);
133 float b1
= -((w2
-w1
)*(y3
-y1
)-(w3
-w1
)*(y2
-y1
))/2.0F
;
135 // y = (a2*z + b2)/dnm;
136 float a2
= -(z2
-z1
)*x3
+(z3
-z1
)*x2
;
137 float b2
= ((w2
-w1
)*x3
- (w3
-w1
)*x2
)/2.0F
;
139 // a*z^2 + b*z + c = 0
140 float a
= a1
*a1
+ a2
*a2
+ dnm
*dnm
;
141 float b
= 2.0F
*(a1
*b1
+ a2
*(b2
-y1
*dnm
) - z1
*dnm
*dnm
);
142 float c
= (b2
-y1
*dnm
)*(b2
-y1
*dnm
) + b1
*b1
+ dnm
*dnm
*(z1
*z1
- delta_re
*delta_re
);
145 float d
= b
*b
- (float)4.0F
*a
*c
;
146 if (d
< 0.0F
) return -1; // non-existing point
148 z0
= -(float)0.5F
*(b
+sqrtf(d
))/a
;
149 x0
= (a1
*z0
+ b1
)/dnm
;
150 y0
= (a2
*z0
+ b2
)/dnm
;
152 z0
+= z_calc_offset
; //nj
157 void RotatableDeltaSolution::init() {
159 z_calc_offset
= (delta_z_offset
- tool_offset
- delta_ee_offs
)*-1.0F
;
161 // This is calculated from the Z_HOME_ANGLE angles specified in the config (file), after applying forward
162 // kinematics, and adding the Z calc offset to it.
163 z_home_offs
= (((delta_z_offset
- tool_offset
- delta_ee_offs
) - 182.002F
) - 0.5F
);
167 void RotatableDeltaSolution::cartesian_to_actuator( float cartesian_mm
[], float actuator_mm
[] )
169 //We need to translate the Cartesian coordinates in mm to the actuator position required in mm so the stepper motor functions
170 float alpha_theta
= 0.0F
;
171 float beta_theta
= 0.0F
;
172 float gamma_theta
= 0.0F
;
174 //Code from Trossen Robotics tutorial, note we put the X axis at the back and not the front of the robot.
176 float x0
= cartesian_mm
[X_AXIS
];
177 float y0
= cartesian_mm
[Y_AXIS
];
178 float z_with_offset
= cartesian_mm
[Z_AXIS
] + z_calc_offset
; //The delta calculation below places zero at the top. Subtract the Z offset to make zero at the bottom.
180 int status
= delta_calcAngleYZ(x0
, y0
, z_with_offset
, alpha_theta
);
181 if (status
== 0) status
= delta_calcAngleYZ(x0
*cos120
+ y0
*sin120
, y0
*cos120
-x0
*sin120
, z_with_offset
, beta_theta
); // rotate co-ordinates to +120 deg
182 if (status
== 0) status
= delta_calcAngleYZ(x0
*cos120
- y0
*sin120
, y0
*cos120
+x0
*sin120
, z_with_offset
, gamma_theta
); // rotate co-ordinates to -120 deg
184 if (status
== -1) //something went wrong
186 THEKERNEL
->streams
->printf("ERROR: Delta calculation fail! Unable to move to:\n");
187 THEKERNEL
->streams
->printf(" x= %f\n",cartesian_mm
[X_AXIS
]);
188 THEKERNEL
->streams
->printf(" y= %f\n",cartesian_mm
[Y_AXIS
]);
189 THEKERNEL
->streams
->printf(" z= %f\n",cartesian_mm
[Z_AXIS
]);
190 THEKERNEL
->streams
->printf(" CalcZ= %f\n",z_calc_offset
);
191 THEKERNEL
->streams
->printf(" Offz= %f\n",z_with_offset
);
195 actuator_mm
[ALPHA_STEPPER
] = alpha_theta
;
196 actuator_mm
[BETA_STEPPER
] = beta_theta
;
197 actuator_mm
[GAMMA_STEPPER
] = gamma_theta
;
199 // THEKERNEL->streams->printf("cartesian x= %f\n\r",cartesian_mm[X_AXIS]);
200 // THEKERNEL->streams->printf(" y= %f\n\r",cartesian_mm[Y_AXIS]);
201 // THEKERNEL->streams->printf(" z= %f\n\r",cartesian_mm[Z_AXIS]);
202 // THEKERNEL->streams->printf(" Offz= %f\n\r",z_with_offset);
203 // THEKERNEL->streams->printf(" delta x= %f\n\r",delta[X_AXIS]);
204 // THEKERNEL->streams->printf(" y= %f\n\r",delta[Y_AXIS]);
205 // THEKERNEL->streams->printf(" z= %f\n\r",delta[Z_AXIS]);
210 void RotatableDeltaSolution::actuator_to_cartesian( float actuator_mm
[], float cartesian_mm
[] )
212 //Use forward kinematics
213 delta_calcForward(actuator_mm
[ALPHA_STEPPER
], actuator_mm
[BETA_STEPPER
], actuator_mm
[GAMMA_STEPPER
], cartesian_mm
[X_AXIS
], cartesian_mm
[Y_AXIS
], cartesian_mm
[Z_AXIS
]);
216 bool RotatableDeltaSolution::set_optional(const arm_options_t
& options
) {
218 for(auto &i
: options
) {
220 case 'A': delta_e
= i
.second
; break;
221 case 'B': delta_f
= i
.second
; break;
222 case 'C': delta_re
= i
.second
; break;
223 case 'D': delta_rf
= i
.second
; break;
224 case 'E': delta_z_offset
= i
.second
; break;
225 case 'F': delta_ee_offs
= i
.second
; break;
226 case 'G': tool_offset
= i
.second
; break;
227 case 'H': z_home_angle
= i
.second
; break;
228 case 'I': delta_printable_radius
= i
.second
; break;
235 bool RotatableDeltaSolution::get_optional(arm_options_t
& options
) {
237 // don't report these if none of them are set
238 if(this->delta_e
!= 0.0F
|| this->delta_f
!= 0.0F
|| this->delta_re
!= 0.0F
||
239 this->delta_rf
!= 0.0F
|| this->delta_z_offset
!= 0.0F
|| this->delta_ee_offs
!= 0.0F
||
240 this->tool_offset
!= 0.0F
|| this->z_home_angle
!= 0.0F
|| this->delta_printable_radius
!= 0.0F
) {
242 options
['A'] = this->delta_e
;
243 options
['B'] = this->delta_f
;
244 options
['C'] = this->delta_re
;
245 options
['D'] = this->delta_rf
;
246 options
['E'] = this->delta_z_offset
;
247 options
['F'] = this->delta_ee_offs
;
248 options
['G'] = this->tool_offset
;
249 options
['H'] = this->z_home_angle
;
250 options
['I'] = this->delta_printable_radius
;