Commit | Line | Data |
---|---|---|
2a06c415 | 1 | #include "LinearDeltaSolution.h" |
7af0714f | 2 | #include "checksumm.h" |
8d54c34c | 3 | #include "ConfigValue.h" |
681a62d7 | 4 | #include "libs/Kernel.h" |
681a62d7 | 5 | #include "libs/nuts_bolts.h" |
681a62d7 | 6 | #include "libs/Config.h" |
f7f4a2f5 JM |
7 | |
8 | #include <fastmath.h> | |
b00bb4b5 MM |
9 | #include "Vector3.h" |
10 | ||
f7f4a2f5 | 11 | |
681a62d7 JM |
12 | #define arm_length_checksum CHECKSUM("arm_length") |
13 | #define arm_radius_checksum CHECKSUM("arm_radius") | |
14 | ||
f7f4a2f5 JM |
15 | #define tower1_offset_checksum CHECKSUM("delta_tower1_offset") |
16 | #define tower2_offset_checksum CHECKSUM("delta_tower2_offset") | |
17 | #define tower3_offset_checksum CHECKSUM("delta_tower3_offset") | |
18 | #define tower1_angle_checksum CHECKSUM("delta_tower1_angle") | |
19 | #define tower2_angle_checksum CHECKSUM("delta_tower2_angle") | |
20 | #define tower3_angle_checksum CHECKSUM("delta_tower3_angle") | |
681a62d7 | 21 | |
ec4773e5 | 22 | #define SQ(x) powf(x, 2) |
4fed9ba1 | 23 | #define ROUND(x, y) (roundf(x * (float)(1e ## y)) / (float)(1e ## y)) |
f7f4a2f5 | 24 | #define PIOVER180 0.01745329251994329576923690768489F |
2c7ab192 | 25 | |
2a06c415 | 26 | LinearDeltaSolution::LinearDeltaSolution(Config* config) |
78d0e16a | 27 | { |
2c7ab192 | 28 | // arm_length is the length of the arm from hinge to hinge |
f7f4a2f5 | 29 | arm_length = config->value(arm_length_checksum)->by_default(250.0f)->as_number(); |
2c7ab192 | 30 | // arm_radius is the horizontal distance from hinge to hinge when the effector is centered |
f7f4a2f5 JM |
31 | arm_radius = config->value(arm_radius_checksum)->by_default(124.0f)->as_number(); |
32 | ||
33 | tower1_angle = config->value(tower1_angle_checksum)->by_default(0.0f)->as_number(); | |
34 | tower2_angle = config->value(tower2_angle_checksum)->by_default(0.0f)->as_number(); | |
35 | tower3_angle = config->value(tower3_angle_checksum)->by_default(0.0f)->as_number(); | |
36 | tower1_offset = config->value(tower1_offset_checksum)->by_default(0.0f)->as_number(); | |
37 | tower2_offset = config->value(tower2_offset_checksum)->by_default(0.0f)->as_number(); | |
38 | tower3_offset = config->value(tower3_offset_checksum)->by_default(0.0f)->as_number(); | |
2c7ab192 | 39 | |
ec4773e5 JM |
40 | init(); |
41 | } | |
42 | ||
2a06c415 | 43 | void LinearDeltaSolution::init() { |
78d0e16a | 44 | arm_length_squared = SQ(arm_length); |
ec4773e5 | 45 | |
f7f4a2f5 JM |
46 | // Effective X/Y positions of the three vertical towers. |
47 | float delta_radius = arm_radius; | |
2c7ab192 | 48 | |
f7f4a2f5 JM |
49 | delta_tower1_x = (delta_radius + tower1_offset) * cosf((210.0F + tower1_angle) * PIOVER180); // front left tower |
50 | delta_tower1_y = (delta_radius + tower1_offset) * sinf((210.0F + tower1_angle) * PIOVER180); | |
51 | delta_tower2_x = (delta_radius + tower2_offset) * cosf((330.0F + tower2_angle) * PIOVER180); // front right tower | |
52 | delta_tower2_y = (delta_radius + tower2_offset) * sinf((330.0F + tower2_angle) * PIOVER180); | |
53 | delta_tower3_x = (delta_radius + tower3_offset) * cosf((90.0F + tower3_angle) * PIOVER180); // back middle tower | |
54 | delta_tower3_y = (delta_radius + tower3_offset) * sinf((90.0F + tower3_angle) * PIOVER180); | |
2c7ab192 JM |
55 | } |
56 | ||
3cc3f421 | 57 | void LinearDeltaSolution::cartesian_to_actuator(const float cartesian_mm[], float actuator_mm[] ) |
b00bb4b5 | 58 | { |
f7f4a2f5 | 59 | |
01cf96fb | 60 | actuator_mm[ALPHA_STEPPER] = sqrtf(this->arm_length_squared |
f7f4a2f5 JM |
61 | - SQ(delta_tower1_x - cartesian_mm[X_AXIS]) |
62 | - SQ(delta_tower1_y - cartesian_mm[Y_AXIS]) | |
63 | ) + cartesian_mm[Z_AXIS]; | |
01cf96fb | 64 | actuator_mm[BETA_STEPPER ] = sqrtf(this->arm_length_squared |
f7f4a2f5 JM |
65 | - SQ(delta_tower2_x - cartesian_mm[X_AXIS]) |
66 | - SQ(delta_tower2_y - cartesian_mm[Y_AXIS]) | |
67 | ) + cartesian_mm[Z_AXIS]; | |
01cf96fb | 68 | actuator_mm[GAMMA_STEPPER] = sqrtf(this->arm_length_squared |
f7f4a2f5 JM |
69 | - SQ(delta_tower3_x - cartesian_mm[X_AXIS]) |
70 | - SQ(delta_tower3_y - cartesian_mm[Y_AXIS]) | |
71 | ) + cartesian_mm[Z_AXIS]; | |
78d0e16a MM |
72 | } |
73 | ||
3cc3f421 | 74 | void LinearDeltaSolution::actuator_to_cartesian(const float actuator_mm[], float cartesian_mm[] ) |
b00bb4b5 MM |
75 | { |
76 | // from http://en.wikipedia.org/wiki/Circumscribed_circle#Barycentric_coordinates_from_cross-_and_dot-products | |
77 | // based on https://github.com/ambrop72/aprinter/blob/2de69a/aprinter/printer/DeltaTransform.h#L81 | |
f7f4a2f5 JM |
78 | Vector3 tower1( delta_tower1_x, delta_tower1_y, actuator_mm[0] ); |
79 | Vector3 tower2( delta_tower2_x, delta_tower2_y, actuator_mm[1] ); | |
80 | Vector3 tower3( delta_tower3_x, delta_tower3_y, actuator_mm[2] ); | |
b00bb4b5 | 81 | |
614b5947 MM |
82 | Vector3 s12 = tower1.sub(tower2); |
83 | Vector3 s23 = tower2.sub(tower3); | |
84 | Vector3 s13 = tower1.sub(tower3); | |
b00bb4b5 | 85 | |
614b5947 | 86 | Vector3 normal = s12.cross(s23); |
b00bb4b5 MM |
87 | |
88 | float magsq_s12 = s12.magsq(); | |
89 | float magsq_s23 = s23.magsq(); | |
90 | float magsq_s13 = s13.magsq(); | |
91 | ||
92 | float inv_nmag_sq = 1.0F / normal.magsq(); | |
93 | float q = 0.5F * inv_nmag_sq; | |
94 | ||
95 | float a = q * magsq_s23 * s12.dot(s13); | |
96 | float b = q * magsq_s13 * s12.dot(s23) * -1.0F; // negate because we use s12 instead of s21 | |
97 | float c = q * magsq_s12 * s13.dot(s23); | |
98 | ||
f7f4a2f5 JM |
99 | Vector3 circumcenter( delta_tower1_x * a + delta_tower2_x * b + delta_tower3_x * c, |
100 | delta_tower1_y * a + delta_tower2_y * b + delta_tower3_y * c, | |
614b5947 | 101 | actuator_mm[0] * a + actuator_mm[1] * b + actuator_mm[2] * c ); |
b00bb4b5 MM |
102 | |
103 | float r_sq = 0.5F * q * magsq_s12 * magsq_s23 * magsq_s13; | |
104 | float dist = sqrtf(inv_nmag_sq * (arm_length_squared - r_sq)); | |
105 | ||
614b5947 | 106 | Vector3 cartesian = circumcenter.sub(normal.mul(dist)); |
b00bb4b5 MM |
107 | |
108 | cartesian_mm[0] = ROUND(cartesian[0], 4); | |
109 | cartesian_mm[1] = ROUND(cartesian[1], 4); | |
110 | cartesian_mm[2] = ROUND(cartesian[2], 4); | |
2c7ab192 JM |
111 | } |
112 | ||
2a06c415 | 113 | bool LinearDeltaSolution::set_optional(const arm_options_t& options) { |
b7cd847e | 114 | |
dde682fd JM |
115 | for(auto &i : options) { |
116 | switch(i.first) { | |
117 | case 'L': arm_length= i.second; break; | |
118 | case 'R': arm_radius= i.second; break; | |
f7f4a2f5 JM |
119 | case 'A': tower1_offset = i.second; break; |
120 | case 'B': tower2_offset = i.second; break; | |
121 | case 'C': tower3_offset = i.second; break; | |
122 | case 'D': tower1_angle = i.second; break; | |
123 | case 'E': tower2_angle = i.second; break; | |
69e08265 JM |
124 | case 'F': tower3_angle = i.second; break; // WARNING this will be deprecated |
125 | case 'H': tower3_angle = i.second; break; | |
dde682fd | 126 | } |
ec4773e5 | 127 | } |
78d0e16a | 128 | init(); |
ec4773e5 JM |
129 | return true; |
130 | } | |
131 | ||
51e6a11d | 132 | bool LinearDeltaSolution::get_optional(arm_options_t& options, bool force_all) { |
b7cd847e JM |
133 | options['L']= this->arm_length; |
134 | options['R']= this->arm_radius; | |
88852a71 JM |
135 | |
136 | // don't report these if none of them are set | |
51e6a11d JM |
137 | if(force_all || (this->tower1_offset != 0.0F || this->tower2_offset != 0.0F || this->tower3_offset != 0.0F || |
138 | this->tower1_angle != 0.0F || this->tower2_angle != 0.0F || this->tower3_angle != 0.0F) ) { | |
88852a71 JM |
139 | |
140 | options['A'] = this->tower1_offset; | |
141 | options['B'] = this->tower2_offset; | |
142 | options['C'] = this->tower3_offset; | |
143 | options['D'] = this->tower1_angle; | |
144 | options['E'] = this->tower2_angle; | |
69e08265 | 145 | options['H'] = this->tower3_angle; |
88852a71 | 146 | } |
f7f4a2f5 | 147 | |
ec4773e5 JM |
148 | return true; |
149 | }; |