Skip to content

Commit 60bb2c0

Browse files
committed
Better encoder alignment and refactoring
1 parent 3a4268e commit 60bb2c0

File tree

12 files changed

+90
-118
lines changed

12 files changed

+90
-118
lines changed

README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
![Library Compile](https://github.com/askuric/Arduino-FOC/workflows/Library%20Compile/badge.svg)
55
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6-
6+
[![arduino-library-badge](https://www.ardu-badge.com/badge/Simple%20FOC.svg?)](https://www.ardu-badge.com/badge/Simple%20FOC.svg)
77

88
Proper low cost FOC supporting boards are very hard to find these days and even may not exist. The reason may be that the hobby community has not yet dug into it properly. Therefore this is the attempt to demistify the Field Oriented Control (FOC) algorithm and make a robust but simple implementation for usage with Arduino hadrware.
99

@@ -13,7 +13,6 @@ Proper low cost FOC supporting boards are very hard to find these days and even
1313
- Simple usage and scalability (Arduino)
1414
and demistify FOC control in a simple way.
1515

16-
1716
For minimal version of the code more suitable for experimenting please visit the [minimal branch](https://github.com/askuric/Arduino-FOC/tree/minimal).
1817

1918
#### The closest you can get to FOC support and low cost (I was able to find) is:
@@ -28,7 +27,7 @@ For minimal version of the code more suitable for experimenting please visit the
2827

2928
<a href="https://www.infineon.com/cms/en/product/evaluation-boards/bldc_shield_tle9879/" >Infineon</a> | <a href="https://github.com/gouldpa/FOC-Arduino-Brushless">FOC-Arduino-Brushless</a>
3029
------------ | -------------
31-
<img src="https://www.infineon.com/export/sites/default/_images/product/evaluation-boards/BLDC_Motor_Shild_with_TLE9879QXA40.jpg_1711722916.jpg" height="300px" width="400px">| <img src="https://hackster.imgix.net/uploads/attachments/998086/dev_kit_89eygMekks.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max" width="400px">
30+
<img src="https://www.infineon.com/export/sites/default/_images/product/evaluation-boards/BLDC_Motor_Shild_with_TLE9879QXA40.jpg_1711722916.jpg" width="400px">| <img src="https://hackster.imgix.net/uploads/attachments/998086/dev_kit_89eygMekks.jpg?auto=compress%2Cformat&w=1280&h=960&fit=max" width="400px">
3231
:x: Open Source | :heavy_check_mark: Open Source
3332
:heavy_check_mark:Simple to use | :x: Simple to use
3433
:heavy_check_mark:Low cost ($40) | :heavy_check_mark: Low cost
@@ -182,12 +181,12 @@ First thing you can configure is your `power_supply_voltage` value. The default
182181
// power supply voltage
183182
motor.power_supply_voltage = 12;
184183
```
185-
You can also change driver type by changing the value of the variable `motor.driver`. It tells the algorithm to generate unipolar of bipolar FOC voltages. This basically means if your BLDC driver can only output voltages in range `[0,power_supply_voltage]` your driver is `DriverType::unipolar` and if it can output voltage in range `[-power_supply_voltage, power_supply_voltage]` than you driver is `DriverType::bipolar` what is case in most of the drivers and what is default value as well.
184+
You can also change driver type by changing the value of the variable `motor.driver`. It tells the algorithm to generate unipolar of bipolar FOC voltages. This basically means if your BLDC driver can only output voltages in range `[0,power_supply_voltage]` your driver is `DriverType::half_bridge` and if it can output voltage in range `[-power_supply_voltage, power_supply_voltage]` than you driver is `DriverType::full_bridge` what is case in most of the drivers and what is default value as well.
186185
```cpp
187186
// set driver type
188-
// DriverType::unipolar // HMBGC
189-
// DriverType::bipolar // L6234 (default)
190-
motor.driver = DriverType::bipolar;
187+
// DriverType::full_bridge // HMBGC
188+
// DriverType::half_bridge // L6234 (default)
189+
motor.driver = DriverType::half_bridge;
191190
```
192191
## Control loop setup
193192
First parameter you can change is the variable you want to control. You set it by changing the `motor.controller` variable. If you want to control the motor angle you will set the `controller` to `ControlType::angle`, if youy seek the DC motor behavior behaviour by controlling the voltage use `ControlType::voltage`, if you wish to control motor angular velocity `ControlType::velocity`. If you wish to control velocities which are very very slow, typically around ~0.01 rad/s you can use the `ControlType::velocity_ultra_slow` controller.
@@ -327,7 +326,7 @@ The real time execution of the Arduino Simple FOC library is govenred by two fun
327326
// the best would be to be in ~10kHz range
328327
motor.loopFOC();
329328
```
330-
The funciton `loopFOC()` gets the current motor angle from the encoder, turns in into the electrical angle and computes Clarke transfrom to set the desired $U_q$ voltage to the motor. Basically it implements the funcitonality of the [velocity control loop](#voltage-control-loop).
329+
The funciton `loopFOC()` gets the current motor angle from the encoder, turns in into the electrical angle and computes Clarke transfrom to set the desired $U_q$ voltage to the motor. Basically it implements the funcitonality of the [voltage control loop](#voltage-control-loop).
331330
- The faster you can run this funciton the better
332331
- In the empty arduino loop it runs at ~1kHz but idealy it would be around ~10kHz
333332

examples/HMBGC_example/HMBGC_example.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ void setup() {
3737
PciManager.registerListener(&listenerB);
3838

3939
// set driver type
40-
// DriverType::unipolar
41-
// DriverType::bipolar - default
42-
motor.driver = DriverType::unipolar;
40+
// DriverType::full_bridge
41+
// DriverType::half_bridge - default
42+
motor.driver = DriverType::half_bridge
4343

4444
// power supply voltage
4545
// default 12V

examples/angle_control/angle_control.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ void setup() {
3131
encoder.init(doA, doB);
3232

3333
// set driver type
34-
// DriverType::unipolar
35-
// DriverType::bipolar - default
36-
motor.driver = DriverType::bipolar;
34+
// DriverType::full_bridge
35+
// DriverType::half_bridge - default
36+
motor.driver = DriverType::half_bridge
3737

3838
// power supply voltage
3939
// default 12V

examples/angle_control_serial/angle_control_serial.ino

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ void setup() {
3232

3333
// initialise encoder hardware
3434
encoder.init(doA, doB);
35+
3536
// set driver type
36-
// DriverType::unipolar
37-
// DriverType::bipolar - default
38-
motor.driver = DriverType::bipolar;
37+
// DriverType::full_bridge
38+
// DriverType::half_bridge - default
39+
motor.driver = DriverType::half_bridge
3940

4041
// power supply voltage
4142
// default 12V

examples/change_direction/change_direction.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ void setup() {
3131
encoder.init(doA, doB);
3232

3333
// set driver type
34-
// DriverType::unipolar
35-
// DriverType::bipolar - default
36-
motor.driver = DriverType::bipolar;
34+
// DriverType::full_bridge
35+
// DriverType::half_bridge - default
36+
motor.driver = DriverType::half_bridge
3737

3838
// power supply voltage
3939
// default 12V

examples/velocity_control/velocity_control.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ void setup() {
3131
encoder.init(doA, doB);
3232

3333
// set driver type
34-
// DriverType::unipolar
35-
// DriverType::bipolar - default
36-
motor.driver = DriverType::bipolar;
34+
// DriverType::full_bridge
35+
// DriverType::half_bridge - default
36+
motor.driver = DriverType::half_bridge
3737

3838
// power supply voltage
3939
// default 12V

examples/velocity_control_serial/velocity_control_serial.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ void setup() {
3535
encoder.init(doA, doB);
3636

3737
// set driver type
38-
// DriverType::unipolar
39-
// DriverType::bipolar - default
40-
motor.driver = DriverType::bipolar;
38+
// DriverType::full_bridge
39+
// DriverType::half_bridge - default
40+
motor.driver = DriverType::half_bridge
4141

4242
// power supply voltage
4343
// default 12V

examples/velocity_ultraslow_control_serial/velocity_ultraslow_control_serial.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ void setup() {
3535
encoder.init(doA, doB);
3636

3737
// set driver type
38-
// DriverType::unipolar
39-
// DriverType::bipolar - default
40-
motor.driver = DriverType::bipolar;
38+
// DriverType::full_bridge
39+
// DriverType::half_bridge - default
40+
motor.driver = DriverType::half_bridge
4141

4242
// power supply voltage
4343
// default 12V

examples/voltage_control/voltage_control.ino

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ void setup() {
3131
encoder.init(doA, doB);
3232

3333
// set driver type
34-
// DriverType::unipolar
35-
// DriverType::bipolar - default
36-
motor.driver = DriverType::bipolar;
34+
// DriverType::full_bridge
35+
// DriverType::half_bridge - default
36+
motor.driver = DriverType::half_bridge
3737

3838
// power supply voltage
3939
// default 12V

src/BLDCMotor.cpp

Lines changed: 53 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ BLDCMotor::BLDCMotor(int phA, int phB, int phC, int pp, int en)
2929
PI_velocity.Ti = DEF_PI_VEL_TI;
3030
PI_velocity.timestamp = micros();
3131
PI_velocity.u_limit = -1;
32+
PI_velocity.uk_1 = 0;
33+
PI_velocity.ek_1 = 0;
3234

3335
// Ultra slow velocity
3436
// PI contoroller
3537
PI_velocity_ultra_slow.K = DEF_PI_VEL_US_K;
3638
PI_velocity_ultra_slow.Ti = DEF_PI_VEL_US_TI;
3739
PI_velocity_ultra_slow.timestamp = micros();
3840
PI_velocity_ultra_slow.u_limit = -1;
41+
PI_velocity_ultra_slow.uk_1 = 0;
42+
PI_velocity_ultra_slow.ek_1 = 0;
3943

4044
// position loop config
4145
// P controller constant
@@ -44,7 +48,7 @@ BLDCMotor::BLDCMotor(int phA, int phB, int phC, int pp, int en)
4448
P_angle.velocity_limit = DEF_P_ANGLE_VEL_LIM;
4549

4650
// driver deafault type
47-
driver = DriverType::bipolar;
51+
driver = DriverType::half_bridge;
4852
}
4953

5054
// init hardware pins
@@ -118,11 +122,13 @@ void BLDCMotor::linkEncoder(Encoder* enc) {
118122
Encoder alignment to electrical 0 angle
119123
*/
120124
void BLDCMotor::alignEncoder() {
121-
setPhaseVoltage(12, -M_PI/2);
122-
delay(1000);
125+
//setPhaseVoltage(12, M_PI/2);
126+
setPwm(pwmA,12);
127+
setPwm(pwmB,0);
128+
setPwm(pwmC,0);
129+
delay(1500);
123130
encoder->setCounterZero();
124-
125-
131+
delay(500);
126132
setPhaseVoltage(0, 0);
127133
}
128134

@@ -191,84 +197,53 @@ void BLDCMotor::move(float target) {
191197
/**
192198
FOC methods
193199
*/
194-
/*
195-
Method using FOC to set Uq to the motor at the optimal angle
196-
- for unipolar drivers - only positive values
197-
*/
198-
void BLDCMotor::setPhaseVoltageUnipolar(double Uq, double angle_el) {
199-
200-
// Uq sign compensation
201-
float angle = Uq > 0 ? angle_el : normalizeAngle( angle_el + M_PI );
202-
// Park transform
203-
Ualpha = abs(Uq) * cos(angle);
204-
Ubeta = abs(Uq) * sin(angle);
205-
206-
// determine the segment I, II, III
207-
if ((angle >= 0) && (angle <= _120_D2R)) {
208-
// section I
209-
Ua = Ualpha + _1_SQRT3 * Ubeta;
210-
Ub = _2_SQRT3 * Ubeta;
211-
Uc = 0;
212-
213-
} else if ((angle > _120_D2R) && (angle <= (2 * _120_D2R))) {
214-
// section III
215-
Ua = 0;
216-
Ub = _1_SQRT3 * Ubeta - Ualpha;
217-
Uc = -_1_SQRT3 * Ubeta - Ualpha;
218-
219-
} else if ((angle > (2 * _120_D2R)) && (angle <= (3 * _120_D2R))) {
220-
// section II
221-
Ua = Ualpha - _1_SQRT3 * Ubeta;
222-
Ub = 0;
223-
Uc = - _2_SQRT3 * Ubeta;
224-
}
225-
226-
// set phase voltages
227-
setPwm(pwmA, Ua);
228-
setPwm(pwmB, Ub);
229-
setPwm(pwmC, Uc);
230-
}
231200
/*
232201
Method using FOC to set Uq to the motor at the optimal angle
233-
- for bipolar drivers - posiitve and negative voltages
234202
*/
235-
void BLDCMotor::setPhaseVoltageBipolar(double Uq, double angle_el) {
236-
237-
// q component angle
238-
float angle = angle_el + M_PI/2;
203+
void BLDCMotor::setPhaseVoltage(double Uq, double angle_el) {
204+
239205
// Uq sign compensation
240-
angle = Uq > 0 ? angle : normalizeAngle( angle + M_PI );
241-
206+
float angle = angle_el + M_PI/2.0;
207+
// Uq sign compensation
208+
angle = normalizeAngle(Uq > 0 ? angle : angle + M_PI );
242209
// Park transform
243210
Ualpha = abs(Uq) * cos(angle);
244211
Ubeta = abs(Uq) * sin(angle);
245-
246-
// determine the segment I, II, III
247-
// section I
248-
Ua = Ualpha;
249-
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta;
250-
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta;
251-
252-
// set phase voltages
253-
setPwm(pwmA, Ua);
254-
setPwm(pwmB, Ub);
255-
setPwm(pwmC, Uc);
256-
}
257-
258-
/*
259-
Method using FOC to set Uq to the motor at the optimal angle
260-
*/
261-
void BLDCMotor::setPhaseVoltage(double Uq, double angle_el) {
212+
262213
switch (driver) {
263-
case DriverType::bipolar :
264-
// L6234
265-
setPhaseVoltageBipolar(Uq, angle_el);
214+
case DriverType::full_bridge :
215+
// full Clarke transform
216+
Ua = Ualpha;
217+
Ub = -0.5 * Ualpha + _SQRT3_2 * Ubeta;
218+
Uc = -0.5 * Ualpha - _SQRT3_2 * Ubeta;
266219
break;
267-
case DriverType::unipolar :
268-
// HMBGC
269-
setPhaseVoltageUnipolar(Uq, angle_el);
220+
case DriverType::half_bridge :
221+
// HMBGC & L6234
222+
// Unipolar Clarke transform
223+
// determine the segment I, II, III
224+
if ((angle >= 0) && (angle <= _120_D2R)) {
225+
// section I
226+
Ua = Ualpha + _1_SQRT3 * Ubeta;
227+
Ub = _2_SQRT3 * Ubeta;
228+
Uc = 0;
229+
} else if ((angle > _120_D2R) && (angle <= (2 * _120_D2R))) {
230+
// section III
231+
Ua = 0;
232+
Ub = _1_SQRT3 * Ubeta - Ualpha;
233+
Uc = -_1_SQRT3 * Ubeta - Ualpha;
234+
} else if ((angle > (2 * _120_D2R)) && (angle <= (3 * _120_D2R))) {
235+
// section II
236+
Ua = Ualpha - _1_SQRT3 * Ubeta;
237+
Ub = 0;
238+
Uc = - _2_SQRT3 * Ubeta;
239+
}
270240
break;
271241
}
242+
243+
// set phase voltages
244+
setPwm(pwmA, Ua);
245+
setPwm(pwmB, Ub);
246+
setPwm(pwmC, Uc);
272247
}
273248

274249

@@ -280,23 +255,21 @@ void BLDCMotor::setPwm(int pinPwm, float U) {
280255
int U_max = power_supply_voltage;
281256
// uniploar or bipolar FOC
282257
switch (driver) {
283-
case DriverType::bipolar :
258+
case DriverType::full_bridge :
284259
// sets the voltage [-U_max,U_max] to pwm [0,255]
285-
// - U_max you can set in header file - default 12V
286-
// - support for L6234 drive
287260
U_pwm = ((float)U + (float)U_max) / (2.0 * (float)U_max) * 255.0;
288261
break;
289-
case DriverType::unipolar :
262+
case DriverType::half_bridge :
290263
// HMBGC
291264
// sets the voltage [0,12V(U_max)] to pwm [0,255]
292-
// - U_max you can set in header file - default 12V
293265
// - support for HMBGC controller
294-
U_pwm = 255.0 * (float)U / (float)U_max;
266+
// - support for L6234 drive
267+
U_pwm = (int)(255.0 * (float)U / (float)U_max);
295268
break;
296269
}
297270
// limit the values between 0 and 255;
298271
U_pwm = U_pwm < 0 ? 0 : U_pwm;
299-
U_pwm = U_pwm > 255 ? 255 : U_pwm;
272+
U_pwm = U_pwm >= 255 ? 255 : U_pwm;
300273

301274
// write hardware pwm
302275
analogWrite(pinPwm, U_pwm);
@@ -325,7 +298,7 @@ float BLDCMotor::velocityPI(float ek) {
325298

326299
// u(s) = Kr(1 + 1/(Ti.s))
327300
float uk = PI_velocity.uk_1;
328-
uk += PI_velocity.K * (Ts / (2 * PI_velocity.Ti) + 1) * ek + PI_velocity.K * (Ts / (2 * PI_velocity.Ti) - 1) * PI_velocity.ek_1;
301+
uk += PI_velocity.K * (Ts / (2.0 * PI_velocity.Ti) + 1.0) * ek + PI_velocity.K * (Ts / (2.0 * PI_velocity.Ti) - 1.0) * PI_velocity.ek_1;
329302
if (abs(uk) > PI_velocity.u_limit) uk = uk > 0 ? PI_velocity.u_limit : -PI_velocity.u_limit;
330303

331304
PI_velocity.uk_1 = uk;

0 commit comments

Comments
 (0)