Open dzid26 opened 1 year ago
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...
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.
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:
L = 0
- 110rad/sL = 0.01
- 130 rad/sWhat'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
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 :)
This is very cool.
Just trying to understand this brilliant idea - this is related to both stepper and BLDC motors, correct? :-)
I have added the changes to my code, and tested it - it works beautifully :-)
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. :smile:
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)
I think the way it works now is that the waveform is saturated by the voltage limit of the driver resulting with overmodulation ?
I don't fully understand what happens when you don't do this, but here is how it should be calculated:
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);
It makes sense to me. I think it is easier to see in the stepper controller because it has just Parke transformation. https://github.com/simplefoc/Arduino-FOC/blob/05954cb9691534dd478407505d810a68f1673a5e/src/StepperMotor.cpp#L367-L368 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.
Actually this is in chapter 4.8 about field weakening:
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).
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.
I came across a nice example here.
It shows that increasing vd not only shifts the waveform, but also increases the magnitude.
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.
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.
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.
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!
I am happy to help. I will test more and share.
This simple example from stackoverflow also made me understand why we need decoupling.
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).
I believe this code should be improved to prioritize BEMF voltage when limiting to supply voltage: https://github.com/simplefoc/Arduino-FOC/blob/c32bdd1e52c64b109d36a93378303e0068c7926c/src/StepperMotor.cpp#L300-L304
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.
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 https://github.com/simplefoc/Arduino-FOC/blob/45219c428074f2b8a54ba6b8a7b8caab5f334538/src/StepperMotor.cpp#L297-L299: Vd=0 Vq=BEMF+ Itarget R
(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:
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.