33#include " motor_addressing.h" // NUM_MOTORS
44
55#include < cmath>
6+ #include < cstdint>
67#include < limits>
78#include < string>
89#include < vector>
@@ -63,6 +64,14 @@ struct MotorConfig {
6364 // Stored here so the HMI can display it in the Motor Config panel.
6465 float gear_reduction = 1 .0f ;
6566
67+ // Datasheet identity (Maxon). poles/kv feed calibration flags — these are
68+ // PER MOTOR: calibrating with the wrong pole count mis-commutates the
69+ // motor (low torque at max current, siren whine, angle-dependent gains).
70+ int poles = 16 ; // pole count (pole pairs x 2)
71+ float kv = 265 .0f ;
72+ float nominal_current_A = 1 .0f ; // continuous thermal rating
73+ const char * part_no = " ?" ;
74+
6675 // -------------------------------------------------------------------------
6776 // Helpers used by configureMotor()
6877 // -------------------------------------------------------------------------
@@ -99,14 +108,18 @@ struct MotorConfig {
99108inline std::vector<MotorConfig> get_arm_configuration () {
100109 std::vector<MotorConfig> motors (NUM_MOTORS );
101110
111+ // --- Synced from controller flash 2026-08-02 (conf get readback) so the
112+ // repo matches the bench tuning. A7/EE was not replying; it keeps
113+ // struct defaults until it can be read.
114+
102115 // --- PID gains (hand-tuned per axis) ---
103116 // Higher kp = stiffer. Add kd to dampen oscillation.
104- motors[0 ].kp = 180 .0f ; motors[0 ].kd = 40 .0f ; // Base
105- motors[1 ].kp = 2100 .0f ; motors[1 ].kd = 100 .0f ; // Shoulder
106- motors[2 ].kp = 4000 .0f ; motors[2 ].kd = 750 .0f ; // Elbow
107- motors[3 ].kp = 50 .0f ; motors[3 ].kd = 0 .0f ; // Wrist Pitch
108- motors[4 ].kp = 600 .0f ; motors[4 ].kd = 100 .0f ; // Wrist Roll
109- motors[5 ].kp = 600 .0f ; motors[5 ].kd = 100 .0f ; // End Effector
117+ motors[0 ].kp = 4000 .0f ; motors[0 ].kd = 600 .0f ; // A1
118+ motors[1 ].kp = 30000 .0f ; motors[1 ].kd = 6000 .0f ; // A2
119+ motors[2 ].kp = 40000 .0f ; motors[2 ].kd = 2000 .0f ; // A3
120+ motors[3 ].kp = 550 .0f ; motors[3 ].kd = 10 .0f ; // A4
121+ motors[4 ].kp = 17000 .0f ; motors[4 ].kd = 3500 .0f ; // A5
122+ motors[5 ].kp = 17000 .0f ; motors[5 ].kd = 3500 .0f ; // A6
110123
111124 // --- Gear reductions (for display in HMI only — firmware holds the real value) ---
112125 motors[0 ].gear_reduction = 1 .0f / 190 .0f ;
@@ -115,23 +128,56 @@ inline std::vector<MotorConfig> get_arm_configuration() {
115128 motors[3 ].gear_reduction = 1 .0f / 190 .0f ;
116129 motors[4 ].gear_reduction = 1 .0f / 66 .0f ;
117130 motors[5 ].gear_reduction = 1 .0f / 66 .0f ;
131+ motors[6 ].gear_reduction = 1 .0f / 190 .0f ; // EE
132+
133+ // --- Datasheet identity (bench notes / Maxon, 2026-08) ---
134+ // A2/A3 are 22-pole kv=134 — calibration flags MUST come from here, not
135+ // the old hardcoded 16/265 (that mis-commutates them).
136+ const char * parts[NUM_MOTORS ] = {" 339281" ," 607942" ," 515458" ," 339281" ," 651607" ," 651607" ," 339281" };
137+ const int poles[NUM_MOTORS ] = { 16 , 22 , 22 , 16 , 16 , 16 , 16 };
138+ const float kvs [NUM_MOTORS ] = { 265 , 134 , 134 , 187 , 265 , 265 , 265 };
139+ const float noms [NUM_MOTORS ] = { 1 .01f , 9 .5f , 6 .44f , 1 .01f , 2 .52f , 2 .52f , 1 .01f };
140+ for (int i = 0 ; i < NUM_MOTORS ; i++) {
141+ motors[i].part_no = parts[i];
142+ motors[i].poles = poles[i];
143+ motors[i].kv = kvs[i];
144+ motors[i].nominal_current_A = noms[i];
145+ }
118146
119147 // --- Current limits (A) ---
120- motors[0 ].max_current_A = 1 .0f ;
121- motors[1 ].max_current_A = 8 .0f ;
122- motors[2 ].max_current_A = 5 .5f ;
123- motors[3 ].max_current_A = 0 .5f ;
124- motors[4 ].max_current_A = 2 .5f ;
125- motors[5 ].max_current_A = 2 .5f ;
126-
127- // --- Software position limits (output-shaft revolutions) ---
128- motors[0 ].position_min = -0 .3f ; motors[0 ].position_max = 0 .3f ;
129- motors[1 ].position_min = -0 .47f ; motors[1 ].position_max = -0 .05f ;
130- motors[2 ].position_min = 0 .01f ; motors[2 ].position_max = 0 .4f ;
131- // motors[3]: uses default (-1.0, 1.0)
132- // TODO this is positions for motors, not motors
133- motors[4 ].position_min = -999 .01f ; motors[4 ].position_max = 999 .4f ; // continuous
134- motors[5 ].position_min = -999 .01f ; motors[5 ].position_max = 999 .4f ; // continuous
148+ motors[0 ].max_current_A = 2 .0f ; // back to 2.0 (2026-08-03); was 3.0 after the 08-02 stall
149+ motors[1 ].max_current_A = 14 .0f ;
150+ motors[2 ].max_current_A = 14 .0f ;
151+ motors[3 ].max_current_A = 1 .01f ; // 200142 nominal 2.14 A; 0.3 starved it (bench 2026-08-03)
152+ motors[4 ].max_current_A = 2 .0f ;
153+ motors[5 ].max_current_A = 2 .0f ;
154+
155+ // --- Voltage / power limits ---
156+ const float fnan = std::numeric_limits<float >::quiet_NaN ();
157+ motors[0 ].max_voltage = 30 .0f ; motors[0 ].max_power_W = 200 .0f ;
158+ motors[1 ].max_voltage = 30 .0f ; motors[1 ].max_power_W = fnan;
159+ motors[2 ].max_voltage = 32 .0f ; motors[2 ].max_power_W = 250 .0f ;
160+ motors[3 ].max_voltage = 24 .0f ; motors[3 ].max_power_W = 30 .0f ;
161+ motors[4 ].max_voltage = 30 .0f ; motors[4 ].max_power_W = fnan;
162+ motors[5 ].max_voltage = 30 .0f ; motors[5 ].max_power_W = fnan;
163+
164+ // --- Motion profile ---
165+ motors[0 ].max_acceleration = 0 .5f ; motors[0 ].max_velocity = 0 .01f ;
166+ motors[1 ].max_acceleration = 0 .5f ; motors[1 ].max_velocity = 0 .03f ;
167+ motors[2 ].max_acceleration = 0 .5f ; motors[2 ].max_velocity = 0 .05f ;
168+ motors[3 ].max_acceleration = 0 .5f ; motors[3 ].max_velocity = 0 .10f ;
169+ motors[4 ].max_acceleration = 1 .0f ; motors[4 ].max_velocity = 0 .05f ;
170+ motors[5 ].max_acceleration = 1 .0f ; motors[5 ].max_velocity = 0 .05f ;
171+
172+ // --- Position limits (output-shaft revolutions) ---
173+ // Flash has servopos unbounded (nan) everywhere except A4 — adopted
174+ // as-is; homing/soft-stop bounds live in AxisConfig, not here.
175+ for (int i : {0 , 1 , 2 , 4 , 5 }) {
176+ motors[i].position_min = fnan;
177+ motors[i].position_max = fnan;
178+ }
179+ motors[3 ].position_min = -0 .5f ; motors[3 ].position_max = 0 .5f ;
180+ motors[3 ].def_timeout = 0 .1f ;
135181
136182 return motors;
137183}
@@ -176,7 +222,7 @@ namespace AxisConfig {
176222 /* AXIS 1 */ 0.1 ,
177223 /* AXIS 2 */ 0.1 ,
178224 /* AXIS 3 */ 0.1 ,
179- /* AXIS 4 */ 0.1 , // DISABLED
225+ /* AXIS 4 */ 0.1 ,
180226 /* AXIS 5 */ 0.1 ,
181227 /* AXIS 6 */ 0.1 ,
182228 /* AXIS EE */ 0.5 };
@@ -190,14 +236,33 @@ namespace AxisConfig {
190236 /* AXIS 6 */ 0.5 ,
191237 /* AXIS EE */ 0.5 };
192238
193- float idle_position[NUM_AXES ] = { // Rev/s Direction Independent.
239+ float idle_position[NUM_AXES ] = { // Rev/s Direction Independent.
194240 /* AXIS 1 */ 0.1 ,
195241 /* AXIS 2 */ 0.1 ,
196242 /* AXIS 3 */ 0.1 ,
197243 /* AXIS 4 */ 0.1 ,
198244 /* AXIS 5 */ -0.1 ,
199245 /* AXIS 6 */ 0.1 ,
200246 /* AXIS EE */ 0.1 };
201-
202247
203- };
248+ // Limit switches — AUX2 digital input, see setup_limit_switches.py.
249+ // mask: aux2 GPIO status bitfield, bit0 = aux2.pins.0 (ABS connector pin 2).
250+ // inverted=false: NC switch to GND (hard 2k pullup) -> pin HIGH = pressed.
251+ bool has_limit_switch[NUM_AXES ] = {
252+ /* AXIS 1 */ true ,
253+ /* AXIS 2 */ true ,
254+ /* AXIS 3 */ true ,
255+ /* AXIS 4 */ true ,
256+ /* AXIS 5 */ false ,
257+ /* AXIS 6 */ false ,
258+ /* AXIS EE */ false };
259+
260+ // Per-axis, measured with DEBUG_LIMIT_SWITCH_RAW_REPLY (2026-08-02).
261+ // All four: pressed = HIGH; only the pin differs (A1/A3 pin 0, A2/A4 pin 1).
262+ // A1's first capture was misread as inverted — the arm was resting on its
263+ // switch, so the observed transitions were releases, not presses.
264+ // A3's pin has no pull; if it flakes: conf set aux2.pins.0.pull 2 on motor 3.
265+ uint8_t limit_switch_mask[NUM_AXES ] = {1 , 2 , 1 , 2 , 0 , 0 , 0 };
266+ bool limit_switch_inverted[NUM_AXES ] = {false , false , false , false , false , false , false };
267+
268+ };
0 commit comments