Skip to content

Commit 4b37d87

Browse files
author
Padok
committed
Merge branch 'dev' of github.com:simplefoc/Arduino-FOC
2 parents dfa64dc + 4ed0be7 commit 4b37d87

File tree

27 files changed

+184
-155
lines changed

27 files changed

+184
-155
lines changed

examples/utils/calibration/find_sensor_offset_and_direction/find_sensor_offset_and_direction.ino

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* Simple example intended to help users find the zero offset and natural direction of the sensor.
33
*
44
* These values can further be used to avoid motor and sensor alignment procedure.
5-
*
6-
* motor.initFOC(zero_offset, sensor_direction);
5+
* To use these values add them to the code:");
6+
* motor.sensor_direction=Direction::CW; // or Direction::CCW
7+
* motor.zero_electric_angle=1.2345; // use the real value!
78
*
89
* This will only work for abosolute value sensors - magnetic sensors.
910
* Bypassing the alignment procedure is not possible for the encoders and for the current implementation of the Hall sensors.
@@ -44,6 +45,9 @@ void setup() {
4445
// set motion control loop to be used
4546
motor.controller = MotionControlType::torque;
4647

48+
// force direction search - because default is CW
49+
motor.sensor_direction = Direction::UNKNOWN;
50+
4751
// initialize motor
4852
motor.init();
4953
// align sensor and start FOC
@@ -54,9 +58,16 @@ void setup() {
5458
Serial.println("Sensor zero offset is:");
5559
Serial.println(motor.zero_electric_angle, 4);
5660
Serial.println("Sensor natural direction is: ");
57-
Serial.println(motor.sensor_direction == 1 ? "Direction::CW" : "Direction::CCW");
61+
Serial.println(motor.sensor_direction == Direction::CW ? "Direction::CW" : "Direction::CCW");
62+
63+
Serial.println("To use these values add them to the code:");
64+
Serial.print(" motor.sensor_direction=");
65+
Serial.print(motor.sensor_direction == Direction::CW ? "Direction::CW" : "Direction::CCW");
66+
Serial.println(";");
67+
Serial.print(" motor.zero_electric_angle=");
68+
Serial.print(motor.zero_electric_angle, 4);
69+
Serial.println(";");
5870

59-
Serial.println("To use these values provide them to the: motor.initFOC(offset, direction)");
6071
_delay(1000);
6172
Serial.println("If motor is not moving the alignment procedure was not successfull!!");
6273
}

src/BLDCMotor.cpp

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ BLDCMotor::BLDCMotor(int pp, float _R, float _KV, float _inductance)
4444
phase_resistance = _R;
4545
// save back emf constant KV = 1/KV
4646
// 1/sqrt(2) - rms value
47-
KV_rating = _KV*_SQRT2;
47+
KV_rating = NOT_SET;
48+
if (_isset(_KV))
49+
KV_rating = _KV*_SQRT2;
4850
// save phase inductance
4951
phase_inductance = _inductance;
5052

@@ -124,20 +126,13 @@ void BLDCMotor::enable()
124126
FOC functions
125127
*/
126128
// FOC initialization function
127-
int BLDCMotor::initFOC( float zero_electric_offset, Direction _sensor_direction) {
129+
int BLDCMotor::initFOC() {
128130
int exit_flag = 1;
129131

130132
motor_status = FOCMotorStatus::motor_calibrating;
131133

132134
// align motor if necessary
133135
// alignment necessary for encoders!
134-
if(_isset(zero_electric_offset)){
135-
// abosolute zero offset provided - no need to align
136-
zero_electric_angle = zero_electric_offset;
137-
// set the sensor direction - default CW
138-
sensor_direction = _sensor_direction;
139-
}
140-
141136
// sensor and motor alignment - can be skipped
142137
// by setting motor.sensor_direction and motor.zero_electric_angle
143138
_delay(500);
@@ -211,7 +206,7 @@ int BLDCMotor::alignSensor() {
211206
if(!exit_flag) return exit_flag;
212207

213208
// if unknown natural direction
214-
if(!_isset(sensor_direction)){
209+
if(sensor_direction==Direction::UNKNOWN){
215210

216211
// find natural direction
217212
// move one electrical revolution forward
@@ -418,7 +413,8 @@ void BLDCMotor::move(float new_target) {
418413
// angle set point
419414
shaft_angle_sp = target;
420415
// calculate velocity set point
421-
shaft_velocity_sp = P_angle( shaft_angle_sp - shaft_angle );
416+
shaft_velocity_sp = feed_forward_velocity + P_angle( shaft_angle_sp - shaft_angle );
417+
shaft_angle_sp = _constrain(shaft_angle_sp,-velocity_limit, velocity_limit);
422418
// calculate the torque command - sensor precision: this calculation is ok, but based on bad value from previous calculation
423419
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control
424420
// if torque controlled through voltage
@@ -543,12 +539,8 @@ void BLDCMotor::setPhaseVoltage(float Uq, float Ud, float angle_el) {
543539
case FOCModulationType::SinePWM :
544540
// Sinusoidal PWM modulation
545541
// Inverse Park + Clarke transformation
542+
_sincos(angle_el, &_sa, &_ca);
546543

547-
// angle normalization in between 0 and 2pi
548-
// only necessary if using _sin and _cos - approximation functions
549-
angle_el = _normalizeAngle(angle_el);
550-
_ca = _cos(angle_el);
551-
_sa = _sin(angle_el);
552544
// Inverse park transform
553545
Ualpha = _ca * Ud - _sa * Uq; // -sin(angle) * Uq;
554546
Ubeta = _sa * Ud + _ca * Uq; // cos(angle) * Uq;

src/BLDCMotor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class BLDCMotor: public FOCMotor
4949
* Function initializing FOC algorithm
5050
* and aligning sensor's and motors' zero position
5151
*/
52-
int initFOC( float zero_electric_offset = NOT_SET , Direction sensor_direction = Direction::CW) override;
52+
int initFOC() override;
5353
/**
5454
* Function running FOC algorithm in real-time
5555
* it calculates the gets motor angle and sets the appropriate voltages

src/StepperMotor.cpp

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,13 @@ void StepperMotor::enable()
9292
FOC functions
9393
*/
9494
// FOC initialization function
95-
int StepperMotor::initFOC( float zero_electric_offset, Direction _sensor_direction ) {
95+
int StepperMotor::initFOC() {
9696
int exit_flag = 1;
9797

9898
motor_status = FOCMotorStatus::motor_calibrating;
9999

100100
// align motor if necessary
101101
// alignment necessary for encoders!
102-
if(_isset(zero_electric_offset)){
103-
// abosolute zero offset provided - no need to align
104-
zero_electric_angle = zero_electric_offset;
105-
// set the sensor direction - default CW
106-
sensor_direction = _sensor_direction;
107-
}
108-
109102
// sensor and motor alignment - can be skipped
110103
// by setting motor.sensor_direction and motor.zero_electric_angle
111104
_delay(500);
@@ -307,7 +300,8 @@ void StepperMotor::move(float new_target) {
307300
// angle set point
308301
shaft_angle_sp = target;
309302
// calculate velocity set point
310-
shaft_velocity_sp = P_angle( shaft_angle_sp - shaft_angle );
303+
shaft_velocity_sp = feed_forward_velocity + P_angle( shaft_angle_sp - shaft_angle );
304+
shaft_angle_sp = _constrain(shaft_angle_sp, -velocity_limit, velocity_limit);
311305
// calculate the torque command
312306
current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control
313307
// if torque controlled through voltage
@@ -357,12 +351,9 @@ void StepperMotor::move(float new_target) {
357351
void StepperMotor::setPhaseVoltage(float Uq, float Ud, float angle_el) {
358352
// Sinusoidal PWM modulation
359353
// Inverse Park transformation
354+
float _sa, _ca;
355+
_sincos(angle_el, &_sa, &_ca);
360356

361-
// angle normalization in between 0 and 2pi
362-
// only necessary if using _sin and _cos - approximation functions
363-
angle_el = _normalizeAngle(angle_el);
364-
float _ca = _cos(angle_el);
365-
float _sa = _sin(angle_el);
366357
// Inverse park transform
367358
Ualpha = _ca * Ud - _sa * Uq; // -sin(angle) * Uq;
368359
Ubeta = _sa * Ud + _ca * Uq; // cos(angle) * Uq;

src/StepperMotor.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,8 @@ class StepperMotor: public FOCMotor
5454
* and aligning sensor's and motors' zero position
5555
*
5656
* - If zero_electric_offset parameter is set the alignment procedure is skipped
57-
*
58-
* @param zero_electric_offset value of the sensors absolute position electrical offset in respect to motor's electrical 0 position.
59-
* @param sensor_direction sensor natural direction - default is CW
60-
*
6157
*/
62-
int initFOC( float zero_electric_offset = NOT_SET , Direction sensor_direction = Direction::CW) override;
58+
int initFOC() override;
6359
/**
6460
* Function running FOC algorithm in real-time
6561
* it calculates the gets motor angle and sets the appropriate voltages

src/common/base_classes/CurrentSense.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ float CurrentSense::getDCCurrent(float motor_electrical_angle){
3838
// if motor angle provided function returns signed value of the current
3939
// determine the sign of the current
4040
// sign(atan2(current.q, current.d)) is the same as c.q > 0 ? 1 : -1
41-
if(motor_electrical_angle)
42-
sign = (i_beta * _cos(motor_electrical_angle) - i_alpha*_sin(motor_electrical_angle)) > 0 ? 1 : -1;
41+
if(motor_electrical_angle) {
42+
float ct;
43+
float st;
44+
_sincos(motor_electrical_angle, &st, &ct);
45+
sign = (i_beta*ct - i_alpha*st) > 0 ? 1 : -1;
46+
}
4347
// return current magnitude
4448
return sign*_sqrt(i_alpha*i_alpha + i_beta*i_beta);
4549
}
@@ -78,8 +82,9 @@ DQCurrent_s CurrentSense::getFOCCurrents(float angle_el){
7882
}
7983

8084
// calculate park transform
81-
float ct = _cos(angle_el);
82-
float st = _sin(angle_el);
85+
float ct;
86+
float st;
87+
_sincos(angle_el, &st, &ct);
8388
DQCurrent_s return_current;
8489
return_current.d = i_alpha * ct + i_beta * st;
8590
return_current.q = i_beta * ct - i_alpha * st;

src/common/base_classes/FOCMotor.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,8 @@ class FOCMotor
104104
* and aligning sensor's and motors' zero position
105105
*
106106
* - If zero_electric_offset parameter is set the alignment procedure is skipped
107-
*
108-
* @param zero_electric_offset value of the sensors absolute position electrical offset in respect to motor's electrical 0 position.
109-
* @param sensor_direction sensor natural direction - default is CW
110-
*
111107
*/
112-
virtual int initFOC( float zero_electric_offset = NOT_SET , Direction sensor_direction = Direction::CW)=0;
108+
virtual int initFOC()=0;
113109
/**
114110
* Function running FOC algorithm in real-time
115111
* it calculates the gets motor angle and sets the appropriate voltages
@@ -155,6 +151,7 @@ class FOCMotor
155151

156152
// state variables
157153
float target; //!< current target value - depends of the controller
154+
float feed_forward_velocity = 0.0f; //!< current feed forward velocity
158155
float shaft_angle;//!< current motor angle
159156
float electrical_angle;//!< current electrical angle
160157
float shaft_velocity;//!< current motor velocity
@@ -208,7 +205,7 @@ class FOCMotor
208205
// sensor related variabels
209206
float sensor_offset; //!< user defined sensor zero offset
210207
float zero_electric_angle = NOT_SET;//!< absolute zero electric angle - if available
211-
int sensor_direction = NOT_SET; //!< if sensor_direction == Direction::CCW then direction will be flipped to CW
208+
Direction sensor_direction = Direction::CW; //!< default is CW. if sensor_direction == Direction::CCW then direction will be flipped compared to CW. Set to UNKNOWN to set by calibration
212209

213210
/**
214211
* Function providing BLDCMotor class with the

src/common/base_classes/Sensor.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ void Sensor::update() {
1818
float Sensor::getVelocity() {
1919
// calculate sample time
2020
float Ts = (angle_prev_ts - vel_angle_prev_ts)*1e-6;
21-
// TODO handle overflow - we do need to reset vel_angle_prev_ts
21+
if (Ts < 0.0f) { // handle micros() overflow - we need to reset vel_angle_prev_ts
22+
vel_angle_prev = angle_prev;
23+
vel_full_rotations = full_rotations;
24+
vel_angle_prev_ts = angle_prev_ts;
25+
return velocity;
26+
}
2227
if (Ts < min_elapsed_time) return velocity; // don't update velocity if deltaT is too small
2328

2429
velocity = ( (float)(full_rotations - vel_full_rotations)*_2PI + (angle_prev - vel_angle_prev) ) / Ts;

src/common/base_classes/Sensor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum Pullup : uint8_t {
4242
*
4343
*/
4444
class Sensor{
45+
friend class SmoothingSensor;
4546
public:
4647
/**
4748
* Get mechanical shaft angle in the range 0 to 2PI. This value will be as precise as possible with

src/common/foc_utils.cpp

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,52 @@
11
#include "foc_utils.h"
22

3-
// int array instead of float array
4-
// 4x200 points per 360 deg
5-
// 2x storage save (int 2Byte float 4 Byte )
6-
// sin*10000
7-
const int sine_array[200] = {0,79,158,237,316,395,473,552,631,710,789,867,946,1024,1103,1181,1260,1338,1416,1494,1572,1650,1728,1806,1883,1961,2038,2115,2192,2269,2346,2423,2499,2575,2652,2728,2804,2879,2955,3030,3105,3180,3255,3329,3404,3478,3552,3625,3699,3772,3845,3918,3990,4063,4135,4206,4278,4349,4420,4491,4561,4631,4701,4770,4840,4909,4977,5046,5113,5181,5249,5316,5382,5449,5515,5580,5646,5711,5775,5839,5903,5967,6030,6093,6155,6217,6279,6340,6401,6461,6521,6581,6640,6699,6758,6815,6873,6930,6987,7043,7099,7154,7209,7264,7318,7371,7424,7477,7529,7581,7632,7683,7733,7783,7832,7881,7930,7977,8025,8072,8118,8164,8209,8254,8298,8342,8385,8428,8470,8512,8553,8594,8634,8673,8712,8751,8789,8826,8863,8899,8935,8970,9005,9039,9072,9105,9138,9169,9201,9231,9261,9291,9320,9348,9376,9403,9429,9455,9481,9506,9530,9554,9577,9599,9621,9642,9663,9683,9702,9721,9739,9757,9774,9790,9806,9821,9836,9850,9863,9876,9888,9899,9910,9920,9930,9939,9947,9955,9962,9969,9975,9980,9985,9989,9992,9995,9997,9999,10000,10000};
83

94
// function approximating the sine calculation by using fixed size array
10-
// ~40us (float array)
11-
// ~50us (int array)
12-
// precision +-0.005
13-
// it has to receive an angle in between 0 and 2PI
14-
float _sin(float a){
15-
if(a < _PI_2){
16-
//return sine_array[(int)(199.0f*( a / (_PI/2.0)))];
17-
//return sine_array[(int)(126.6873f* a)]; // float array optimized
18-
return 0.0001f*sine_array[_round(126.6873f* a)]; // int array optimized
19-
}else if(a < _PI){
20-
// return sine_array[(int)(199.0f*(1.0f - (a-_PI/2.0) / (_PI/2.0)))];
21-
//return sine_array[398 - (int)(126.6873f*a)]; // float array optimized
22-
return 0.0001f*sine_array[398 - _round(126.6873f*a)]; // int array optimized
23-
}else if(a < _3PI_2){
24-
// return -sine_array[(int)(199.0f*((a - _PI) / (_PI/2.0)))];
25-
//return -sine_array[-398 + (int)(126.6873f*a)]; // float array optimized
26-
return -0.0001f*sine_array[-398 + _round(126.6873f*a)]; // int array optimized
27-
} else {
28-
// return -sine_array[(int)(199.0f*(1.0f - (a - 3*_PI/2) / (_PI/2.0)))];
29-
//return -sine_array[796 - (int)(126.6873f*a)]; // float array optimized
30-
return -0.0001f*sine_array[796 - _round(126.6873f*a)]; // int array optimized
5+
// uses a 65 element lookup table and interpolation
6+
// thanks to @dekutree for his work on optimizing this
7+
__attribute__((weak)) float _sin(float a){
8+
// 16bit integer array for sine lookup. interpolation is used for better precision
9+
// 16 bit precision on sine value, 8 bit fractional value for interpolation, 6bit LUT size
10+
// resulting precision compared to stdlib sine is 0.00006480 (RMS difference in range -PI,PI for 3217 steps)
11+
static uint16_t sine_array[65] = {0,804,1608,2411,3212,4011,4808,5602,6393,7180,7962,8740,9512,10279,11039,11793,12540,13279,14010,14733,15447,16151,16846,17531,18205,18868,19520,20160,20788,21403,22006,22595,23170,23732,24279,24812,25330,25833,26320,26791,27246,27684,28106,28511,28899,29269,29622,29957,30274,30572,30853,31114,31357,31581,31786,31972,32138,32286,32413,32522,32610,32679,32729,32758,32768};
12+
unsigned int i = (unsigned int)(a * (64*4*256 /_2PI));
13+
int t1, t2, frac = i & 0xff;
14+
i = (i >> 8) & 0xff;
15+
if (i < 64) {
16+
t1 = sine_array[i]; t2 = sine_array[i+1];
17+
}
18+
else if(i < 128) {
19+
t1 = sine_array[128 - i]; t2 = sine_array[127 - i];
20+
}
21+
else if(i < 192) {
22+
t1 = -sine_array[-128 + i]; t2 = -sine_array[-127 + i];
3123
}
24+
else {
25+
t1 = -sine_array[256 - i]; t2 = -sine_array[255 - i];
26+
}
27+
return (1.0f/32768.0f) * (t1 + (((t2 - t1) * frac) >> 8));
3228
}
3329

3430
// function approximating cosine calculation by using fixed size array
3531
// ~55us (float array)
3632
// ~56us (int array)
3733
// precision +-0.005
3834
// it has to receive an angle in between 0 and 2PI
39-
float _cos(float a){
35+
__attribute__((weak)) float _cos(float a){
4036
float a_sin = a + _PI_2;
4137
a_sin = a_sin > _2PI ? a_sin - _2PI : a_sin;
4238
return _sin(a_sin);
4339
}
4440

4541

42+
__attribute__((weak)) void _sincos(float a, float* s, float* c){
43+
*s = _sin(a);
44+
*c = _cos(a);
45+
}
46+
47+
4648
// normalizing radian angle to [0,2PI]
47-
float _normalizeAngle(float angle){
49+
__attribute__((weak)) float _normalizeAngle(float angle){
4850
float a = fmod(angle, _2PI);
4951
return a >= 0 ? a : (a + _2PI);
5052
}
@@ -57,15 +59,13 @@ float _electricalAngle(float shaft_angle, int pole_pairs) {
5759
// square root approximation function using
5860
// https://reprap.org/forum/read.php?147,219210
5961
// https://en.wikipedia.org/wiki/Fast_inverse_square_root
60-
float _sqrtApprox(float number) {//low in fat
61-
long i;
62-
float y;
62+
__attribute__((weak)) float _sqrtApprox(float number) {//low in fat
6363
// float x;
6464
// const float f = 1.5F; // better precision
6565

6666
// x = number * 0.5F;
67-
y = number;
68-
i = * ( long * ) &y;
67+
float y = number;
68+
long i = * ( long * ) &y;
6969
i = 0x5f375a86 - ( i >> 1 );
7070
y = * ( float * ) &i;
7171
// y = y * ( f - ( x * y * y ) ); // better precision

0 commit comments

Comments
 (0)