SimpleFOClibrary 2.4.0
Loading...
Searching...
No Matches
FOCMotor.cpp
Go to the documentation of this file.
1#include "FOCMotor.h"
2#include "../../communication/SimpleFOCDebug.h"
3
4/**
5 * Default constructor - setting all variabels to default values
6 */
8{
9 // maximum angular velocity to be used for positioning
11 // maximum voltage to be set to the motor
13 // not set on the begining
15
16 // index search velocity
18 // sensor and motor align voltage
20
21 // default modulation is SinePWM
23
24 // default target value
25 target = 0;
26 voltage.d = 0;
27 voltage.q = 0;
28 // current target values
29 current_sp = 0;
30 current.q = 0;
31 current.d = 0;
32
33 // voltage bemf
34 voltage_bemf = 0;
35
36 // Initialize phase voltages U alpha and U beta used for inverse Park and Clarke transform
37 Ualpha = 0;
38 Ubeta = 0;
39
40 //monitor_port
41 monitor_port = nullptr;
42 //sensor
43 sensor_offset = 0.0f;
44 sensor = nullptr;
45 //current sensor
46 current_sense = nullptr;
47}
48
49
50/**
51 Sensor linking method
52*/
54 sensor = _sensor;
55}
56
57/**
58 CurrentSense linking method
59*/
61 current_sense = _current_sense;
62}
63
64// shaft angle calculation
66 // if no sensor linked return previous value ( for open loop )
67 if(!sensor) return shaft_angle;
69}
70// shaft velocity calculation
72 // if no sensor linked return previous value ( for open loop )
73 if(!sensor) return shaft_velocity;
75}
76
78 // if no sensor linked return previous value ( for open loop )
79 if(!sensor) return electrical_angle;
81}
82
83/**
84 * Monitoring functions
85 */
86// function implementing the monitor_port setter
87void FOCMotor::useMonitoring(Print &print){
88 monitor_port = &print; //operate on the address of print
89 #ifndef SIMPLEFOC_DISABLE_DEBUG
91 SIMPLEFOC_MOTOR_DEBUG("Monitor enabled!");
92 #endif
93}
94
95// Measure resistance and inductance of a motor
96int FOCMotor::characteriseMotor(float voltage, float correction_factor=1.0f){
97 if (!this->current_sense || !this->current_sense->initialized)
98 {
99 SIMPLEFOC_MOTOR_ERROR("Fail. CS not init.");
100 return 1;
101 }
102
103 if (voltage <= 0.0f){
104 SIMPLEFOC_MOTOR_ERROR("Fail. Volt. <= 0");
105 return 2;
106 }
108
109 SIMPLEFOC_MOTOR_DEBUG("Meas R..");
110
111 float current_electric_angle = electricalAngle();
112
113 // Apply zero volts and measure a zero reference
114 setPhaseVoltage(0, 0, current_electric_angle);
115 _delay(500);
116
118 DQCurrent_s zerocurrent = current_sense->getDQCurrents(current_sense->getABCurrents(zerocurrent_raw), current_electric_angle);
119
120
121 // Ramp and hold the voltage to measure resistance
122 // 300 ms of ramping
123 current_electric_angle = electricalAngle();
124 for(int i=0; i < 100; i++){
125 setPhaseVoltage(0, voltage/100.0*((float)i), current_electric_angle);
126 _delay(3);
127 }
128 _delay(10);
130 DQCurrent_s r_currents = current_sense->getDQCurrents(current_sense->getABCurrents(r_currents_raw), current_electric_angle);
131
132 // Zero again
133 setPhaseVoltage(0, 0, current_electric_angle);
134
135
136 if (fabsf(r_currents.d - zerocurrent.d) < 0.2f)
137 {
138 SIMPLEFOC_MOTOR_ERROR("Fail. current too low");
139 return 3;
140 }
141
142 float resistance = voltage / (correction_factor * (r_currents.d - zerocurrent.d));
143 if (resistance <= 0.0f)
144 {
145 SIMPLEFOC_MOTOR_ERROR("Fail. Est. R<= 0");
146 return 4;
147 }
148
149 SIMPLEFOC_MOTOR_DEBUG("Est. R: ", 2.0f * resistance);
150 _delay(100);
151
152
153 // Start inductance measurement
154 SIMPLEFOC_MOTOR_DEBUG("Meas L...");
155
156 unsigned long t0 = 0;
157 unsigned long t1 = 0;
158 float Ltemp = 0;
159 float Ld = 0;
160 float Lq = 0;
161 float d_electrical_angle = 0;
162
163 unsigned int iterations = 40; // how often the algorithm gets repeated.
164 unsigned int cycles = 3; // averaged measurements for each iteration
165 unsigned int risetime_us = 200; // initially short for worst case scenario with low inductance
166 unsigned int settle_us = 100000; // initially long for worst case scenario with high inductance
167
168 // Pre-rotate the angle to the q-axis (only useful with sensor, else no harm in doing it)
169 current_electric_angle += 0.5f * _PI;
170 current_electric_angle = _normalizeAngle(current_electric_angle);
171
172 for (size_t axis = 0; axis < 2; axis++)
173 {
174 for (size_t i = 0; i < iterations; i++)
175 {
176 // current_electric_angle = i * _2PI / iterations; // <-- Do a sweep of the inductance. Use eg. for graphing
177 float inductanced = 0.0f;
178
179 float qcurrent = 0.0f;
180 float dcurrent = 0.0f;
181 for (size_t j = 0; j < cycles; j++)
182 {
183 // read zero current
184 zerocurrent_raw = current_sense->readAverageCurrents(20);
185 zerocurrent = current_sense->getDQCurrents(current_sense->getABCurrents(zerocurrent_raw), current_electric_angle);
186
187 // step the voltage
188 setPhaseVoltage(0, voltage, current_electric_angle);
189 t0 = micros();
190 delayMicroseconds(risetime_us); // wait a little bit
191
193 t1 = micros();
194 setPhaseVoltage(0, 0, current_electric_angle);
195
196 DQCurrent_s l_currents = current_sense->getDQCurrents(current_sense->getABCurrents(l_currents_raw), current_electric_angle);
197 delayMicroseconds(settle_us); // wait a bit for the currents to go to 0 again
198
199 if (t0 > t1) continue; // safety first
200
201 // calculate the inductance
202 float dt = (t1 - t0)/1000000.0f;
203 if (l_currents.d - zerocurrent.d <= 0 || (voltage - resistance * (l_currents.d - zerocurrent.d)) <= 0)
204 {
205 continue;
206 }
207
208 inductanced += fabsf(- (resistance * dt) / log((voltage - resistance * (l_currents.d - zerocurrent.d)) / voltage))/correction_factor;
209
210 qcurrent+= l_currents.q - zerocurrent.q; // average the measured currents
211 dcurrent+= l_currents.d - zerocurrent.d;
212 }
213 qcurrent /= cycles;
214 dcurrent /= cycles;
215
216 float delta = qcurrent / (fabsf(dcurrent) + fabsf(qcurrent));
217
218
219 inductanced /= cycles;
220 Ltemp = i < 2 ? inductanced : Ltemp * 0.6 + inductanced * 0.4;
221
222 float timeconstant = fabsf(Ltemp / resistance); // Timeconstant of an RL circuit (L/R)
223 // SIMPLEFOC_MOTOR_DEBUG("Estimated time constant in us: ", 1000000.0f * timeconstant);
224
225 // Wait as long as possible (due to limited timing accuracy & sample rate), but as short as needed (while the current still changes)
226 risetime_us = _constrain(risetime_us * 0.6f + 0.4f * 1000000 * 0.6f * timeconstant, 100, 10000);
227 settle_us = 10 * risetime_us;
228
229
230 // Serial.printf(">inductance:%f:%f|xy\n", current_electric_angle, Ltemp * 1000.0f); // <-- Plot an angle sweep
231
232
233 /**
234 * How this code works:
235 * If we apply a current spike in the d´-axis, there will be cross coupling to the q´-axis current, if we didn´t use the actual d-axis (ie. d´ != d).
236 * This has to do with saliency (Ld != Lq).
237 * The amount of cross coupled current is somewhat proportional to the angle error, which means that if we iteratively change the angle to min/maximise this current, we get the correct d-axis (and q-axis).
238 */
239 if (axis)
240 {
241 qcurrent *= -1.0f; // to d or q axis
242 }
243
244 if (qcurrent < 0)
245 {
246 current_electric_angle+=fabsf(delta);
247 } else
248 {
249 current_electric_angle-=fabsf(delta);
250 }
251 current_electric_angle = _normalizeAngle(current_electric_angle);
252
253
254 // Average the d-axis angle further for calculating the electrical zero later
255 if (axis)
256 {
257 d_electrical_angle = i < 2 ? current_electric_angle : d_electrical_angle * 0.9 + current_electric_angle * 0.1;
258 }
259
260 }
261
262 // We know the minimum is 0.5*PI radians away, so less iterations are needed.
263 current_electric_angle += 0.5f * _PI;
264 current_electric_angle = _normalizeAngle(current_electric_angle);
265 iterations /= 2;
266
267 if (axis == 0)
268 {
269 Lq = Ltemp;
270 }else
271 {
272 Ld = Ltemp;
273 }
274
275 }
276
277 if (sensor)
278 {
279 /**
280 * The d_electrical_angle should now be aligned to the d axis or the -d axis. We can therefore calculate two possible electrical zero angles.
281 * We then report the one closest to the actual value. This could be useful if the zero search method is not reliable enough (eg. high pole count).
282 */
283
284 float estimated_zero_electric_angle_A = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() - d_electrical_angle);
285 float estimated_zero_electric_angle_B = _normalizeAngle( (float)(sensor_direction * pole_pairs) * sensor->getMechanicalAngle() - d_electrical_angle + _PI);
286 float estimated_zero_electric_angle = 0.0f;
287 if (fabsf(estimated_zero_electric_angle_A - zero_electric_angle) < fabsf(estimated_zero_electric_angle_B - zero_electric_angle))
288 {
289 estimated_zero_electric_angle = estimated_zero_electric_angle_A;
290 } else
291 {
292 estimated_zero_electric_angle = estimated_zero_electric_angle_B;
293 }
294
295 SIMPLEFOC_MOTOR_DEBUG("New el. zero: ", estimated_zero_electric_angle);
296 SIMPLEFOC_MOTOR_DEBUG("Curr. el. zero: ", zero_electric_angle);
297 }
298
299
300 SIMPLEFOC_MOTOR_DEBUG("Ld [mH]: ", Ld * 1000.0f);
301 SIMPLEFOC_MOTOR_DEBUG("Lq [mH]: ", Lq * 1000.0f);
302 if (Ld > Lq)
303 {
304 SIMPLEFOC_MOTOR_WARN("Ld>Lq. Likely error.");
305 }
306 if (Ld * 2.0f < Lq)
307 {
308 SIMPLEFOC_MOTOR_WARN("Lq > 2*Ld. Likely error.");
309 }
310
311 // store the measured values
312 phase_resistance = 2.0f * resistance;
313 axis_inductance = {Ld, Lq};
314 phase_inductance = (Ld + Lq) / 2.0f; // FOR BACKWARDS COMPATIBILITY
315 return 0;
316
317}
318
319// utility function intended to be used with serial plotter to monitor motor variables
320// significantly slowing the execution down!!!!
322 if( !monitor_downsample || monitor_cnt++ < (monitor_downsample-1) ) return;
323 monitor_cnt = 0;
324 if(!monitor_port) return;
325 bool printed = 0;
326
328 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
330 printed= true;
331 }
333 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
334 else if(printed) monitor_port->print(monitor_separator);
336 printed= true;
337 }
339 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
340 else if(printed) monitor_port->print(monitor_separator);
342 printed= true;
343 }
344 // read currents if possible - even in voltage mode (if current_sense available)
349 c.q = LPF_current_q(c.q);
350 c.d = LPF_current_d(c.d);
351 }
353 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
354 else if(printed) monitor_port->print(monitor_separator);
355 monitor_port->print(c.q*1000, monitor_decimals); // mAmps
356 printed= true;
357 }
359 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
360 else if(printed) monitor_port->print(monitor_separator);
361 monitor_port->print(c.d*1000, monitor_decimals); // mAmps
362 printed= true;
363 }
364 }
365
367 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
368 else if(printed) monitor_port->print(monitor_separator);
370 printed= true;
371 }
373 if(!printed && monitor_start_char) monitor_port->print(monitor_start_char);
374 else if(printed) monitor_port->print(monitor_separator);
376 printed= true;
377 }
378 if(printed){
380 else monitor_port->println("");
381 }
382}
383
384
385
386// Function (iterative) generating open loop movement for target velocity
387// - target_velocity - rad/s
388// it uses voltage_limit variable
389float FOCMotor::velocityOpenloop(float target_velocity){
390 // get current timestamp
391 unsigned long now_us = _micros();
392 // calculate the sample time from last call
393 float Ts = (now_us - open_loop_timestamp) * 1e-6f;
394 // quick fix for strange cases (micros overflow + timestamp not defined)
395 if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;
396
397 // calculate the necessary angle to achieve target velocity
398 shaft_angle = _normalizeAngle(shaft_angle + target_velocity*Ts);
399 // for display purposes
400 shaft_velocity = target_velocity;
401
402 // save timestamp for next call
403 open_loop_timestamp = now_us;
404
406 return voltage_limit;
407 else
408 return current_limit;
409}
410
411// Function (iterative) generating open loop movement towards the target angle
412// - target_angle - rad
413// it uses voltage_limit and velocity_limit variables
414float FOCMotor::angleOpenloop(float target_angle){
415 // get current timestamp
416 unsigned long now_us = _micros();
417 // calculate the sample time from last call
418 float Ts = (now_us - open_loop_timestamp) * 1e-6f;
419 // quick fix for strange cases (micros overflow + timestamp not defined)
420 if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;
421
422 // calculate the necessary angle to move from current position towards target angle
423 // with maximal velocity (velocity_limit)
424 // TODO sensor precision: this calculation is not numerically precise. The angle can grow to the point
425 // where small position changes are no longer captured by the precision of floats
426 // when the total position is large.
427 if(abs( target_angle - shaft_angle ) > abs(velocity_limit*Ts)){
428 shaft_angle += _sign(target_angle - shaft_angle) * abs( velocity_limit )*Ts;
430 }else{
431 shaft_angle = target_angle;
432 shaft_velocity = 0;
433 }
434
435 // save timestamp for next call
436 open_loop_timestamp = now_us;
437
438 // use voltage limit or current limit
440 return voltage_limit;
441 else
442 return current_limit;
443}
444
445// Update limit values in controllers when changed
446void FOCMotor::updateVelocityLimit(float new_velocity_limit) {
447 velocity_limit = new_velocity_limit;
449 P_angle.limit = abs(velocity_limit); // if angle control but no velocity cascade, limit the angle controller by the velocity limit
450}
451
452// Update limit values in controllers when changed
453void FOCMotor::updateCurrentLimit(float new_current_limit) {
454 current_limit = new_current_limit;
456 // if current control
457 PID_velocity.limit = new_current_limit;
459 // if angle control but no velocity cascade, limit the angle controller by the current limit
460 P_angle.limit = new_current_limit;
461 }
462}
463
464// Update limit values in controllers when changed
465// PID values and limits
466void FOCMotor::updateVoltageLimit(float new_voltage_limit) {
467 voltage_limit = new_voltage_limit;
468 PID_current_q.limit = new_voltage_limit;
469 PID_current_d.limit = new_voltage_limit;
471 // if voltage control
472 PID_velocity.limit = new_voltage_limit;
474 // if angle control but no velocity cascade, limit the angle controller by the voltage limit
475 P_angle.limit = new_voltage_limit;
476 }
477}
478
479// Update torque control type and related controller limit values
481 torque_controller = new_torque_controller;
482 // update the
484 // voltage control
486 else
487 // current control
489}
490
491// Update motion control type and related target values
492// - if changing to angle control set target to current angle
493// - if changing to velocity control set target to zero
494// - if changing to torque control set target to zero
496
497 if (controller == new_motion_controller) return; // no change
498
499 switch(new_motion_controller)
500 {
507 // if the previous controller was not angle control
508 // set target to current angle
510 break;
512 if(controller == MotionControlType::velocity_openloop) break; // nothing to do if we are already in velocity control
515 // if the previous controller was not velocity control
516 // stop the motor
517 target = 0;
518 break;
520 // if torque control set target to zero
521 target = 0;
522 break;
523 default:
524 // if torque control set target to zero
525 target = 0;
526 break;
527 }
528
529 // finally set the new controller
530 controller = new_motion_controller;
531 // update limits in case they need to be changed for the new controller
535}
536
537
539 if (bandwidth <= 0.0f) {
540 // check bandwidth is positive
541 SIMPLEFOC_MOTOR_ERROR("Fail. BW <= 0");
542 return 1;
543 }
544 if (loopfoc_time_us && bandwidth > 0.5f * (1e6f / loopfoc_time_us)) {
545 // check bandwidth is not too high for the control loop frequency
546 SIMPLEFOC_MOTOR_ERROR("Fail. BW too high, current loop freq:" , (1e6f / loopfoc_time_us));
547 return 2;
548 }
550 // need motor parameters to tune the controller
551 SIMPLEFOC_MOTOR_WARN("Motor params missing!");
553 return 3;
554 }
555 }else if (_isset(phase_inductance) && !(_isset(axis_inductance.q))) {
556 // if only single inductance value is set, use it for both d and q axis
558 }
559
560 PID_current_q.P = axis_inductance.q * (_2PI * bandwidth);
561 PID_current_q.I = phase_resistance * (_2PI * bandwidth);
562 PID_current_d.P = axis_inductance.d * (_2PI * bandwidth);
563 PID_current_d.I = phase_resistance * (_2PI * bandwidth);
564 LPF_current_d.Tf = 1.0f / (_2PI * bandwidth * 5.0f); // filter cutoff at 5x bandwidth
565 LPF_current_q.Tf = 1.0f / (_2PI * bandwidth * 5.0f); // filter cutoff at 5x bandwidth
566
567 SIMPLEFOC_MOTOR_DEBUG("Tunned PI params for BW [Hz]: ", bandwidth);
572
573 return 0;
574}
575
576
577
578// Iterative function looping FOC algorithm, setting Uq on the Motor
579// The faster it can be run the better
581 // update loop time measurement
583
584 // if initFOC is being executed at the moment, do nothing
586
587 // update sensor - do this even in open-loop mode, as user may be switching between modes and we could lose track
588 // of full rotations otherwise.
589 if (sensor) sensor->update();
590
591 // if disabled do nothing
592 if(!enabled) return;
593
594 // if open-loop do nothing
596 // calculate the open loop electirical angle
598 else
599 // Needs the update() to be called first
600 // This function will not have numerical issues because it uses Sensor::getMechanicalAngle()
601 // which is in range 0-2PI
603
604 switch (torque_controller) {
608 break;
610 if(! _isset(phase_resistance)) return;
611 // constrain current setpoint
612 current_sp = _constrain(current_sp, -current_limit, current_limit) + feed_forward_current.q; // desired current is the setpoint
613 // calculate the back-emf voltage if KV_rating available U_bemf = vel*(1/KV)
615 // filter the value values
617 // calculate the phase voltage
619 // constrain voltage within limits
621 // d voltage - lag compensation
624 break;
626 if(!current_sense) return;
627 // constrain current setpoint
629 // read overall current magnitude
631 // filter the value values
633 // calculate the phase voltage
635 // d voltage - lag compensation
638 break;
640 if(!current_sense) return;
641 // constrain current setpoint
643 // read dq currents
645 // filter values
648 // calculate the phase voltages
651 // d voltage - lag compensation
653 // q voltage - cross coupling compensation - TODO verify
655 // add feed forward
658 break;
659 default:
660 // no torque control selected
661 SIMPLEFOC_MOTOR_ERROR("no torque control selected!");
662 break;
663 }
664 // set the phase voltage - FOC heart function :)
666}
667
668// Iterative function running outer loop of the FOC algorithm
669// Behavior of this function is determined by the motor.controller variable
670// It runs either angle, velocity or voltage loop
671// - needs to be called iteratively it is asynchronous function
672// - if target is not set it uses motor.target value
673void FOCMotor::move(float new_target) {
674
675 // set internal target variable
676 if(_isset(new_target) ) target = new_target;
677
678 // downsampling (optional)
679 if(motion_cnt++ < motion_downsample) return;
680 motion_cnt = 0;
681
682 // if initFOC is being executed at the moment, do nothing
684
685 // calculate the elapsed time between the calls
686 // TODO replace downsample by runnind the code at
687 // a specific frequency (or almost)
689
690 // read value even if motor is disabled to keep the monitoring updated
691 // except for the open loop modes where the values are updated within angle/velocityOpenLoop functions
692
693 // shaft angle/velocity need the update() to be called first
694 // get shaft angle
695 // TODO sensor precision: the shaft_angle actually stores the complete position, including full rotations, as a float
696 // For this reason it is NOT precise when the angles become large.
697 // Additionally, the way LPF works on angle is a precision issue, and the angle-LPF is a problem
698 // when switching to a 2-component representation.
700 // read the values only if the motor is not in open loop
701 // because in open loop the shaft angle/velocity is updated within angle/velocityOpenLoop functions
704 }
705
706 // if disabled do nothing
707 // and if
708 if(!enabled) return;
709
710
711 // upgrade the current based voltage limit
712 switch (controller) {
715 break;
717 // TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
718 // the angles are large. This results in not being able to command small changes at high position values.
719 // to solve this, the delta-angle has to be calculated in a numerically precise way.
720 // angle set point
722 // calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
724 break;
726 // TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
727 // the angles are large. This results in not being able to command small changes at high position values.
728 // to solve this, the delta-angle has to be calculated in a numerically precise way.
729 // angle set point
731 // calculate velocity set point
734 // calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
736 break;
738 // velocity set point - sensor precision: this calculation is numerically precise.
740 // calculate the torque command
742 break;
744 // velocity control in open loop - sensor precision: this calculation is numerically precise.
746 // this function updates the shaft_angle and shaft_velocity
747 // returns the voltage or current that is to be set to the motor (depending on torque control mode)
748 // returned values correspond to the voltage_limit and current_limit
750 break;
752 // angle control in open loop -
753 // TODO sensor precision: this calculation NOT numerically precise, and subject
754 // to the same problems in small set-point changes at high angles
755 // as the closed loop version.
757 // this function updates the shaft_angle and shaft_velocity
758 // returns the voltage or current that is to be set to the motor (depending on torque control mode)
759 // returned values correspond to the voltage_limit and current_limit
761 break;
763 // custom control - user provides the function that calculates the current_sp
764 // based on the target value and the motor state
765 // user makes sure to use it with appropriate torque control mode
766 if(customMotionControlCallback)
767 current_sp = customMotionControlCallback(this);
768 break;
769 }
770}
771
772
773// FOC initialization function
775 int exit_flag = 1;
776
778
779 // align motor if necessary
780 // alignment necessary for encoders!
781 // sensor and motor alignment - can be skipped
782 // by setting motor.sensor_direction and motor.zero_electric_angle
783 if(sensor){
784 exit_flag *= alignSensor();
785 // added the shaft_angle update
786 sensor->update();
788 } else {
789 SIMPLEFOC_MOTOR_DEBUG("No sensor.");
791 exit_flag = 1;
792 }else{
793 SIMPLEFOC_MOTOR_ERROR("Only openloop allowed!");
794 exit_flag = 0; // no FOC without sensor
795 }
796 }
797
798 // aligning the current sensor - can be skipped
799 // checks if driver phases are the same as current sense phases
800 // and checks the direction of measuremnt.
801 if(exit_flag){
802 if(current_sense){
805 SIMPLEFOC_MOTOR_ERROR("Current sense not init!");
806 exit_flag = 0;
807 }else{
808 exit_flag *= alignCurrentSense();
809 }
810 }
811 else { SIMPLEFOC_MOTOR_ERROR("No current sense."); }
812 }
813
814 if(exit_flag){
815 SIMPLEFOC_MOTOR_DEBUG("Ready.");
817 }else{
818 SIMPLEFOC_MOTOR_ERROR("Init FOC fail");
820 disable();
821 }
822
823 return exit_flag;
824}
825
826// Calibarthe the motor and current sense phases
828 int exit_flag = 1; // success
829
830 SIMPLEFOC_MOTOR_DEBUG("Align current sense.");
831
832 // align current sense and the driver
834 if(!exit_flag){
835 // error in current sense - phase either not measured or bad connection
836 SIMPLEFOC_MOTOR_ERROR("Align error!");
837 exit_flag = 0;
838 }else{
839 // output the alignment status flag
840 SIMPLEFOC_MOTOR_DEBUG("Success: ", exit_flag);
841 }
842
843 return exit_flag > 0;
844}
845
846// Encoder alignment to electrical 0 angle
848 int exit_flag = 1; // success
849 SIMPLEFOC_MOTOR_DEBUG("Align sensor.");
850
851 // check if sensor needs zero search
852 if(sensor->needsSearch()) exit_flag = absoluteZeroSearch();
853 // stop init if not found index
854 if(!exit_flag) return exit_flag;
855
856 // v2.3.3 fix for R_AVR_7_PCREL against symbol" bug for AVR boards
857 // TODO figure out why this works
858 float voltage_align = voltage_sensor_align;
859
860 // if unknown natural direction
862
863 // find natural direction
864 // move one electrical revolution forward
865 for (int i = 0; i <=500; i++ ) {
866 float angle = _3PI_2 + _2PI * i / 500.0f;
867 setPhaseVoltage(voltage_align, 0, angle);
868 sensor->update();
869 _delay(2);
870 }
871 // take and angle in the middle
872 sensor->update();
873 float mid_angle = sensor->getAngle();
874 // move one electrical revolution backwards
875 for (int i = 500; i >=0; i-- ) {
876 float angle = _3PI_2 + _2PI * i / 500.0f ;
877 setPhaseVoltage(voltage_align, 0, angle);
878 sensor->update();
879 _delay(2);
880 }
881 sensor->update();
882 float end_angle = sensor->getAngle();
883 // setPhaseVoltage(0, 0, 0);
884 _delay(200);
885 // determine the direction the sensor moved
886 float moved = fabs(mid_angle - end_angle);
887 if (moved<MIN_ANGLE_DETECT_MOVEMENT) { // minimum angle to detect movement
888 SIMPLEFOC_MOTOR_ERROR("Failed to notice movement");
889 return 0; // failed calibration
890 } else if (mid_angle < end_angle) {
891 SIMPLEFOC_MOTOR_DEBUG("sensor dir: CCW");
893 } else{
894 SIMPLEFOC_MOTOR_DEBUG("sensor dir: CW");
896 }
897 // check pole pair number
898 pp_check_result = !(fabs(moved*pole_pairs - _2PI) > 0.5f); // 0.5f is arbitrary number it can be lower or higher!
899 if( pp_check_result==false ) {
900 SIMPLEFOC_MOTOR_WARN("PP check: fail - est. pp: ", _2PI/moved);
901 } else {
902 SIMPLEFOC_MOTOR_DEBUG("PP check: OK!");
903 }
904
905 } else SIMPLEFOC_MOTOR_DEBUG("Skip dir calib.");
906
907 // zero electric angle not known
909 // align the electrical phases of the motor and sensor
910 // set angle -90(270 = 3PI/2) degrees
911 setPhaseVoltage(voltage_align, 0, _3PI_2);
912 _delay(700);
913 // read the sensor
914 sensor->update();
915 // get the current zero electric angle
918 _delay(20);
919 SIMPLEFOC_MOTOR_DEBUG("Zero elec. angle: ", zero_electric_angle);
920 // stop everything
921 setPhaseVoltage(0, 0, 0);
922 _delay(200);
923 } else { SIMPLEFOC_MOTOR_DEBUG("Skip offset calib."); }
924 return exit_flag;
925}
926
927
928// Encoder alignment the absolute zero angle
929// - to the index
931 // sensor precision: this is all ok, as the search happens near the 0-angle, where the precision
932 // of float is sufficient.
933 SIMPLEFOC_MOTOR_DEBUG("Index search...");
934 // search the absolute zero with small velocity
935 float limit_vel = velocity_limit;
936 float limit_volt = voltage_limit;
939 shaft_angle = 0;
940 while(sensor->needsSearch() && shaft_angle < _2PI){
941 angleOpenloop(1.5f*_2PI);
942 // call important for some sensors not to loose count
943 // not needed for the search
944 sensor->update();
945 }
946 // disable motor
947 setPhaseVoltage(0, 0, 0);
948 // reinit the limits
949 velocity_limit = limit_vel;
950 voltage_limit = limit_volt;
951 // check if the zero found
952 if(monitor_port){
953 if(sensor->needsSearch()) { SIMPLEFOC_MOTOR_ERROR("Not found!"); }
954 else { SIMPLEFOC_MOTOR_DEBUG("Success!"); }
955 }
956 return !sensor->needsSearch();
957}
#define _MON_CURR_Q
Definition FOCMotor.h:41
#define _MON_ANGLE
Definition FOCMotor.h:44
MotionControlType
Definition FOCMotor.h:49
@ velocity_openloop
Definition FOCMotor.h:53
@ velocity
Velocity motion control.
Definition FOCMotor.h:51
@ angle_openloop
Definition FOCMotor.h:54
@ custom
Custom control method - control method added by user.
Definition FOCMotor.h:56
@ torque
Torque control.
Definition FOCMotor.h:50
@ angle_nocascade
Position/angle motion control without velocity cascade.
Definition FOCMotor.h:55
@ angle
Position/angle motion control.
Definition FOCMotor.h:52
#define _MON_VOLT_D
Definition FOCMotor.h:40
#define _MON_VEL
Definition FOCMotor.h:43
#define _MON_CURR_D
Definition FOCMotor.h:42
#define _MON_TARGET
Definition FOCMotor.h:38
#define SIMPLEFOC_MOTOR_DEBUG(msg,...)
Definition FOCMotor.h:27
@ SinePWM
Sinusoidal PWM modulation.
Definition FOCMotor.h:73
@ motor_ready
Motor is initialized and calibrated (closed loop possible)
Definition FOCMotor.h:86
@ motor_calibrating
Motor calibration in progress.
Definition FOCMotor.h:85
@ motor_calib_failed
Motor calibration failed (possibly recoverable)
Definition FOCMotor.h:88
#define SIMPLEFOC_MOTOR_ERROR(msg,...)
Definition FOCMotor.h:23
#define _MON_VOLT_Q
Definition FOCMotor.h:39
#define SIMPLEFOC_MOTOR_WARN(msg,...)
Definition FOCMotor.h:19
TorqueControlType
Definition FOCMotor.h:62
@ estimated_current
torque control using estimated current (provided motor parameters)
Definition FOCMotor.h:66
@ voltage
Torque control using voltage.
Definition FOCMotor.h:63
@ dc_current
Torque control using DC current (one current magnitude)
Definition FOCMotor.h:64
@ foc_current
torque control using dq currents
Definition FOCMotor.h:65
@ UNKNOWN
Definition Sensor.h:12
@ CCW
Definition Sensor.h:11
@ CW
Definition Sensor.h:10
virtual int driverAlign(float align_voltage, bool modulation_centered=false)
PhaseCurrent_s readAverageCurrents(int N=100)
DQCurrent_s getDQCurrents(ABCurrent_s current, float angle_el)
virtual PhaseCurrent_s getPhaseCurrents()=0
virtual float getDCCurrent(float angle_el=0)
ABCurrent_s getABCurrents(PhaseCurrent_s current)
DQCurrent_s getFOCCurrents(float angle_el)
int characteriseMotor(float voltage, float correction_factor)
Definition FOCMotor.cpp:96
int8_t enabled
enabled or disabled motor flag
Definition FOCMotor.h:243
void updateCurrentLimit(float new_current_limit)
Definition FOCMotor.cpp:453
void updateLoopFOCTime()
Definition FOCMotor.h:393
int absoluteZeroSearch()
Definition FOCMotor.cpp:930
void linkSensor(Sensor *sensor)
Definition FOCMotor.cpp:53
int8_t modulation_centered
flag (1) centered modulation around driver limit /2 or (0) pulled to 0
Definition FOCMotor.h:248
DQ_s axis_inductance
motor direct axis phase inductance
Definition FOCMotor.h:235
void monitor()
Definition FOCMotor.cpp:321
void useMonitoring(Print &serial)
Definition FOCMotor.cpp:87
char monitor_separator
monitor outputs separation character
Definition FOCMotor.h:289
int alignSensor()
Definition FOCMotor.cpp:847
PIDController PID_current_q
parameter determining the q current PID config
Definition FOCMotor.h:256
float Ualpha
Definition FOCMotor.h:221
virtual void setPhaseVoltage(float Uq, float Ud, float angle_el)=0
void updateVoltageLimit(float new_voltage_limit)
Definition FOCMotor.cpp:466
float sensor_offset
user defined sensor zero offset
Definition FOCMotor.h:268
LowPassFilter LPF_angle
parameter determining the angle low pass filter configuration
Definition FOCMotor.h:263
DQVoltage_s voltage
current d and q voltage set to the motor
Definition FOCMotor.h:218
LowPassFilter LPF_velocity
parameter determining the velocity Low pass filter configuration
Definition FOCMotor.h:262
uint8_t monitor_variables
Bit array holding the map of variables the user wants to monitor.
Definition FOCMotor.h:292
MotionControlType controller
parameter determining the control loop to be used
Definition FOCMotor.h:253
float phase_resistance
motor phase resistance
Definition FOCMotor.h:231
uint32_t loopfoc_time_us
filtered loop times
Definition FOCMotor.h:308
void updateTorqueControlType(TorqueControlType new_torque_controller)
Definition FOCMotor.cpp:480
virtual int initFOC()
Definition FOCMotor.cpp:774
float velocityOpenloop(float target_velocity)
Definition FOCMotor.cpp:389
float angleOpenloop(float target_angle)
Definition FOCMotor.cpp:414
PIDController PID_current_d
parameter determining the d current PID config
Definition FOCMotor.h:257
Sensor * sensor
CurrentSense link.
Definition FOCMotor.h:300
DQVoltage_s feed_forward_voltage
current d and q voltage set to the motor
Definition FOCMotor.h:224
CurrentSense * current_sense
Definition FOCMotor.h:302
void linkCurrentSense(CurrentSense *current_sense)
Definition FOCMotor.cpp:60
virtual void move(float target=NOT_SET)
Definition FOCMotor.cpp:673
float current_sp
target current ( q current )
Definition FOCMotor.h:215
float voltage_limit
Voltage limiting variable - global limit.
Definition FOCMotor.h:238
float velocity_limit
Velocity limiting variable - global limit.
Definition FOCMotor.h:240
float electricalAngle()
Definition FOCMotor.cpp:77
bool pp_check_result
the result of the PP check, if run during loopFOC
Definition FOCMotor.h:271
PIDController P_angle
parameter determining the position PID configuration
Definition FOCMotor.h:261
unsigned int monitor_decimals
monitor outputs decimal places
Definition FOCMotor.h:290
unsigned int monitor_downsample
show monitor outputs each monitor_downsample calls
Definition FOCMotor.h:286
virtual float estimateBEMF(float velocity)
Definition FOCMotor.h:130
LowPassFilter LPF_current_q
parameter determining the current Low pass filter configuration
Definition FOCMotor.h:258
unsigned int motion_cnt
counting variable for downsampling for move commad
Definition FOCMotor.h:265
char monitor_end_char
monitor outputs ending character
Definition FOCMotor.h:288
Print * monitor_port
Serial terminal variable if provided.
Definition FOCMotor.h:305
FOCMotorStatus motor_status
motor status
Definition FOCMotor.h:244
float velocity_index_search
target velocity for index search
Definition FOCMotor.h:228
float feed_forward_velocity
current feed forward velocity
Definition FOCMotor.h:211
char monitor_start_char
monitor starting character
Definition FOCMotor.h:287
virtual void loopFOC()
Definition FOCMotor.cpp:580
LowPassFilter LPF_current_d
parameter determining the current Low pass filter configuration
Definition FOCMotor.h:259
float Ubeta
Phase voltages U alpha and U beta used for inverse Park and Clarke transform.
Definition FOCMotor.h:221
int alignCurrentSense()
Definition FOCMotor.cpp:827
float shaft_angle_sp
current target angle
Definition FOCMotor.h:217
float shaft_velocity
current motor velocity
Definition FOCMotor.h:214
float shaftVelocity()
Definition FOCMotor.cpp:71
float shaftAngle()
Definition FOCMotor.cpp:65
float KV_rating
motor KV rating
Definition FOCMotor.h:233
float voltage_bemf
estimated backemf voltage (if provided KV constant)
Definition FOCMotor.h:220
float shaft_angle
current motor angle
Definition FOCMotor.h:212
int tuneCurrentController(float bandwidth)
Definition FOCMotor.cpp:538
float voltage_sensor_align
sensor and motor align voltage parameter
Definition FOCMotor.h:227
Direction sensor_direction
default is CW. if sensor_direction == Direction::CCW then direction will be flipped compared to CW....
Definition FOCMotor.h:270
void updateMotionControlTime()
Definition FOCMotor.h:397
float target
current target value - depends of the controller
Definition FOCMotor.h:210
float current_limit
Current limiting variable - global limit.
Definition FOCMotor.h:239
float electrical_angle
current electrical angle
Definition FOCMotor.h:213
DQCurrent_s feed_forward_current
current d and q current measured
Definition FOCMotor.h:223
TorqueControlType torque_controller
parameter determining the torque control type
Definition FOCMotor.h:252
PIDController PID_velocity
parameter determining the velocity PID configuration
Definition FOCMotor.h:260
float phase_inductance
motor phase inductance q axis - FOR BACKWARDS COMPATIBILITY
Definition FOCMotor.h:234
float zero_electric_angle
absolute zero electric angle - if available
Definition FOCMotor.h:269
DQCurrent_s current
current d and q current measured
Definition FOCMotor.h:219
virtual void disable()=0
int pole_pairs
motor pole pairs number
Definition FOCMotor.h:232
void updateVelocityLimit(float new_velocity_limit)
Definition FOCMotor.cpp:446
FOCModulationType foc_modulation
parameter determining modulation algorithm
Definition FOCMotor.h:247
unsigned int motion_downsample
parameter defining the ratio of downsampling for move commad
Definition FOCMotor.h:264
void updateMotionControlType(MotionControlType new_motion_controller)
Definition FOCMotor.cpp:495
float shaft_velocity_sp
current target velocity
Definition FOCMotor.h:216
float Tf
Low pass filter time constant.
float limit
Maximum output value.
Definition pid.h:38
float P
Proportional gain.
Definition pid.h:34
float I
Integral gain.
Definition pid.h:35
virtual int needsSearch()
Definition Sensor.cpp:97
virtual float getMechanicalAngle()
Definition Sensor.cpp:73
virtual float getAngle()
Definition Sensor.cpp:79
virtual float getVelocity()
Definition Sensor.cpp:20
virtual void update()
Definition Sensor.cpp:7
static void enable(Print *debugPrint=&Serial)
#define DEF_VEL_LIM
angle velocity limit default
Definition defaults.h:41
#define DEF_CURRENT_LIM
2Amps current limit by default
Definition defaults.h:33
#define DEF_VOLTAGE_SENSOR_ALIGN
default voltage for sensor and motor zero alignemt
Definition defaults.h:46
#define DEF_POWER_SUPPLY
default power supply voltage
Definition defaults.h:4
#define DEF_INDEX_SEARCH_TARGET_VELOCITY
default index search velocity
Definition defaults.h:44
float _electricalAngle(float shaft_angle, int pole_pairs)
Definition foc_utils.cpp:83
float float * c
Definition foc_utils.cpp:43
#define _sign(a)
Definition foc_utils.h:7
#define _PI
Definition foc_utils.h:26
#define _2PI
Definition foc_utils.h:29
#define _3PI_2
Definition foc_utils.h:30
float _normalizeAngle(float angle)
#define MIN_ANGLE_DETECT_MOVEMENT
Definition foc_utils.h:40
#define _isset(a)
Definition foc_utils.h:13
#define _constrain(amt, low, high)
Definition foc_utils.h:11
float q
Definition foc_utils.h:46
float d
Definition foc_utils.h:45
void _delay(unsigned long ms)
Definition time_utils.cpp:5
unsigned long _micros()