/
sensorless_estimator.cpp
108 lines (91 loc) · 4.42 KB
/
sensorless_estimator.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "odrive_main.h"
void SensorlessEstimator::reset() {
pll_pos_ = 0.0f;
vel_estimate_ = 0.0f;
V_alpha_beta_memory_[0] = 0.0f;
V_alpha_beta_memory_[1] = 0.0f;
flux_state_[0] = 0.0f;
flux_state_[1] = 0.0f;
}
bool SensorlessEstimator::update() {
// Algorithm based on paper: Sensorless Control of Surface-Mount Permanent-Magnet Synchronous Motors Based on a Nonlinear Observer
// http://cas.ensmp.fr/~praly/Telechargement/Journaux/2010-IEEE_TPEL-Lee-Hong-Nam-Ortega-Praly-Astolfi.pdf
// In particular, equation 8 (and by extension eqn 4 and 6).
// The V_alpha_beta applied immedietly prior to the current measurement associated with this cycle
// is the one computed two cycles ago. To get the correct measurement, it was stored twice:
// once by final_v_alpha/final_v_beta in the current control reporting, and once by V_alpha_beta_memory.
// PLL
// TODO: the PLL part has some code duplication with the encoder PLL
// Pll gains as a function of bandwidth
float pll_kp = 2.0f * config_.pll_bandwidth;
// Critically damped
float pll_ki = 0.25f * (pll_kp * pll_kp);
// Check that we don't get problems with discrete time approximation
if (!(current_meas_period * pll_kp < 1.0f)) {
error_ |= ERROR_UNSTABLE_GAIN;
axis_->error_ |= Axis::ERROR_SENSORLESS_ESTIMATOR_FAILED;
reset(); // Reset state for when the next valid current measurement comes in.
return false;
}
// TODO: we read values here which are modified by a higher priority interrupt.
// This is not thread-safe.
auto current_meas = axis_->motor_.current_meas_;
if (!axis_->motor_.is_armed_) {
// While the motor is disarmed the current is not measurable so we
// assume that it's zero.
current_meas = {0.0f, 0.0f};
}
if (!current_meas.has_value()) {
error_ |= ERROR_UNKNOWN_CURRENT_MEASUREMENT;
axis_->error_ |= Axis::ERROR_SENSORLESS_ESTIMATOR_FAILED;
reset(); // Reset state for when the next valid current measurement comes in.
return false;
}
// Clarke transform
float I_alpha_beta[2] = {
current_meas->phA,
one_by_sqrt3 * (current_meas->phB - current_meas->phC)};
// alpha-beta vector operations
float eta[2];
for (int i = 0; i <= 1; ++i) {
// y is the total flux-driving voltage (see paper eqn 4)
float y = -axis_->motor_.config_.phase_resistance * I_alpha_beta[i] + V_alpha_beta_memory_[i];
// flux dynamics (prediction)
float x_dot = y;
// integrate prediction to current timestep
flux_state_[i] += x_dot * current_meas_period;
// eta is the estimated permanent magnet flux (see paper eqn 6)
eta[i] = flux_state_[i] - axis_->motor_.config_.phase_inductance * I_alpha_beta[i];
}
// Non-linear observer (see paper eqn 8):
float pm_flux_sqr = config_.pm_flux_linkage * config_.pm_flux_linkage;
float est_pm_flux_sqr = eta[0] * eta[0] + eta[1] * eta[1];
float bandwidth_factor = 1.0f / pm_flux_sqr;
float eta_factor = 0.5f * (config_.observer_gain * bandwidth_factor) * (pm_flux_sqr - est_pm_flux_sqr);
// alpha-beta vector operations
for (int i = 0; i <= 1; ++i) {
// add observer action to flux estimate dynamics
float x_dot = eta_factor * eta[i];
// convert action to discrete-time
flux_state_[i] += x_dot * current_meas_period;
// update new eta
eta[i] = flux_state_[i] - axis_->motor_.config_.phase_inductance * I_alpha_beta[i];
}
// Flux state estimation done, store V_alpha_beta for next timestep
V_alpha_beta_memory_[0] = axis_->motor_.current_control_.final_v_alpha_;
V_alpha_beta_memory_[1] = axis_->motor_.current_control_.final_v_beta_;
float phase_vel = phase_vel_.previous().value_or(0.0f);
// predict PLL phase with velocity
pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * phase_vel);
// update PLL phase with observer permanent magnet phase
float phase = fast_atan2(eta[1], eta[0]);
float delta_phase = wrap_pm_pi(phase - pll_pos_);
pll_pos_ = wrap_pm_pi(pll_pos_ + current_meas_period * pll_kp * delta_phase);
// update PLL velocity
phase_vel += current_meas_period * pll_ki * delta_phase;
// set outputs
phase_ = phase;
phase_vel_ = phase_vel;
vel_estimate_ = phase_vel / (std::max((float)axis_->motor_.config_.pole_pairs, 1.0f) * 2.0f * M_PI);
return true;
};