Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Include motor inductance in voltage control. #246

Open
dzid26 opened this issue Jan 27, 2023 · 21 comments
Open

[FEATURE] Include motor inductance in voltage control. #246

dzid26 opened this issue Jan 27, 2023 · 21 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@dzid26
Copy link

dzid26 commented Jan 27, 2023

I want to just lay out the foundation for a possible improvement, and also get feedback to verify the idea.

Issue

Lemma 1
Current is proportional to forward torque but only when its vector is at a 90-degree electrical angle.
Lemma 2
The current always lags the rotating voltage vector.

In the present Voltage control scheme, a 90-degree electrical angle is not maintained when speed increases.
Currently, this is the control scheme

else voltage.q = target*phase_resistance + voltage_bemf;
voltage.q = _constrain(voltage.q, -voltage_limit, voltage_limit);
voltage.d = 0;
:
Vd=0
Vq=BEMF+ Itarget R
image
(Source)

Proposal

The voltage vector must be phase-shifted to compensate for the current vector lag. Since Park transform is already implemented, the phase lead angle can happen naturally by manipulating Vd. (White vector below)
The control should look like this:
image
Setting Vd=-Itarget * ω*RL will shift the effective current target vector to the desired 90deg. Only then will this current be proportional to torque.
ω is the electrical angular speed in rad/s. I.e. shaft speed * poles_pairs.
If the combined motor inductance RL is unknown, it can be set to 0.
From what I know, coil inductance can be an approximation of motor inductance, but I don't know how good. On the other hand, RLcan be found experimentally presumably by optimizing the value for maximum motor power.

References:

https://docs.simplefoc.com/voltage_torque_mode#voltage-control-with-current-estimation-and-back-emf-compensation
https://endless-sphere.com/sphere/threads/tsdz2-mid-drive-with-860c-850c-or-sw102-displays-only-flexible-opensource-firmware-casainho-code-only.93818/post-1376865
https://github.com/EGG-electric-unicycle/documentation/blob/master/Shane_Colton/3phduo.pdf - page 27.

@dzid26 dzid26 added the enhancement New feature or request label Jan 27, 2023
@runger1101001
Copy link
Member

Thank you for this report! It makes sense to me, but I would like to think what @askuric thinks as well.

It could be easily implemented, I think as a subclass of BLDCMotor, and if it works well eventually rolled into the main implementation.

As you point out, the main problem for users will be obtaining the value of the motor inductance if it is not given in the datasheet...

@askuric
Copy link
Member

askuric commented Jan 29, 2023

Very interesting @dzid26,
I like the idea very much.
And it goes hand-in-hand with the field weakening also. We could definitely implement this in the motor classes.
For the sensorless control we will need the inductance value as well and this enhancement could be an easy addition trough which we could start extending our API to allow users to set it.

I'm going to test this in code and report back, if the results are as expected we could make it a part of our next release.

@askuric
Copy link
Member

askuric commented Jan 29, 2023

Hey guys,
So I've had some time today and I've tested your proposal. It works like a charm, the voltage control (depending on the inductance value set) can reach much higher velocities than it could without it.
In my setup:

  • gbm4108H
  • 500cpr encoder
  • simplefocshield
  • stm32g474 nucleo
  • 20 V power supply - 12V voltage limit
    motor with the voltage control max velocity reached is:
  • L = 0 - 110rad/s
  • L = 0.01 - 130 rad/s

What's even more interesting is that using this simple approach the max velocity reached is higher than for the pure foc_current approach, which reaches the max velocity of around 120rad/s. foc_current approach has a longer loop time and that might be the issue. However foc_current approach reaches the 120rad/s velocity with much lower current than the any of the voltage control modes, which is natural as well.

So all in all, I am very happy with this addition and it sets a nice base for the future features. Thatnks a lot for your proposition and if you have the time to test the code that would be awesome as well.

For my motor the

// pole_pairs   : 11
// resitance     : 10 Ohm
// KV rating    : 110 
// inductance : 0.01 H
BLDCMotor motor = BLDCMotor(11, 10, 110, 0.01);

The phase inductance can be also set using

motor.phase_inductance = 0.001;

of in the Commander using the command I.
For example if you motor's command is M, then you can change the phase inductance

$ MI     
L phase: 0.01
$ MI0.001
L phase: 0.001

This is just an initial implementation though, and it might change till the next release, however I am fairly certain it will be a part of the simplefoc v2.3

