Skip to content

Commit cab7af5

Browse files
committed
calibration update
1 parent ab90dba commit cab7af5

File tree

4 files changed

+59
-43
lines changed

4 files changed

+59
-43
lines changed

examples/encoders/calibrated_sensor/calibration_save/calibration_save.ino

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#include "encoders/calibrated/CalibratedSensor.h"
99

1010
// fill this array with the calibration values outputed by the calibration procedure
11-
float calibrationLut[100] = {0};
11+
float calibrationLut[50] = {0};
12+
float zero_electric_angle = 0;
13+
Direction sensor_direction = Direction::CW;
1214

1315
// magnetic sensor instance - SPI
1416
MagneticSensorSPI sensor = MagneticSensorSPI(AS5147_SPI, 14);
@@ -20,7 +22,7 @@ StepperDriver4PWM driver = StepperDriver4PWM(10, 9, 5, 6,8);
2022
// argument 1 - sensor object
2123
// argument 2 - number of samples in the LUT (default 200)
2224
// argument 3 - pointer to the LUT array (defualt nullptr)
23-
CalibratedSensor sensor_calibrated = CalibratedSensor(sensor, 100, calibrationLut);
25+
CalibratedSensor sensor_calibrated = CalibratedSensor(sensor, 50);
2426

2527
// voltage set point variable
2628
float target_voltage = 2;
@@ -56,16 +58,12 @@ void setup() {
5658
motor.init();
5759

5860
// Running calibration
59-
// as the Lookup table (LUT) has been provided as an argument this function will not do anything
60-
sensor_calibrated.calibrate(motor);
61+
// the function will setup everything for the provided calibration LUT
62+
sensor_calibrated.calibrate(motor, calibrationLut, zero_electric_angle, sensor_direction);
6163

6264
// Linking sensor to motor object
6365
motor.linkSensor(&sensor_calibrated);
6466

65-
// write the sensor direction and zero electrical angle outputed by the calibration
66-
motor.sensor_direction = Direction::CW; // replace with the value outputed by the calibration
67-
motor.zero_electric_angle = 0.0; // replace with the value outputed by the calibration
68-
6967
// calibrated init FOC
7068
motor.initFOC();
7169

src/encoders/calibrated/CalibratedSensor.cpp

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
// CalibratedSensor()
44
// sensor - instance of original sensor object
55
// n_lut - number of samples in the LUT
6-
// lut - pointer to the LUT array
7-
CalibratedSensor::CalibratedSensor(Sensor &wrapped, int n_lut, float* lut) : _wrapped(wrapped) {
6+
CalibratedSensor::CalibratedSensor(Sensor &wrapped, int n_lut) : _wrapped(wrapped) {
87
this->n_lut = n_lut;
9-
this->calibrationLut = lut;
108
};
119

1210
CalibratedSensor::~CalibratedSensor() {
@@ -36,7 +34,7 @@ float CalibratedSensor::getSensorAngle()
3634
// Calculate the resolution of the LUT in radians
3735
float lut_resolution = _2PI / n_lut;
3836
// Calculate LUT index
39-
int lut_index = raw_angle / lut_resolution + bucket_offset;
37+
int lut_index = raw_angle / lut_resolution;
4038

4139
// Get calibration values from the LUT
4240
float y0 = calibrationLut[lut_index];
@@ -86,49 +84,59 @@ void CalibratedSensor::filter_error(float* error, float &error_mean, int n_ticks
8684

8785
}
8886

89-
void CalibratedSensor::calibrate(FOCMotor &motor)
87+
void CalibratedSensor::calibrate(FOCMotor &motor, float* lut, float zero_electric_angle, Direction senor_direction)
9088
{
91-
92-
if(this->calibrationLut != nullptr){
93-
motor.monitor_port->println("Using existing LUT.");
89+
// if the LUT is already defined, skip the calibration
90+
if(lut != nullptr){
91+
motor.monitor_port->println("Using the provided LUT.");
92+
motor.zero_electric_angle = zero_electric_angle;
93+
motor.sensor_direction = senor_direction;
94+
this->calibrationLut = lut;
9495
return;
9596
}else{
9697
this->calibrationLut = new float[n_lut]();
9798
motor.monitor_port->println("Starting Sensor Calibration.");
9899
}
100+
101+
// Calibration variables
99102

100103
// Init inital angles
101104
float theta_actual = 0.0;
102105
float avg_elec_angle = 0.0;
106+
// set the inital electric angle to 0
107+
float elec_angle = 0.0;
103108

109+
// Calibration parameters
110+
// The motor will take a n_pos samples per electrical cycle
111+
// which amounts to n_ticks (n_pos * motor.pole_pairs) samples per mechanical rotation
112+
// Additionally, the motor will take n2_ticks steps to reach any of the n_ticks posiitons
113+
// incrementing the electrical angle by deltaElectricalAngle each time
104114
int n_pos = 5;
105115
int _NPP = motor.pole_pairs; // number of pole pairs which is user input
106116
const int n_ticks = n_pos * _NPP; // number of positions to be sampled per mechanical rotation. Multiple of NPP for filtering reasons (see later)
107117
const int n2_ticks = 5; // increments between saved samples (for smoothing motion)
108118
float deltaElectricalAngle = _2PI * _NPP / (n_ticks * n2_ticks); // Electrical Angle increments for calibration steps
109-
float error[n_ticks] = {0.0}; // pointer to error array (average of forward & backward)
110-
const int window = 5; // window size for moving average filter of raw error
111-
// set the electric angle to 0
112-
float elec_angle = 0.0;
119+
float error[n_ticks] = {0.0}; // pointer to error array (average of forward & backward)
120+
// The fileter window size is set to n_pos - one electrical cycle
121+
// important for cogging filtering !!!
122+
const int window = n_pos; // window size for moving average filter of raw error
123+
113124

114125
// find the first guess of the motor.zero_electric_angle
115126
// and the sensor direction
116127
// updates motor.zero_electric_angle
117128
// updates motor.sensor_direction
118-
bool skip_align_current_sense = false;
119-
if(motor.current_sense != nullptr){
120-
skip_align_current_sense = motor.current_sense->skip_align;
121-
motor.current_sense->skip_align = true;
122-
}
129+
// temporarily unlink the sensor and current sense
130+
CurrentSense *current_sense = motor.current_sense;
131+
motor.current_sense = nullptr;
123132
motor.linkSensor(&this->_wrapped);
124133
if(!motor.initFOC()){
125134
motor.monitor_port->println("Failed to align the sensor.");
126135
return;
127136
}
128-
if(motor.current_sense != nullptr){
129-
motor.current_sense->skip_align = skip_align_current_sense;
130-
}
137+
// link back the sensor and current sense
131138
motor.linkSensor(this);
139+
motor.linkCurrentSense(current_sense);
132140

133141
// Set voltage angle to zero, wait for rotor position to settle
134142
// keep the motor in position while getting the initial positions
@@ -177,8 +185,12 @@ void CalibratedSensor::calibrate(FOCMotor &motor)
177185
}
178186
zero_angle_prev = zero_angle;
179187
avg_elec_angle += zero_angle/n_ticks;
180-
}
181188

189+
motor.monitor_port->print(">error:");
190+
motor.monitor_port->println(zero_angle);
191+
motor.monitor_port->print(">zero_angle:");
192+
motor.monitor_port->println(avg_elec_angle);
193+
}
182194
_delay(2000);
183195

184196
/*
@@ -212,6 +224,11 @@ void CalibratedSensor::calibrate(FOCMotor &motor)
212224
}
213225
zero_angle_prev = zero_angle;
214226
avg_elec_angle += zero_angle/n_ticks;
227+
228+
motor.monitor_port->print(">error:");
229+
motor.monitor_port->println(zero_angle);
230+
motor.monitor_port->print(">zero_angle:");
231+
motor.monitor_port->println(avg_elec_angle);
215232
}
216233

217234
// get post calibration mechanical angle.

src/encoders/calibrated/CalibratedSensor.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class CalibratedSensor: public Sensor{
1111

1212
public:
1313
// constructor of class with pointer to base class sensor and driver
14-
CalibratedSensor(Sensor& wrapped, int n_lut = 200, float* lut = nullptr);
14+
CalibratedSensor(Sensor& wrapped, int n_lut = 200);
1515

1616
~CalibratedSensor();
1717

@@ -23,11 +23,10 @@ class CalibratedSensor: public Sensor{
2323
/**
2424
* Calibrate method computes the LUT for the correction
2525
*/
26-
virtual void calibrate(FOCMotor& motor);
26+
virtual void calibrate(FOCMotor& motor, float* lut = nullptr, float zero_electric_angle = 0.0, Direction senor_direction= Direction::CW);
2727

2828
// voltage to run the calibration: user input
29-
float voltage_calibration = 1;
30-
int bucket_offset = 0;
29+
float voltage_calibration = 1;
3130
protected:
3231

3332
/**
@@ -48,7 +47,7 @@ class CalibratedSensor: public Sensor{
4847
void alignSensor(FOCMotor &motor);
4948
void filter_error(float* error, float &error_mean, int n_ticks, int window);
5049

51-
// lut size, currently constan. Perhaps to be made variable by user?
50+
// lut size - settable by the user
5251
int n_lut { 200 } ;
5352
// create pointer for lut memory
5453
// depending on the size of the lut

src/encoders/calibrated/README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Please see the more complete [example](https://github.com/simplefoc/Arduino-FOC-
8484
## EDIT March 2025
8585

8686
The code has been rewritten to reduce its memory footprint and allow more flexible Lookup table (LUT) sizing.
87-
Additionally, the calibrated sensor class now supports providing the saved LUT as a parameter to the constructor. This allows you to save the LUT and load it on startup to avoid recalibration on each startup.
87+
Additionally, the calibrated sensor class now supports providing the saved LUT as a paramer for calibration. This allows you to save the LUT and load it on startup to avoid recalibration on each startup.
8888

8989
The LUT and sensor's zero angle and direction are outputed by the calibration process to the Serial terminal. So you can copy and paste them into your code.
9090

@@ -96,29 +96,31 @@ Your code will look something like this:
9696
const N_LUT = 100;
9797
// Lookup table that has been ouptut from the calibration process
9898
float calibrationLut[N_LUT] = {...};
99+
float zero_eletrical_angle = 0.0;
100+
Direction sensor_direction = Direction::CW;
99101

100102
// provide the sensor class and the number of points in the LUT
101-
CalibratedSensor sensor_calibrated = CalibratedSensor(sensor, N_LUT, calibrationLut);
103+
CalibratedSensor sensor_calibrated = CalibratedSensor(sensor, N_LUT);
102104

103105
...
104106

105107
void setup() {
106108
...
107-
// as LUT is provided this function does not need to be called
108-
sensor_calibrated.calibrate(motor);
109+
// as LUT is provided to this function
110+
sensor_calibrated.calibrate(motor, calibrationLut, zero_eletrical_angle, sensor_direction);
109111
...
110112

111113
motor.linkSensor(&sensor_calibrated);
112114

113-
114-
// write the sensor direction and zero electrical angle outputed by the calibration
115-
motor.sensor_direction = Direction::CW; // replace with the value outputed by the calibration
116-
motor.zero_electric_angle = 0.0; // replace with the value outputed by the calibration
117-
118115
...
119116
motor.initFOC();
120117
....
121118
}
122119

123120

124121
```
122+
123+
## Future work
124+
125+
- Reduce the LUT size by using a more efficient LUT type - maybe pass to uint16_t
126+
- Use a more eficient LUT interpolation method - maybe a polynomial interpolation

0 commit comments

Comments
 (0)