Skip to content

Commit 5065c84

Browse files
Merge pull request #48 from Candas1/flux_observer
MXLemming flux observer
2 parents c8174f5 + 4a091f0 commit 5065c84

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include "MXLEMMINGObserverSensor.h"
2+
#include "common/foc_utils.h"
3+
#include "common/time_utils.h"
4+
5+
6+
MXLEMMINGObserverSensor::MXLEMMINGObserverSensor(const FOCMotor& m) : _motor(m)
7+
{
8+
// Derive Flux linkage from KV_rating and pole_pairs
9+
if (_isset(_motor.pole_pairs) && _isset(_motor.KV_rating)){
10+
flux_linkage = 60 / ( _SQRT3 * _PI * _motor.KV_rating * _motor.pole_pairs * 2);
11+
}
12+
}
13+
14+
15+
void MXLEMMINGObserverSensor::update() {
16+
// Current sense is required for the observer
17+
if (!_motor.current_sense) return;
18+
19+
// Exit if one of the parameter needed for the flux observer is 0
20+
if ((_motor.phase_inductance == 0) ||
21+
(_motor.phase_resistance == 0) ||
22+
(flux_linkage == 0)) return;
23+
24+
// Update sensor, with optional downsampling of update rate
25+
if (sensor_cnt++ < sensor_downsample) return;
26+
27+
sensor_cnt = 0;
28+
29+
// read current phase currents
30+
PhaseCurrent_s current = _motor.current_sense->getPhaseCurrents();
31+
32+
// calculate clarke transform
33+
ABCurrent_s ABcurrent = _motor.current_sense->getABCurrents(current);
34+
35+
// get current timestamp
36+
long now_us = _micros();
37+
// calculate the sample time from last call
38+
float dt = (now_us - angle_prev_ts) * 1e-6f;
39+
// quick fix for strange cases (micros overflow + timestamp not defined)
40+
if(dt <= 0 || dt > 0.5f) dt = 1e-3f;
41+
42+
// This work deviates slightly from the BSD 3 clause licence.
43+
// The work here is entirely original to the MESC FOC project, and not based
44+
// on any appnotes, or borrowed from another project. This work is free to
45+
// use, as granted in BSD 3 clause, with the exception that this note must
46+
// be included in where this code is implemented/modified to use your
47+
// variable names, structures containing variables or other minor
48+
// rearrangements in place of the original names I have chosen, and credit
49+
// to David Molony as the original author must be noted.
50+
51+
// MXLEMMING Flux Observer
52+
float resistive_term_a = _motor.phase_resistance * ABcurrent.alpha;
53+
float resistive_term_b = _motor.phase_resistance * ABcurrent.beta;
54+
float inductive_term_a = _motor.phase_inductance * (ABcurrent.alpha - i_alpha_prev);
55+
float inductive_term_b = _motor.phase_inductance * (ABcurrent.beta - i_beta_prev);
56+
57+
flux_alpha = _constrain( flux_alpha + (_motor.Ualpha - resistive_term_a) * dt - inductive_term_a ,-flux_linkage, flux_linkage);
58+
flux_beta = _constrain( flux_beta + (_motor.Ubeta - resistive_term_b) * dt - inductive_term_b ,-flux_linkage, flux_linkage);
59+
60+
// Calculate electrical angle
61+
electrical_angle = _normalizeAngle(_atan2(flux_beta,flux_alpha));
62+
63+
// Electrical angle difference
64+
float d_electrical_angle = electrical_angle - electrical_angle_prev;
65+
if(abs(d_electrical_angle) > _2PI * 0.8 ){ //change the factor based on sample rate can also just use _PI for simplicity
66+
if (d_electrical_angle > 0){
67+
d_electrical_angle -= _2PI;
68+
}else{
69+
d_electrical_angle += _2PI;
70+
}
71+
}
72+
angle_track += d_electrical_angle;
73+
74+
// Mechanical angle and full_rotations
75+
float full_rotation = _2PI * _motor.pole_pairs;
76+
if(abs(angle_track) > full_rotation){
77+
if (angle_track>0){
78+
full_rotations += 1;
79+
angle_track -= full_rotation;
80+
}else{
81+
full_rotations -= 1;
82+
angle_track += full_rotation;
83+
}
84+
}
85+
angle_prev = angle_track /_motor.pole_pairs;
86+
87+
// Store Previous values
88+
i_alpha_prev = ABcurrent.alpha;
89+
i_beta_prev = ABcurrent.beta;
90+
angle_prev_ts = now_us;
91+
electrical_angle_prev = electrical_angle;
92+
93+
}
94+
95+
void MXLEMMINGObserverSensor::init(){
96+
this->Sensor::init(); // call base class
97+
}
98+
99+
/*
100+
Shaft angle calculation
101+
*/
102+
float MXLEMMINGObserverSensor::getSensorAngle(){
103+
return 0;
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef MXLEMMING_OBSERVER_SENSOR_H
2+
#define MXLEMMING_OBSERVER_SENSOR_H
3+
4+
#include "Arduino.h"
5+
#include "common/base_classes/FOCMotor.h"
6+
#include "common/base_classes/Sensor.h"
7+
8+
/**
9+
10+
*/
11+
12+
class MXLEMMINGObserverSensor : public Sensor
13+
{
14+
public:
15+
/**
16+
MXLEMMINGObserverSensor class constructor
17+
@param m Motor that the MXLEMMINGObserverSensor will be linked to
18+
*/
19+
MXLEMMINGObserverSensor(const FOCMotor& m);
20+
void update() override;
21+
22+
void init() override;
23+
24+
// Abstract functions of the Sensor class implementation
25+
/** get current angle (rad) */
26+
float getSensorAngle() override;
27+
28+
29+
// For sensors with slow communication, use these to poll less often
30+
unsigned int sensor_downsample = 0; // parameter defining the ratio of downsampling for sensor update
31+
unsigned int sensor_cnt = 0; // counting variable for downsampling
32+
float flux_alpha = 0; // Flux Alpha
33+
float flux_beta = 0; // Flux Beta
34+
float flux_linkage = 0; // Flux linkage, calculated based on KV and pole number
35+
float i_alpha_prev = 0; // Previous Alpha current
36+
float i_beta_prev = 0; // Previous Beta current
37+
float electrical_angle = 0; // Electrical angle
38+
float electrical_angle_prev = 0; // Previous electrical angle
39+
float angle_track = 0; // Total Electrical angle
40+
41+
protected:
42+
const FOCMotor& _motor;
43+
44+
};
45+
46+
#endif
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# MXLEMMING Observer Sensor
2+
3+
The MXLEMMING Obserser has been ported from the MESC Firmware, it's also the default Flux observer in Vesc firmware.
4+
The [MESC book](https://davidmolony.github.io/MESC_Firmware/operation/CONTROL.html#the-sensorless-observer) explains the math behind this flux observer.
5+
6+
It's a simple solution for sensorless motor control only using phase currents and motor parameters, if tracking the position at low speed and when not driving the motor is not relevant.
7+
8+
### Motor Parameters:
9+
The MXLEMMING Observer needs the following motor parameters to be set:
10+
- phase resistance
11+
- KV rating
12+
- phase inductance
13+
- pole pairs
14+
15+
It will not track the position if any of those parameters are missing.
16+
17+
The KV rating and pole pairs parameters are used to derive the motor flux linkage which is key for the flux observer to run well.
18+
```
19+
#include <encoders/MXLEMMING_observer/MXLEMMINGObserverSensor.h>
20+
21+
BLDCMotor motor = BLDCMotor(15, 0.1664, 17.0, 0.00036858); // Hoverboard Motor
22+
MXLEMMINGObserverSensor sensor = MXLEMMINGObserverSensor(motor);
23+
```
24+
flux_linkage parameter can be adjusted from the code.
25+
26+
### Current Sense
27+
The current sense is required as this flux observer only relies on phase currents.
28+
29+
### Sensor Alignment
30+
The flux observer sensor doesn't need sensor alignment.
31+
```
32+
motor.sensor_direction= Direction::CW;
33+
motor.zero_electric_angle = 0;
34+
```
35+
36+
### To do:
37+
- The Clarke transform is running both in the loopFOC and in the sensor update now, it can be remove from the sensor when the Alpha and Beta currents will be persisted as a BLDCMotor member
38+
- The flux observer is calculating the electrical angle directly, but SimpleFOC needs to derive the electrical angle from the sensor angle for the FOC calculation
39+

0 commit comments

Comments
 (0)