@dzid26
Copy link
Author

dzid26 commented Jan 29, 2023

Amazing.
Maybe what is happening is that the motor starts to "experience" the field weakening and thus reaching higher speed.
I was thinking this could happen and that's why I was thinking the best way to find optimal L value is by maximizing peak power, that is under load.

I would love to try it. Funny thing is that I haven't run SimpleFOC on anything yet. So far I was just admiring simplicity and the good documentation :)

@runger1101001
Copy link
Member

This is very cool.

@runger1101001 runger1101001 added this to the 2.3_Release milestone Feb 5, 2023
@runger1101001 runger1101001 changed the title [FEATURE] Include motor inductance in voltage ocntrol. [FEATURE] Include motor inductance in voltage control. Feb 5, 2023
@greymfm
Copy link

greymfm commented Mar 11, 2023

Just trying to understand this brilliant idea - this is related to both stepper and BLDC motors, correct? :-)

@greymfm
Copy link

greymfm commented Mar 22, 2023

I have added the changes to my code, and tested it - it works beautifully :-)

@askuric
Copy link
Member

askuric commented Mar 23, 2023

Awesome!
Yeah the steppers have the same issue. However they do have one transformation less when we calculate the Park and Clarke, as they have only two phases.

In the release v2.3 we did include this change to the stepper code as well, so please do let us know if it works for you as well. 😄

@Candas1
Copy link
Collaborator

Candas1 commented Sep 8, 2023

Hi,

I used this feature with voltage mode and it works great.
I saw that the lag compensation code is commented out for foc_current mode, I assume that's what the PI control of D current does.

Now I think something is missing here, and that would also help with field weakening, I think it's called the circle limitation.
As you compensate the lag by increasing Vd, you should limit Vq.
This is how it is described in the STM32 FOC bible (chapter 4.12.1)
image
image

@Candas1
Copy link
Collaborator

Candas1 commented Sep 8, 2023

I think the way it works now is that the waveform is saturated by the voltage limit of the driver resulting with overmodulation ?

@Candas1
Copy link
Collaborator

Candas1 commented Sep 8, 2023

I don't fully understand what happens when you don't do this, but here is how it should be calculated:
image

And at the end of loopfoc, it should look like this:

// Circle limitation - Saturation with priority on d axis
  voltage.d = min(voltage.d,voltage_limit);
  voltage.q = min(voltage.q,_sqrt(voltage_limit*voltage_limit-voltage.d*voltage.d));

  // set the phase voltage - FOC heart function :)
  setPhaseVoltage(voltage.q, voltage.d, electrical_angle);

@dzid26
Copy link
Author

dzid26 commented Sep 9, 2023

It makes sense to me. I think it is easier to see in the stepper controller because it has just Parke transformation.

Ualpha = _ca * Ud - _sa * Uq; // -sin(angle) * Uq;
Ubeta = _sa * Ud + _ca * Uq; // cos(angle) * Uq;

The voltage can be exceeded when the angle is say 45 deg because cos+sin adds up to sqrt(2).

I am just not sure why Vd should be prioritized and how one magnetizes a rotor. I think that would apply to induction motors. Here, I think the geometrical limitation from STM32 FOC bible (chapter 4.12.1) would be more appropriate.
image

@Candas1
Copy link
Collaborator

Candas1 commented Sep 9, 2023

Actually this is in chapter 4.8 about field weakening:
image

@Candas1
Copy link
Collaborator

Candas1 commented Sep 10, 2023

I did an experiment.
I changed the foc_current mode and removed the q axis PI so I can directly drive the q axis voltage, so it's like a foc_voltage mode.
The d axis PI is compensating the phase lag as the lag compensation does, and I displayed what the lag_compensation would be (-target * velocity * pole_pairs * phase_inductance).
image

I tweaked the phase__inductance to get the d voltage and lag_compensation close.
I found out that my phase_inductance was half what it should be.
That's maybe a way to roughly estimate the phase inductance.

@Candas1
Copy link
Collaborator

Candas1 commented Sep 12, 2023

I came across a nice example here.

It shows that increasing vd not only shifts the waveform, but also increases the magnitude.
image
image

SimpleFOC is limiting the magnitude in the driver by doing clipping.

I believe this approach is meant to limit the magnitude without clipping, while prioritizing d axis for efficiency.
You want to prioritize d axis to keep an optimal angle and not to sacrifice efficiency when reaching the voltage limit.

Sorry for all the posts, I am sharing as I learn more about the topic.

