Skip to content

Commit 996f312

Browse files
authored
Merge pull request #422 from simplefoc/feat_new_cs_align
Feat new cs align
2 parents 31ed492 + dbc62a1 commit 996f312

25 files changed

+770
-600
lines changed

Diff for: README.md

+4-13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
![GitHub commits since tagged version](https://img.shields.io/github/commits-since/simplefoc/arduino-foc/latest/dev)
1414
![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/m/simplefoc/arduino-foc/dev)
1515

16-
[![arduino-library-badge](https://www.ardu-badge.com/badge/Simple%20FOC.svg?)](https://www.ardu-badge.com/badge/Simple%20FOC.svg)
16+
[![arduino-library-badge](https://ardubadge.simplefoc.com?lib=Simple%20FOC)](https://www.ardu-badge.com/badge/Simple%20FOC.svg)
17+
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/askuric/library/Simple%20FOC.svg)](https://registry.platformio.org/libraries/askuric/Simple%20FOC)
1718
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
1819
[![status](https://joss.theoj.org/papers/4382445f249e064e9f0a7f6c1bb06b1d/status.svg)](https://joss.theoj.org/papers/4382445f249e064e9f0a7f6c1bb06b1d)
1920

@@ -28,18 +29,8 @@ Therefore this is an attempt to:
2829
- For official driver boards see [<span class="simple">Simple<span class="foc">FOC</span>Boards</span>](https://docs.simplefoc.com/boards)
2930
- Many many more boards developed by the community members, see [<span class="simple">Simple<span class="foc">FOC</span>Community</span>](https://community.simplefoc.com/)
3031

31-
> NEW RELEASE 📢 : <span class="simple">Simple<span class="foc">FOC</span>library</span> v2.3.3
32-
> - Teensy4
33-
> - support for low-side current sensing [#392](https://github.com/simplefoc/Arduino-FOC/pull/392)
34-
> - support for center aligned 6pwm and 3pwm (optional) [#392](https://github.com/simplefoc/Arduino-FOC/pull/392)
35-
> - stm32
36-
> - support for center aligned pwm (even across multiple timers and motors/drivers) [#374](https://github.com/simplefoc/Arduino-FOC/pull/374), [#388](https://github.com/simplefoc/Arduino-FOC/pull/388)
37-
> - support for DMA based low-side current sensing: [#383](https://github.com/simplefoc/Arduino-FOC/pull/383),[#378](https://github.com/simplefoc/Arduino-FOC/pull/378)
38-
> - support for f7 architecture [#388](https://github.com/simplefoc/Arduino-FOC/pull/388),[#394](https://github.com/simplefoc/Arduino-FOC/pull/394)
39-
> - KV rating calculation fix [#347](https://github.com/simplefoc/Arduino-FOC/pull/347)
40-
> - Much more performant Space Vector PWM calculation [#340](https://github.com/simplefoc/Arduino-FOC/pull/340)
41-
> - And much more:
42-
> - See the complete list of bugfixes and new features of v2.3.3 [fixes and PRs](https://github.com/simplefoc/Arduino-FOC/milestone/10?closed=1)
32+
> NEXT RELEASE 📢 : <span class="simple">Simple<span class="foc">FOC</span>library</span> v2.3.4
33+
> - Current sensing support for Stepper motors (lowside and inline)
4334
4435

4536
## Arduino *SimpleFOClibrary* v2.3.3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
*
3+
* SimpleFOCMini motor control example
4+
*
5+
* For Arduino UNO or the other boards with the UNO headers
6+
* the most convenient way to use the board is to stack it to the pins:
7+
* - 12 - ENABLE
8+
* - 11 - IN1
9+
* - 10 - IN2
10+
* - 9 - IN3
11+
*
12+
*/
13+
#include <SimpleFOC.h>
14+
15+
16+
// BLDC motor & driver instance
17+
BLDCMotor motor = BLDCMotor(11);
18+
// BLDCDriver3PWM driver = BLDCDriver3PWM(11, 10, 9, 8); // mini v1.0
19+
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 10, 11, 12); // mini v1.1
20+
21+
// instantiate the commander
22+
Commander command = Commander(Serial);
23+
void doMotor(char* cmd) { command.motor(&motor, cmd); }
24+
25+
void setup() {
26+
// use monitoring with serial
27+
Serial.begin(115200);
28+
// enable more verbose output for debugging
29+
// comment out if not needed
30+
SimpleFOCDebug::enable(&Serial);
31+
32+
// if SimpleFOCMini is stacked in arduino headers
33+
// on pins 12,11,10,9,8
34+
// pin 12 is used as ground
35+
pinMode(12,OUTPUT);
36+
pinMode(12,LOW);
37+
38+
// driver config
39+
// power supply voltage [V]
40+
driver.voltage_power_supply = 12;
41+
driver.init();
42+
// link the motor and the driver
43+
motor.linkDriver(&driver);
44+
45+
// aligning voltage [V]
46+
motor.voltage_sensor_align = 3;
47+
48+
// set motion control loop to be used
49+
motor.controller = MotionControlType::velocity_openloop;
50+
51+
// default voltage_power_supply
52+
motor.voltage_limit = 2; // Volts
53+
54+
// comment out if not needed
55+
motor.useMonitoring(Serial);
56+
57+
// initialize motor
58+
motor.init();
59+
// align encoder and start FOC
60+
motor.initFOC();
61+
62+
// add target command M
63+
command.add('M', doMotor, "motor");
64+
65+
Serial.println(F("Motor ready."));
66+
Serial.println(F("Set the target velocity using serial terminal:"));
67+
68+
motor.target = 1; //initial target velocity 1 rad/s
69+
Serial.println("Target velocity: 1 rad/s");
70+
Serial.println("Voltage limit 2V");
71+
_delay(1000);
72+
}
73+
74+
void loop() {
75+
// main FOC algorithm function
76+
// the faster you run this function the better
77+
// Arduino UNO loop ~1kHz
78+
// Bluepill loop ~10kHz
79+
motor.loopFOC();
80+
81+
// Motion control function
82+
// velocity, position or voltage (defined in motor.controller)
83+
// this function can be run at much lower frequency than loopFOC() function
84+
// You can also use motor.move() and set the motor.target in the code
85+
motor.move();
86+
87+
// function intended to be used with serial plotter to monitor motor variables
88+
// significantly slowing the execution down!!!!
89+
// motor.monitor();
90+
91+
// user communication
92+
command.run();
93+
}

Diff for: src/BLDCMotor.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ int BLDCMotor::alignCurrentSense() {
202202
SIMPLEFOC_DEBUG("MOT: Align current sense.");
203203

204204
// align current sense and the driver
205-
exit_flag = current_sense->driverAlign(voltage_sensor_align);
205+
exit_flag = current_sense->driverAlign(voltage_sensor_align, modulation_centered);
206206
if(!exit_flag){
207207
// error in current sense - phase either not measured or bad connection
208208
SIMPLEFOC_DEBUG("MOT: Align error!");

Diff for: src/BLDCMotor.h

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Arduino.h"
55
#include "common/base_classes/FOCMotor.h"
66
#include "common/base_classes/Sensor.h"
7+
#include "common/base_classes/FOCDriver.h"
78
#include "common/base_classes/BLDCDriver.h"
89
#include "common/foc_utils.h"
910
#include "common/time_utils.h"

Diff for: src/StepperMotor.cpp

+112-31
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,28 @@ int StepperMotor::initFOC() {
108108
// alignment necessary for encoders!
109109
// sensor and motor alignment - can be skipped
110110
// by setting motor.sensor_direction and motor.zero_electric_angle
111-
_delay(500);
112111
if(sensor){
113112
exit_flag *= alignSensor();
114113
// added the shaft_angle update
115114
sensor->update();
116-
shaft_angle = sensor->getAngle();
115+
shaft_angle = shaftAngle();
116+
117+
// aligning the current sensor - can be skipped
118+
// checks if driver phases are the same as current sense phases
119+
// and checks the direction of measuremnt.
120+
if(exit_flag){
121+
if(current_sense){
122+
if (!current_sense->initialized) {
123+
motor_status = FOCMotorStatus::motor_calib_failed;
124+
SIMPLEFOC_DEBUG("MOT: Init FOC error, current sense not initialized");
125+
exit_flag = 0;
126+
}else{
127+
exit_flag *= alignCurrentSense();
128+
}
129+
}
130+
else { SIMPLEFOC_DEBUG("MOT: No current sense."); }
131+
}
132+
117133
} else {
118134
SIMPLEFOC_DEBUG("MOT: No sensor.");
119135
if ((controller == MotionControlType::angle_openloop || controller == MotionControlType::velocity_openloop)){
@@ -136,6 +152,26 @@ int StepperMotor::initFOC() {
136152
return exit_flag;
137153
}
138154

155+
// Calibrate the motor and current sense phases
156+
int StepperMotor::alignCurrentSense() {
157+
int exit_flag = 1; // success
158+
159+
SIMPLEFOC_DEBUG("MOT: Align current sense.");
160+
161+
// align current sense and the driver
162+
exit_flag = current_sense->driverAlign(voltage_sensor_align, modulation_centered);
163+
if(!exit_flag){
164+
// error in current sense - phase either not measured or bad connection
165+
SIMPLEFOC_DEBUG("MOT: Align error!");
166+
exit_flag = 0;
167+
}else{
168+
// output the alignment status flag
169+
SIMPLEFOC_DEBUG("MOT: Success: ", exit_flag);
170+
}
171+
172+
return exit_flag > 0;
173+
}
174+
139175
// Encoder alignment to electrical 0 angle
140176
int StepperMotor::alignSensor() {
141177
int exit_flag = 1; //success
@@ -261,8 +297,6 @@ void StepperMotor::loopFOC() {
261297

262298
// if open-loop do nothing
263299
if( controller==MotionControlType::angle_openloop || controller==MotionControlType::velocity_openloop ) return;
264-
// shaft angle
265-
shaft_angle = shaftAngle();
266300

267301
// if disabled do nothing
268302
if(!enabled) return;
@@ -271,7 +305,40 @@ void StepperMotor::loopFOC() {
271305
// This function will not have numerical issues because it uses Sensor::getMechanicalAngle()
272306
// which is in range 0-2PI
273307
electrical_angle = electricalAngle();
274-
308+
switch (torque_controller) {
309+
case TorqueControlType::voltage:
310+
// no need to do anything really
311+
break;
312+
case TorqueControlType::dc_current:
313+
if(!current_sense) return;
314+
// read overall current magnitude
315+
current.q = current_sense->getDCCurrent(electrical_angle);
316+
// filter the value values
317+
current.q = LPF_current_q(current.q);
318+
// calculate the phase voltage
319+
voltage.q = PID_current_q(current_sp - current.q);
320+
// d voltage - lag compensation
321+
if(_isset(phase_inductance)) voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
322+
else voltage.d = 0;
323+
break;
324+
case TorqueControlType::foc_current:
325+
if(!current_sense) return;
326+
// read dq currents
327+
current = current_sense->getFOCCurrents(electrical_angle);
328+
// filter values
329+
current.q = LPF_current_q(current.q);
330+
current.d = LPF_current_d(current.d);
331+
// calculate the phase voltages
332+
voltage.q = PID_current_q(current_sp - current.q);
333+
voltage.d = PID_current_d(-current.d);
334+
// d voltage - lag compensation - TODO verify
335+
// if(_isset(phase_inductance)) voltage.d = _constrain( voltage.d - current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
336+
break;
337+
default:
338+
// no torque control selected
339+
SIMPLEFOC_DEBUG("MOT: no torque control selected!");
340+
break;
341+
}
275342
// set the phase voltage - FOC heart function :)
276343
setPhaseVoltage(voltage.q, voltage.d, electrical_angle);
277344
}
@@ -310,56 +377,70 @@ void StepperMotor::move(float new_target) {
310377
// estimate the motor current if phase reistance available and current_sense not available
311378
if(!current_sense && _isset(phase_resistance)) current.q = (voltage.q - voltage_bemf)/phase_resistance;
312379

313-
// choose control loop
380+
// upgrade the current based voltage limit
314381
switch (controller) {
315382
case MotionControlType::torque:
316-
if(!_isset(phase_resistance)) voltage.q = target; // if voltage torque control
317-
else voltage.q = target*phase_resistance + voltage_bemf;
318-
voltage.q = _constrain(voltage.q, -voltage_limit, voltage_limit);
319-
// set d-component (lag compensation if known inductance)
320-
if(!_isset(phase_inductance)) voltage.d = 0;
321-
else voltage.d = _constrain( -target*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
383+
if(torque_controller == TorqueControlType::voltage){ // if voltage torque control
384+
if(!_isset(phase_resistance)) voltage.q = target;
385+
else voltage.q = target*phase_resistance + voltage_bemf;
386+
voltage.q = _constrain(voltage.q, -voltage_limit, voltage_limit);
387+
// set d-component (lag compensation if known inductance)
388+
if(!_isset(phase_inductance)) voltage.d = 0;
389+
else voltage.d = _constrain( -target*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
390+
}else{
391+
current_sp = target; // if current/foc_current torque control
392+
}
322393
break;
323394
case MotionControlType::angle:
395+
// TODO sensor precision: this calculation is not numerically precise. The target value cannot express precise positions when
396+
// the angles are large. This results in not being able to command small changes at high position values.
397+
// to solve this, the delta-angle has to be calculated in a numerically precise way.
324398
// angle set point
325399
shaft_angle_sp = target;
326400
// calculate velocity set point
327401
shaft_velocity_sp = feed_forward_velocity + P_angle( shaft_angle_sp - shaft_angle );
328-
shaft_velocity_sp = _constrain(shaft_velocity_sp, -velocity_limit, velocity_limit);
329-
// calculate the torque command
402+
shaft_velocity_sp = _constrain(shaft_velocity_sp,-velocity_limit, velocity_limit);
403+
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
330404
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control
331405
// if torque controlled through voltage
332-
// use voltage if phase-resistance not provided
333-
if(!_isset(phase_resistance)) voltage.q = current_sp;
334-
else voltage.q = _constrain( current_sp*phase_resistance + voltage_bemf , -voltage_limit, voltage_limit);
335-
// set d-component (lag compensation if known inductance)
336-
if(!_isset(phase_inductance)) voltage.d = 0;
337-
else voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
406+
if(torque_controller == TorqueControlType::voltage){
407+
// use voltage if phase-resistance not provided
408+
if(!_isset(phase_resistance)) voltage.q = current_sp;
409+
else voltage.q = _constrain( current_sp*phase_resistance + voltage_bemf , -voltage_limit, voltage_limit);
410+
// set d-component (lag compensation if known inductance)
411+
if(!_isset(phase_inductance)) voltage.d = 0;
412+
else voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
413+
}
338414
break;
339415
case MotionControlType::velocity:
340-
// velocity set point
416+
// velocity set point - sensor precision: this calculation is numerically precise.
341417
shaft_velocity_sp = target;
342418
// calculate the torque command
343419
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if current/foc_current torque control
344420
// if torque controlled through voltage control
345-
// use voltage if phase-resistance not provided
346-
if(!_isset(phase_resistance)) voltage.q = current_sp;
347-
else voltage.q = _constrain( current_sp*phase_resistance + voltage_bemf , -voltage_limit, voltage_limit);
348-
// set d-component (lag compensation if known inductance)
349-
if(!_isset(phase_inductance)) voltage.d = 0;
350-
else voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
421+
if(torque_controller == TorqueControlType::voltage){
422+
// use voltage if phase-resistance not provided
423+
if(!_isset(phase_resistance)) voltage.q = current_sp;
424+
else voltage.q = _constrain( current_sp*phase_resistance + voltage_bemf , -voltage_limit, voltage_limit);
425+
// set d-component (lag compensation if known inductance)
426+
if(!_isset(phase_inductance)) voltage.d = 0;
427+
else voltage.d = _constrain( -current_sp*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);
428+
}
351429
break;
352430
case MotionControlType::velocity_openloop:
353-
// velocity control in open loop
431+
// velocity control in open loop - sensor precision: this calculation is numerically precise.
354432
shaft_velocity_sp = target;
355433
voltage.q = velocityOpenloop(shaft_velocity_sp); // returns the voltage that is set to the motor
356-
voltage.d = 0; // TODO d-component lag-compensation
434+
voltage.d = 0;
357435
break;
358436
case MotionControlType::angle_openloop:
359-
// angle control in open loop
437+
// angle control in open loop -
438+
// TODO sensor precision: this calculation NOT numerically precise, and subject
439+
// to the same problems in small set-point changes at high angles
440+
// as the closed loop version.
360441
shaft_angle_sp = target;
361442
voltage.q = angleOpenloop(shaft_angle_sp); // returns the voltage that is set to the motor
362-
voltage.d = 0; // TODO d-component lag-compensation
443+
voltage.d = 0;
363444
break;
364445
}
365446
}

Diff for: src/StepperMotor.h

+2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class StepperMotor: public FOCMotor
8989
int alignSensor();
9090
/** Motor and sensor alignment to the sensors absolute 0 angle */
9191
int absoluteZeroSearch();
92+
/** Current sense and motor phase alignment */
93+
int alignCurrentSense();
9294

9395
// Open loop motion control
9496
/**

0 commit comments

Comments
 (0)