Skip to content

Commit 452aeef

Browse files
committed
FEATURE Added Space Vector modulation
1 parent 29efc20 commit 452aeef

File tree

8 files changed

+260
-70
lines changed

8 files changed

+260
-70
lines changed

arduino_foc_minimal_encoder/BLDCMotor.cpp

Lines changed: 115 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ BLDCMotor::BLDCMotor(int phA, int phB, int phC, int pp, int en)
2020
voltage_power_supply = DEF_POWER_SUPPLY;
2121

2222
// Velocity loop config
23-
// PI contoroller constant
23+
// PI controller constant
2424
PI_velocity.P = DEF_PI_VEL_P;
2525
PI_velocity.I = DEF_PI_VEL_I;
2626
PI_velocity.timestamp = _micros();
@@ -55,6 +55,9 @@ BLDCMotor::BLDCMotor(int phA, int phB, int phC, int pp, int en)
5555
// electric angle of the zero angle
5656
zero_electric_angle = 0;
5757

58+
// default modulation is SinePWM
59+
foc_modulation = FOCModulationType::SinePWM;
60+
5861
//debugger
5962
debugger = nullptr;
6063
}
@@ -76,8 +79,8 @@ void BLDCMotor::init() {
7679
setPwmFrequency(pwmC);
7780

7881
// sanity check for the voltage limit configuration
79-
if(PI_velocity.voltage_limit > voltage_power_supply/2) PI_velocity.voltage_limit = voltage_power_supply/2;
80-
if(PI_velocity_index_search.voltage_limit > voltage_power_supply/2) PI_velocity_index_search.voltage_limit = voltage_power_supply/2;
82+
if(PI_velocity.voltage_limit > 0.7*voltage_power_supply) PI_velocity.voltage_limit = 0.7*voltage_power_supply;
83+
if(PI_velocity_index_search.voltage_limit > 0.7*voltage_power_supply) PI_velocity_index_search.voltage_limit = 0.7*voltage_power_supply;
8184

8285
_delay(500);
8386
// enable motor
@@ -135,7 +138,7 @@ int BLDCMotor::alignSensor() {
135138
if(debugger){
136139
if(exit_flag< 0 ) debugger->println("DEBUG: Error: Absolute zero not found!");
137140
if(exit_flag> 0 ) debugger->println("DEBUG: Success: Absolute zero found!");
138-
else debugger->println("DEBUG: Absolute zero not availabe!");
141+
else debugger->println("DEBUG: Absolute zero not available!");
139142
}
140143
return exit_flag;
141144
}
@@ -199,7 +202,6 @@ float BLDCMotor::electricAngle(float shaftAngle) {
199202
/**
200203
FOC functions
201204
*/
202-
203205
// FOC initialization function
204206
int BLDCMotor::initFOC() {
205207
// sensor and motor alignment
@@ -225,6 +227,7 @@ void BLDCMotor::loopFOC() {
225227
void BLDCMotor::move(float target) {
226228
// get angular velocity
227229
shaft_velocity = shaftVelocity();
230+
// choose control loop
228231
switch (controller) {
229232
case ControlType::voltage:
230233
voltage_q = target;
@@ -247,43 +250,121 @@ void BLDCMotor::move(float target) {
247250

248251

249252
// Method using FOC to set Uq to the motor at the optimal angle
253+
// Function implementing Space Vector PWM and Sine PWM algorithms
254+
//
255+
// Function using sine approximation
256+
// regular sin + cos ~300us (no memory usaage)
257+
// approx _sin + _cos ~110us (400Byte ~ 20% of memory)
250258
void BLDCMotor::setPhaseVoltage(float Uq, float angle_el) {
259+
switch (foc_modulation)
260+
{
261+
case FOCModulationType::SinePWM :
262+
// Sinusoidal PWM modulation
263+
// Inverse Park + Clarke transformation
264+
265+
// angle normalization in between 0 and 2pi
266+
// only necessary if using _sin and _cos - approximation functions
267+
angle_el = normalizeAngle(angle_el + zero_electric_angle);
268+
// Inverse park transform
269+
Ualpha = -_sin(angle_el) * Uq; // -sin(angle) * Uq;
270+
Ubeta = _cos(angle_el) * Uq; // cos(angle) * Uq;
271+
272+
// Clarke transform
273+
Ua = Ualpha + voltage_power_supply/2;
274+
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta + voltage_power_supply/2;
275+
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta + voltage_power_supply/2;
276+
break;
251277

252-
// angle normalisation in between 0 and 2pi
253-
// only necessary if using _sin and _cos - approximation functions
254-
float angle = normalizeAngle(angle_el + zero_electric_angle);
255-
// Inverse park transform
256-
// regular sin + cos ~300us (no memory usaage)
257-
// approx _sin + _cos ~110us (400Byte ~ 20% of memory)
258-
// Ualpha = -_sin(angle) * Uq; // -sin(angle) * Uq;
259-
// Ubeta = _cos(angle) * Uq; // cos(angle) * Uq;
260-
Ualpha = -_sin(angle) * Uq; // -sin(angle) * Uq;
261-
Ubeta = _cos(angle) * Uq; // cos(angle) * Uq;
262-
263-
// Clarke transform
264-
Ua = Ualpha;
265-
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta;
266-
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta;
278+
case FOCModulationType::SpaceVectorPWM :
279+
// Nice video explaining the SpaceVectorModulation (SVPWM) algorithm
280+
// https://www.youtube.com/watch?v=QMSWUMEAejg
281+
282+
// if negative voltages change inverse the phase
283+
// angle + 180degrees
284+
if(Uq < 0) angle_el += M_PI;
285+
Uq = abs(Uq);
286+
287+
// angle normalisation in between 0 and 2pi
288+
// only necessary if using _sin and _cos - approximation functions
289+
angle_el = normalizeAngle(angle_el + zero_electric_angle + _PI_2);
290+
291+
// find the sector we are in currently
292+
int sector = floor(angle_el / _PI_3) + 1;
293+
// calculate the duty cycles
294+
float T1 = _SQRT3*_sin(sector*_PI_3 - angle_el);
295+
float T2 = _SQRT3*_sin(angle_el - (sector-1.0)*_PI_3);
296+
// two versions possible
297+
// centered around voltage_power_supply/2
298+
float T0 = 1 - T1 - T2;
299+
// centered around 0
300+
//T0 = 0;
301+
302+
// calculate the duty cycles(times)
303+
float Ta,Tb,Tc;
304+
switch(sector){
305+
case 1:
306+
Ta = T1 + T2 + T0/2;
307+
Tb = T2 + T0/2;
308+
Tc = T0/2;
309+
break;
310+
case 2:
311+
Ta = T1 + T0/2;
312+
Tb = T1 + T2 + T0/2;
313+
Tc = T0/2;
314+
break;
315+
case 3:
316+
Ta = T0/2;
317+
Tb = T1 + T2 + T0/2;
318+
Tc = T2 + T0/2;
319+
break;
320+
case 4:
321+
Ta = T0/2;
322+
Tb = T1+ T0/2;
323+
Tc = T1 + T2 + T0/2;
324+
break;
325+
case 5:
326+
Ta = T2 + T0/2;
327+
Tb = T0/2;
328+
Tc = T1 + T2 + T0/2;
329+
break;
330+
case 6:
331+
Ta = T1 + T2 + T0/2;
332+
Tb = T0/2;
333+
Tc = T1 + T0/2;
334+
break;
335+
default:
336+
// possible error state
337+
Ta = 0;
338+
Tb = 0;
339+
Tc = 0;
340+
}
341+
342+
// calculate the phase voltages
343+
Ua = Ta*Uq;
344+
Ub = Tb*Uq;
345+
Uc = Tc*Uq;
346+
break;
347+
}
267348

268-
// set phase voltages
349+
// set the voltages in hardware
269350
setPwm(pwmA, Ua);
270351
setPwm(pwmB, Ub);
271352
setPwm(pwmC, Uc);
272353
}
273354

274355

356+
357+
275358
// Set voltage to the pwm pin
276359
// - function a bit optimized to get better performance
277360
void BLDCMotor::setPwm(int pinPwm, float U) {
278361
// max value
279-
float U_max = voltage_power_supply/2.0;
280-
281-
// sets the voltage [-U_max,U_max] to pwm [0,255]
282-
// u_pwm = 255 * (U + U_max)/(2*U_max)
283-
// it can be further optimized
284-
// (example U_max = 6 > U_pwm = 127.5 + 21.25*U)
285-
int U_pwm = 127.5 * (U/U_max + 1);
286-
362+
float U_max = voltage_power_supply;
363+
364+
// sets the voltage [0,12V(U_max)] to pwm [0,255]
365+
// - U_max you can set in header file - default 12V
366+
int U_pwm = 255.0 * U / U_max;
367+
287368
// limit the values between 0 and 255
288369
U_pwm = (U_pwm < 0) ? 0 : (U_pwm >= 255) ? 255 : U_pwm;
289370

@@ -340,9 +421,11 @@ float BLDCMotor::velocityIndexSearchPI(float tracking_error) {
340421
}
341422
// P controller for position control loop
342423
float BLDCMotor::positionP(float ek) {
343-
float velk = P_angle.P * ek;
344-
if (abs(velk) > P_angle.velocity_limit) velk = velk > 0 ? P_angle.velocity_limit : -P_angle.velocity_limit;
345-
return velk;
424+
// calculate the target velocity from the position error
425+
float velocity_target = P_angle.P * ek;
426+
// constrain velocity target value
427+
if (abs(velocity_target) > P_angle.velocity_limit) velocity_target = velocity_target > 0 ? P_angle.velocity_limit : -P_angle.velocity_limit;
428+
return velocity_target;
346429
}
347430

348431
/**

arduino_foc_minimal_encoder/BLDCMotor.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
#define BLDCMotor_h
33

44
#include "Arduino.h"
5-
#include "MagneticSensor.h"
6-
#include "Encoder.h"
75
#include "FOCutils.h"
86
#include "Sensor.h"
97

@@ -34,6 +32,12 @@ enum ControlType{
3432
angle
3533
};
3634

35+
// FOC Type
36+
enum FOCModulationType{
37+
SinePWM,
38+
SpaceVectorPWM
39+
};
40+
3741
// PI controller structure
3842
struct PI_s{
3943
float P;
@@ -77,7 +81,7 @@ class BLDCMotor
7781
void init();
7882
void disable();
7983
void enable();
80-
// connect encoder
84+
// connect sensor
8185
void linkSensor(Sensor* _sensor);
8286

8387
// initilise FOC
@@ -119,6 +123,7 @@ class BLDCMotor
119123

120124
// configuration structures
121125
ControlType controller;
126+
FOCModulationType foc_modulation;
122127
PI_s PI_velocity;
123128
PI_s PI_velocity_index_search;
124129
P_s P_angle;

arduino_foc_minimal_encoder/FOCutils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
88
// utility defines
99
#define _2_SQRT3 1.15470053838
10+
#define _SQRT3 1.73205080757
1011
#define _1_SQRT3 0.57735026919
1112
#define _SQRT3_2 0.86602540378
1213
#define _SQRT2 1.41421356237
1314
#define _120_D2R 2.09439510239
1415
#define _PI_2 1.57079632679
16+
#define _PI_3 1.0471975512
1517
#define _2PI 6.28318530718
1618
#define _3PI_2 4.71238898038
1719

arduino_foc_minimal_encoder/arduino_foc_minimal_encoder.ino

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ void setup() {
2323
encoder.init();
2424
encoder.enableInterrupts(doA,doB);
2525

26+
// choose FOC algorithm to be used:
27+
// FOCModulationType::SinePWM (default)
28+
// FOCModulationType::SpaceVectorPWM
29+
motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
30+
2631
// power supply voltage
2732
// default 12V
2833
motor.voltage_power_supply = 12;

0 commit comments

Comments
 (0)