@dzid26
Copy link
Author

dzid26 commented Sep 12, 2023

I came across a nice example here.

That's a cool stack exchange answer.

You want to prioritize d axis to keep an optimal angle and not to sacrifice efficiency when reaching the voltage limit

I think if you start to prioritize one over another then you effectively rotate the frame reference.

I think it should be:
Vd_limited = min(Vd, Vd * voltage_limit / max(voltage_limit , sqrt(Vd^2 + Vq^2)))
Vq_limited = min(Vq, Vq * voltage_limit / max(voltage_limit , sqrt(Vd^2 + Vq^2)))
as described in stm foc book.

@Candas1
Copy link
Collaborator

Candas1 commented Sep 12, 2023

Look at this comment in VESC

@runger1101001 @askuric please let me know if you would be interested in this in the future, it will be useful for MTPA and FW.

@askuric
Copy link
Member

askuric commented Sep 12, 2023

Hey @Candas1 and @dzid26,
This is a great great discussion.
I've had the issue of Vq and Vd limiting in the back of mind from when I first implemented it :D
I would love to include this solution to SimpleFOC!

@Candas1
Copy link
Collaborator

Candas1 commented Sep 13, 2023

I am happy to help. I will test more and share.

This simple example from stackoverflow also made me understand why we need decoupling.

@dzid26
Copy link
Author

dzid26 commented Dec 3, 2023

I came across cool diagram that is an extension of the vector adding visualisation from the my first post, but with field weakening (negative Id current).
Phase-Diagram-of-PMSM-with-Field-Weakening-Mode-If-Id-is-positive-ie-When-the-phase

Analytical Tool to Generate Torque-Speed Characteristics for Surface Mounted PM Machines in Constant Torque and Field Weakening Regions

@dzid26
Copy link
Author

dzid26 commented May 17, 2024

I believe this code should be improved to prioritize BEMF voltage when limiting to supply voltage:

else voltage.q = target*phase_resistance + voltage_bemf;
voltage.q = _constrain(voltage.q, -voltage_limit, voltage_limit);
// set d-component (lag compensation if known inductance)
if(!_isset(phase_inductance)) voltage.d = 0;
else voltage.d = _constrain( -target*shaft_velocity*pole_pairs*phase_inductance, -voltage_limit, voltage_limit);

The idea is that the remaining of the Uq would then be the actual IR voltage. From that, the actual Iq (and actual torque!) can be back-calculated. Then the actual Iq shall be used with IqwL.

Here is a simplified c code from my other project that does that.

U_emf_sat = clip(U_emf, -U_lim, U_lim);
U_IR_sat = clip(U_IR, -U_lim - U_emf_sat, U_lim - U_emf_sat);
U_IR_sat = clip(U_IR_sat, -U_lim, U_lim);  //limit again to cover all cases
U_q = U_IR_sat + U_emf_sat;
I_q_act = (U_IR_sat * Ohm_to_mOhm / phase_R);
U_d = (-I_q_act) * phase_L * e_rad_s / H_to_uH;

That's what I am using currently and it's good enough for me (but I don't care about high speed too much). It's better than limiting without the prioritization, but it doesn't solve issue brought by @Candas1 about exceeding the limit circle. The exceeded voltage can be seen on this visualization (X is rev/s and Y is Voltage). We'd need more advance limit circle limiting to avoid that.

I mentioned earlier a relatively simple circle limiting. But as can be seen now here above the 10rev/s the Uq stops increasing (it's slowly decreasing for the given parameters) and there is too much Ud voltage compared to the actual Iq. This would cause premature field weakening - good for the speed, but would reduce available torque/power. I would rather leave it at as I did it in the code above with simple bemf priority.

As to prioritizing Vd, this doesn't make sense to me. If Iq drops (which it will when crossing base speed), so should the Vd. I believe, that even when limiting for the limit circle the U_emf should be prioritized to remain true to FOC theory (at least in the context of Id=0, aka below field weakening).
Unfortunately, when prioritizing for U_emf and staying within limit circle, the calculation becomes much more difficult and self-referencing. - It has annoying square roots, so I don't think it can be solved algebraically. - It would require multiple iterations to find solution - (i.e limit the outcome, recalculate Park transform or calculate square roots, then limiting again - few times). It would be ugly. But there might be away to offload this iterative calculation offline (or after motor parameters i.e. induction, Kbemf, are identified) and store as a lookup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants