I was measuring the resistance of a hoverboard motor using a VESC 6 MKIII. The value I get is about 140 mΩ.
I also tested the motor directly on my power supply. 0.4 V phase-phase gives 1 Amp, meaning the phase-zero resistance is about 200 mΩ (400 mV / 1 A / 2).
As the difference was quite large I started to check some things. When checking the VQ and IQ in the realtime data after the RL measurement I manually calculate close to 200 mΩ (Rs=Vq/Iq for stationary motor and current). Then in the source code I found the following in mcpwm_foc_measure_resistance:
return (voltage_avg / current_avg) * (2.0 / 3.0);
I do not think this factor 2/3 should be used here. The voltage and current in the dq domain divided by each other directly gives you the phase-zero resistance. If this factor 2/3 was not there the value would have been 210 mΩ, much closer to my measured value.
I also see the same factor used in the inductance measurement. But then, when the inductance value is used for the PMSM decoupling a factor 3/2 is applied again, making it correct there. However, as the current controller parameters use the R and L with the factor 2/3 the current controller has a control bandwidth (zero-dB crossing) of 2/3th of the setting in the calculation screen.
I hope I am wrong and the code is correct, can somebody please explain the factor 2/3?
PS. The controller and software are really awesome, great job Benjamin!
I checked more of the source code to get a better understanding. It looks very good, but I think there is improvement possible in some of the formulas. Here are my thoughts:
As far as I know the typical hobby BLDC motors have nearly sinusoidal back EMF. This means they can be described well using PMSM equations.
PMSM equations taken from mathworks.com/help/physmod/sps/ref/pmsm.html:
Rs is the stator resistance (phase-zero). For explanation of the other symbols check the link.
During the resistance measurement theLqdiqdt+Nω(idLd+ψm) terms are all zero. What remains is vq=Rsiq =>Rs= vq / iq
So, as far as I can see, there should be no scaling of 2/3 of the result for a PMSM.
In the code the inductance is also scaled with a factor 2/3. In the cross decoupling code actually the inductance is multiplied by 3/2, which I also did not expect (see the equations above, there is no factor 3/2 in a PMSM). However, because the inductance is reduced by a factor 2/3, the result should still be correct.
dec_vq = state_m->id * state_m->speed_rad_s * conf_now->foc_motor_l * (3.0 / 2.0);
The calculation for flux linkage also seems to try to correct for the factor 2/3, but instead of multiplying by 3/2 as in the decoupling a multiplication of 2/3 is used. This causes the estimation to incorrectly compensate for the resistive and inductive components, surely making the results invalid if the current is not small.
*linkage = (v_mag - (2.0 / 3.0) * res * i_mag) / rad_s - (2.0 / 3.0) * i_mag * ind;
Looking back to the PMSM equations the formula used also seems a bit strange. I am not 100% sure about this but it seems more logical to me to use the following formulas instead:
Nω = ωe (electrical speed [rad/s])
ψm= (vq-Rsiq) / ωe - idLd
The formula is similar in structure (except for the factor 2/3), but now the (I think) correct currents and voltages are used. I think this can improve the motor identification, especially in cases where the current is not small.
Very interested to hear your thoughts on all this. Would also be happy to help / test on my VESC.
Interesting discussion. For others who might like to follow along, here are the links in the code:
For the code authors' consideration, I believe there's also something to learn from this. The code is dependent on delicate math implementation, but little of it is documented. The upshot is that it becomes arduous to back out the theoretical underpinnings, and thus nigh impossible to evaluate and review the code. Consider this a kind coaxing for anyone implementing math to please, please, please document the crap out of it. Papers, relevant websites, the math written out in plain text, whatever it takes to make it very easy for readers to understand where the code is coming from. This will help when we're either curious to understand-- e.g. because we think we might have found an error--, or we believe we have a better algo and would like to give it a shot.
I think I found where the factor 2/3 comes into play. If you look at the following document about the PMSM by NXP:
you can find that if you measure the inductance (but also resistance) like shown in figure 7 (page 9), you need to take into account a factor 2/3. This is because there is 1 inductor (or resistor) in series with 2 inductors (or resistors) in parallel.
This is however not the way the VESC should calculate R or L, because the VESC measures using vd and vq, which are not phase-phase quantities.
Agree on the idea to better document where the used formulas come from. Thanks for the nice Github links, I did not know that was possible like that. Learn something new every day.
I'm not familiar enough with PMSM motor param identification to have a well-formed opinion on the 2/3 scalar (although what you say makes a lot of sense). However I do notice a couple numerical issues in the calibration code. Both the R and the L depend on accumulators which are defined as floats. Unfortunately, the shear number of samples stresses the ability of a float to faithfully capture the summations.
In other words, a `float` can only handle a value with 2^23 = 8.4 million, which means almost 7 significant digits. That sounds like a lot, but since the R calibration uses 10,000 measurements, and the L calibration uses 20,000 measurements, then 8.4e6/10e4 = 840. So this is fewer than three significant digits for raw measurements, and barely more than two for L. I'd have to understand how much precision the ADC has to know for sure if it's an issue. However, if the `float` is indeed running out of precision, this would lead to a smaller estimate (because the `float accumulator doesn't increase but the `int` divisor still does) which would also be consistent with your experience.
I looked at the math, and I think your point rings true. If the system is measuring phase-to-phase current, then phase-zero resistance is 0.5*voltage/(phase-to-phase current). In other words, V_d/I_d. So where is the 2/3 term coming from in https://github.com/vedderb/bldc/blob/812f5302ca7078330f4cd293abb9d33fb04...?
Yes, that is correct. However the vd vq id and iq are all related to phase values, not phase-phase.
Phase quantities are sine waves (120 degrees apart) for a rotating motor. The (stationary) vd vq id and iq values are relate 1to1 to the peak of these sine waves. For example, if iq is 2 and id is zero (and the motor is rotating), you will have sine waves of current in the phases, with an amplitude of 2 Ampere. Similarly, a vq of 5 volt relates to sine waves (120 degrees apart) with an amplitude of 5 volt each.
But to get back to the resistance calculation, if you look to the formulas in the Mathworks link I sent, you will find that the resistance observed in the dq domain is exactly the same as the resistance of a single phase Rs.
Note: there exists also a power invariant transform, then the relation is different. However, the vesc uses the amplitude invariant transformation, making the above explanation valid.
I did some simulations with single precision floating point numbers to check the point you raised about the precision. The way the floating point numbers are added like this introduces a small error of less then 0.1%, I would not worry about it.
What is important is that you use sufficient current for the measurment, othewise the quantization of the adc can become a problem. But I think this is done correctly in the vesc.
Thanks for looking into this, it always helps to have some extra eyes on these things.
When I looked at the equations I came to the conclusion that the Clarke transform (3-phase to 2-phase) introduces the 2/3 compared to what you would measure between two motor wires, and I thought it would be useful to have the value as what you would see with a multimeter and a motor. I verified the 2/3 by running quite high current in a motor with a power supply and measure the voltage drop across it, which matched up with the 2/3 at that time. This was a long time ago though and there might have been some dead time distortion that affected my measurements, so it is possible that I got that wrong.
I also set up simulations of the whole system based on the virtual motor code contributed by axiom to make sure that everything is correct and adds up in the code. This is why the 2/3 is removed in the observer and the axis decoupling (as you saw with the axis decoupling):
I also verified the flux linkage calculation, and I'm fairly confident that it is correct and things add up the way they are now. That being said, all of the 2/3s in the detection, observer and decoupling code could be removed, and it would still work correctly.
One thing I'm aware of that is not correct is the time constant for the current controller parameter calculation, it will be off by a factor of 2/3. That is not too important though, it only matters that it is not too fast and not too slow and what is correct on each motor depends on the rest of the system. Most systems work well with how it is calculated now, and I have not prioritized to update it yet.
Interesting observation about the float precision, it might make sense to average over fewer samples in a few places.
About writing down the math, I personally often have an easier time reading code than mathematical notations (depending on both how the code and math is written obviously). Keeping two sets of the same equations updated is also a burden, and there are plenty of sources where one can read about motor math out there that should match the code more or less. Therefore I haven't spent any time making another writeup about motor math, but I encourage anyone who is interested to go through the code and document what is going in a different format. I'm sure that makes it easier to understand and follow for many people who are not like me.
I looked into this today with a VESC that has phase filters to work around the dead time distortion, and the 2/3 is not supposed to be there. The compensation for that was done correctly in the code at least, so everything should have been working with the exception of the PI controller time constant being calculated correctly.
I will work more on it in the weekend and report back.
Thank you for the response!
Was the factor 2/3 in any way related to the dead time distortion? I do not see the link there.
I did the check of measuring the phase-phase resistance of my motor using my lab power supply and the result was as follows:
I = 3 A
Vph-ph = 1.61 V
Rph-ph = 1.61/3 = 0.54 Ohm
Rph-0 = Rph-ph / 2 = 0.27 Ohm.
VESC gives Rph-0 = 0.174 Ohm
However, if the factor 2/3 was not there, it would have given 0.26 Ohm, pretty much spot on compared to the 0.27 Ohm.
I did some more checking on the flux linkage detection and found that it is indeed correct, but depending on which option you choose.
Because here the (incorrect) resistance is corrected with a factor 3/2:
(although inductance cross coupling is ignored, I think this is not a large issue)
not correct: measure_linkage_openloop
Here actually there is another factor 2/3 instead of 3/2 (making R actually (2/3)² too low), making the flux linkage overestimated.
Interested to hear what you find!
Can you try this with a motor with very low resistance?
I think we also might have a problem that with low resistance motors <10mOhm. The on resistance of the mosfets will start to make quite a large impact on the result.
For the math to work should we only be wanting the motor resistance / inductance. Or do we also need to take into account the whole powerstage?
If we only want the motor resistance then maybe we need a setting in the hwconf that defines the on resistance per phase and that gets factored into the detection?
measure_linkage_openloop looks at both the driven and undriven flux linkage, and when it is used the undriven one is always preferred as it does not depend on the resistance and inductance. Only on motors with low inertia and high friction that does not work as the motors stops so fast that the flux linkage cannot be measured from the back emf alone, and then the driven flux linkage that has to be compensated for resistance, inductance and current is used. That being said, that is an extremely rare case and most motors measure the flux linkage in the undriven way just fine.
The problem with the formula that contains id and iq is that it assumes you know them, which you only do if you know the motor phase. All you know in openloop is the magnitude as you don't have observer parameters yet, so you cannot calculate the flux linkage properly. I know the formula is wrong, it just gets close enough in some cases.
About the power stage resistance, that should be included in the parameters. What matters is what the motor, power stage, connectors and cables look like together to track the motor well, including the voltage and current tolerances on the hardware. So the best resistance to use for good tracking and operation is the one you see when running the detection, not what you measure with an accurate LCR meter.
Thank you for the response. Ok, that about the measure_linkage_openloop makes sense. I was not aware that the undriven value was taken.
I was looking at the inductance measurement, which made me check out the hfi, both in code and practice. Let me start of by saying that I really like the idea and implementation, great job!
However, I think I did find a discrepancy in the math (on top of the factor 2/3), which makes the inductance measured to be lower.
In this part:
motor->m_hfi.buffer[motor->m_hfi.ind] = ((hfi_voltage / 2.0 - conf_now->foc_motor_r *
current_sample) / (conf_now->foc_f_sw * current_sample));
I think you try to calculate back the inductance by L = (delta_V - R*delta_i) / (delta_i / delta_t).
However, I think there are three problems here:
1. delta_V should be hfi_voltage * 2, not hfi_voltage /2 (the step in voltage is from -hfi_voltage to +hfi_voltage and visaversa, making the step 2 * hfi_voltage )
2. foc_motor_r should have the factor 3/2, unless you remove the factor 2/3 we talked about earlier. Of course this is automatically fixed if the 2/3 is removed the the R calculation as suggested.
3. I have a feeling the conf_now->foc_f_sw parameter is 1 divided by the set switching frequency. However, when measured on an external scope, one finds that the real switching frequency is actually halve of what you set. This causes a factor 2 error.
My suggestion then would be:
motor->m_hfi.buffer[motor->m_hfi.ind] = ((hfi_voltage * 2.0 - conf_now->foc_motor_r *
current_sample) / (conf_now->foc_f_sw * 2 * current_sample));
Which actually doubles the measured induction.
I know my industrial servo motor specifications pretty wel, Lq = 260 uH, Ld = 220 uH. However, when measuring on the VESC it gives measurements in the range of 70 to 90 uH. Correcting for both the 2/3 factor and the above factor 2 the measurement actually would be 210 uH to 270 uH, which does make a lot of sense to me.
Really interested to hear what you think about this.
PS. Great job for making all this work!
> Interesting observation about the float precision, it might make sense to average over fewer samples in a few places.
I made a PR to convert certain floats to doubles. The use of doubles is limited to the calibration routines, so there shouldn't be any material impact on normal performance.
> Can you try this with a motor with very low resistance?
I can take a look with a Geiger HPD20. It has with published specs of 3.2mOhms and 8.7uH. According to the factory, they measure resistance with a 60A current and they measure inductance at 30kHz.
> However, if the factor 2/3 was not there, it would have given 0.26 Ohm, pretty much spot on compared to the 0.27 Ohm.
I have a similar problem with my Geiger's detected resistance. It is somewhere about 2/3 of what the manufacturer spec says it should be. This weekend I'll hook it up to my bench supply to validate.
> Correcting for both the 2/3 factor and the above factor 2 the measurement actually would be 210 uH to 270 uH, which does make a lot of sense to me.
I'm eager to see the results of this. I documented at https://vesc-project.com/node/3216 where I get a 3x smaller estimate vs the manufacturer published inductance spec.
I will do some more testing with the HFI and inductance measurement in the next days and report back.
Instead of testing things with the hfi, wouldn't it make more sense to discuss the physics/math first? The behaviour can also easily be simulated. I would be happy to assist, for example in a Teams call or forum discussion, just let me know whether you are interested.
We can do that as I go, but I have to get into the HFI-code again and understand exactly what is going on again. There are so many details that I had to get right to make it work at all, I spent a full week implementing it doing nothing else. How do deal with sampling in V0/V7, aligning samples, making the calculation current invariant, transitioning in and out of HFI, making it run fast, using a good amount of samples, how much current to get the initial samples etc. It took me 2 hours to get a proof of concept running, but making it work well and useful was a lot of work. I have turned every stone around 10 times already.
The tricky thing with datasheets is that they don't always define resistance and inductance in the same way as the code expects it. It is quite easy to see if it is correct though with a testbench that can load a motor and has an encoder. If the inductance is wrong the observer angle will deviate from the encoder angle as the load and speed increases.
I agree that the details in getting everything to work right is very complicated and most of the work. However the basic concept and math is relatively easy to understand and should be the simple part to get right. I think getting these basics right should be prio 1. The good thing is you got the hard stuff all figured out already!
For example the factor 2 in the hfi injection. I think the math and physics should show whether this is needed. But, since you have gotten this far with your project, I am sure you know best on how to tackle this.
Good luck in testing!
Have you found time to check it out? Any comments on the formula?
I checked out the code a bit deeper and I think I found a flaw in my explanation.
I now see you seem to apply the hfi voltage for only halve the time between two current measurements. That would mean the voltage step is 1x hfi, not 2x hfi. Still do not see why the step would be hfi/2 though.
I was working on it yesterday, and that is the same conclusion I came to. All 2/3 and 3/2 are gone, and both resistance and inductance measurements are within a few percent of my LCR meter. The only confusing thing left is the division by 2 in the hfi (I didn't spend too much time in that yet though). That being said, I still have to do a lot of testing with the updated inductance measurement to make sure that motors run well under load. I will report back soon.
To make things clear for myself I made some hand calculations with just an inductance which shows the VESC calculation is underestimating by a factor 2. Either v_hfi should not be divided by 2, or foc_f_sw needs to be divided by 2:
To also include a resistance component I used a Matlab script to simulate and come to the same conclusion:
So actually in the end the factor v_hfi /2 is fine, but the foc_f_sw needs to be divided by 2 (just as shown by hand calculation with just L).
Matlab code for anyone interested:
TAU = 1 ./ logspace(0 , 4 , 20);
for jmodel = 1:length(TAU) %s time constant of electrical system (tau = L/R)
tau = TAU( jmodel );
L = 100e-6; %[H] Inductance (in the direction the hfi is injecting)
R = L / tau; %[Ohm] Resistance (in the direction the hfi is injecting)
v_hfi = 1; %[V] voltage level for hfi
v_bus = 48; %[V]
foc_f_sw = 30e3; %[Hz]
t = linspace( 0 , 2/foc_f_sw , 1e5); %[s] Time vector
v = zeros(size(t));
% v(t< 1/foc_f_sw) = v_hfi; %[V] input voltage vector. v_hfi for half the time of foc_f_sw.
v(t< (1/foc_f_sw) * v_hfi / v_bus) = v_bus; %[V] input voltage vector. Takes into account bus voltage.
sys = tf( 1 , [L R]); %Transer function of system I/V = 1/(Ls+R)
i = lsim( sys , v , t ); %[A] Simulate current
% subplot( 2, 1 , 1)
% plot( t*1e6 , v , 'linewidth' , 2 )
% hold all
% ylabel('Voltage [V]')
% xlabel('Time [\mus]')
% grid on
% subplot( 2, 1 , 2)
% plot( t*1e6 , i , 'linewidth' , 2 )
% hold all
% xlabel('Time [μs]')
% ylabel('Current [A]')
% grid on
di = i(end) - i(1);
L_est1(jmodel) = (v_hfi - R * di) / (foc_f_sw * di );
L_est2(jmodel) = (v_hfi/2 - R * di) / (foc_f_sw/2 * di );
L_est_vesc(jmodel) = (v_hfi/2 - R * di) / (foc_f_sw * di );
semilogx( TAU([1 end]) , [L L] *1e6 , 'linewidth' , 2 , 'Color' , 'Black')
semilogx( TAU , L_est1 *1e6 , 'linewidth' , 2 )
semilogx( TAU , L_est2 *1e6, 'linewidth' , 2 )
semilogx( TAU , L_est_vesc *1e6 , 'linewidth' , 2 )
xlabel('Electrical time contant (L/R) [s]')
legend( '"Real" L value: 100 μH' , 'Option1: L = (v_hfi - R * di) / (foc_f_sw * di )', 'Proposal: L = (v_hfi/2 - R * di) / (foc_f_sw/2 * di )' , 'VESC now: (v_hfi/2 - R * di) / (foc_f_sw * di )' )
title('hfi indutance estimation simulation (foc\_f\_sw = 30 kHz)')
Nice work here! Ben and I have been investigating this and I did some more work with your matlab script. Thanks!
First step was to get the positive and negative pulses being applied:
Looking here we can see the real issue is the exponential rolloff since the samples are being taken at the center of the off time here:
so I made a very ugly drawing:
And tried to find a closed form solution using the solution to the differential equation of an RL circuit as described here
Unfortunately that was an absolute mess and I am fairly certain no easy solution exists. So instead I opted for a fixed point solution. We can start with our existing estimate for R and L and solve for ix1 and ix2 in the picture above. Then I solved the response between ix1 and ix2 with the applied negative bus voltage for L. Iterate through this a few times updating L and hopefully we converge. I got lazy and used symbolic to solve the exponential equation it and the result is this:
Then the total fixed point thing looks like this:
The mean part is just picking off the points and obviously isn't needed here in this world of perfect precision.
The results at high and low duty:
Can see the compensated line is falling dead on hooray!
Full code below:
Wow, you did some nice work there! Seems to be a very good estimation like that. However, we should not forget that even if you can estimate L correctly, having these enormous current ripples is pretty bad for your performance and efficiency. Motors which such a low time constant really beg for much higher switching frequencies. That being said, having a good estimation for L seems pretty helpful to me.
As promised, today I finished fully measuring another of my industrial motors on a BK Precision 891 LCR meter and the VESC 6 MK III. The results are as follows:
BK Precision 891 LCR meter:
Rphase-zero = 258 mOhm (Raw measurement 516 mOhm phase-phase)
Ld = 167 μH (Raw measurement between phase 1 and 2,3 together 250 μH with rotor aligned to D axis. Ld is 2/3 of this measurement, see NXP PMSM document)
Lq = 212 μH (Raw measurement between phase 1 and 2,3 together 318 μH with rotor aligned to Q axis. Lq is 2/3 of this measurement, see NXP PMSM document)
Lavg = (Lq + Ld)/2 = 189.5 μH
LdLqdiff = 45 μH
Rphase-zero = 165,66 mΩ (= 248.5 mΩ if the 2/3 is removed)
Lavg = 66,14 µH (= 99.21 µH if the 2/3 is removed, 198.4 µH if the L estimation is also fixed)
ld_lq_diff: 7.33 uH (= 11 µH if the 2/3 is removed, 22 µH if the L estimation is also fixed)
Note how everything will be very close if the corrections are made, expect for the ld_lq difference (unless you define it as offset from the average to both sides, but that seems to not be the variable name). I Think you need to double the ld_lq_diff measurement and do the other fixes described earlier and everything seems to be very close.
I had a question about the HFI rotor tracking. Does it assume the higher inductance is the D axis or the Q axis? I see for my motor the Q axis has a higher induction, but this can also be the other way around, see for example the rotor configurations in: https://en.engineering-solutions.ru/motorcontrol/pmsm/.
In my previous post I already mentioned the ld_lq difference should be increased by a factor of 2. I checked the code and found out why this happens:
The FFT of the signal is taken and then divided by the number of samples. Then correctly the abs of this value is taken. However, what seems to be forgotten is the FFT of a frequency component gives 2 components in the frequency domain, one at the positive frequency, and one at the negative frequency. For any real input signal (without any imaginary part) the component at the negative frequency is the same value as the positive frequency. To get back the amplitude of the time domain signal one has to add the negative and positive frequency components. To achieve this one can simply multiply all (except for the DC component) results by 2. This factor 2 seems to be missing in the code, explaining why the ld_lq difference is a factor 2 too low.
Afterwards there correctly is a factor 2 to go from zero-peak to peak-peak.
Example figures of what happens in case the amplitude is 1:
Matlab code which shows the effect:
Everything looks correct on my end now, the only mystery left is the /2 in the HFI code. For now I will just ignore it and continue testing.
This is a known inductor Jeff made that we used for testing. It shows the same value (within a few percent) in both my LCR meter and the VESC detection.
I also tested one of my outrunners and carefully measured the lowest and highest inductance with the LCR meter to get the average inductance and ld_lq_diff (at 1 kHz, 10 kHz). The VESC is very close to this one too (half of what is measured between 2 phases, which should correspond to one winding).
After that I did some testing on a known difficult motor: the T-motor U15 with a large propeller
It worked better than ever now at up 250 phase amps (iq). Before I had to tweak the inductance, but now I just took the default values and increased the observer gain by a factor of 4.
I got some promising results with another difficult motor too, but on this one I had to halve the observer gain.
Next I will connect an encoder to my dual motor rig and see if the observer angle drifts away from the encoder angle as I increase the load and speed - that should give an indication if the inductance is correct.
Everything is pushed on github and the latest VESC Tool beta version with the updated firmwares is updated here: https://vesc-project.com/node/2859
Please give it a try and let me know how it works!
Great job, looks very promising! Good to hear that things are working even better, and with less tweaking!
I have a few small remaining remarks on the code changes:
You removed the scaling here,
but now your real voltage is a factor 1.5 higher than what you use in the calculation. The scaling should be like you have in other parts of the code:
(although I really don't understand the 2/3 that is used, but I checked and the outcome with the SVM function gives the correct amplitudes for the phases)
I think this factor 1.5 is why the results are now very close, without fixing the L calculation function.
Power loss in alpha-beta and dq domain are given by 1.5*i*i*R, not 2*i*i*R.
For a given i, the 3 phase currents are sine waves with i as amplitude. the RMS value of these waves is equal to i / sqrt(2). The power loss for each of these is (i / sqrt(2))^2* R. Since you have 3 phases you end up with Ploss = 1.5*i*i*R.
These lines need scaling, or your alpha-beta voltages can be higher than you can make without overmodulation (you correcty removed the 2/3, but you need scaling there):
The correct scaling between duty cycle and dq or alpha-beta voltages is: duty * mc_interface_get_input_voltage_filtered() * ONE_BY_SQRT3.
This is because the largest sine waves you can make have phase-phase voltage of Vbus. However, the dq/alpha beta are related to phase-zero quantities, which are 1/sqrt(3) the amplitude of the phase-phase quantity.
I did the math, and you are correct about the loss calculation. It did not add up with the measurements I made, and it turns out that the power error was due to dead-time distortion in the bus current estimation. This has been an issue for a long time, but now it is improved a bit.
The HFI max voltage should be fixed now as well. I agree that the 2/3 should be there and the division by 2 should be removed, but I simply don't get correct measurements if I do that on any of the hardware versions. I tested the inductor and all my motors with the detection and LCR meter, and this is what gives the most correct results by far under a range of input voltages, switching frequencies and HFI voltages. For now I will just ignore that and move on. At least the other things in the code are consistent now with what you would read in a datasheet of a motor, and the inductance value works much better. I will still do some testing with an encoder on my dual motor rig and confirm that it tracks well in the next days, hopefully that reveals something else.
The test builds are currently building, I will update the link in the other post in a few minutes.
I agree that it is best to move on. Happy to see accurate measurement results.
I installed an encoder on my dual motor rig, and it turns out that it works better with the (slightly higher) induction measured by correct formulas:
When the inductance is measured like this, the phase error by the observer when applying a load becomes smaller compared to the same test with the other inductance. That indicates that it is closer to the truth, even if it is a bit higher than what I measured with the LCR meter (although the other formula gave lower inductance than the LCR meter).
I will continue testing in the next days and report back!
Sounds good. Good luck with your testing!
Well spotted. Curious to see how much of a difference it makes to the accuracy of the observer.
@Benjamin, in a few hours I'm going to try tuning a HPD20, which is a 20kW continuous 42-pole motor, optimized for peak power at ~2500rpm. It should be similar to the T-Motor U15, only maybe even more cantankerous. I'd love to try these code fixes, if they're pushed somewhere where I could give them a shot. Are they available online yet? I checked the `dev_fw_5_03` branch but I didn't see them there.
You can find the beta firmware on the first message on this topic : https://www.vesc-project.com/node/2859
Using the 2021-10-16 beta firmware, on my HPD20 the autodetection gives 3.50mH @30kHz for a single phase, which is not too far off from the published 8.7uH @30kHz for phase-to-phase. +-20% seems really decent for this measurement, and might be all we can hope for out of what is otherwise a motor controller and not a sensitive LCR meter.
The motor seems to start more easily, but that could also be because of other changes or even because I'm using PPM instead of keyboard control.
Only up to 180A so far, but running strong with the auto-detected settings.
That's good news, I hope you will be able to drive this motor at full power
Did you have to tweak the observer gain ?
As near as I can tell[*], this is rock solid! I didn't have to do any tuning at all for the system to start and run impeccibaly. I got to 300A[**] with nary an instability in the motor current. Now my next concern is overheating, as even with the ESC in the propwash-- which is about as good a fan as one can get-- I was only able to do 180A[***] sustained.
[*] I do have an issue where the battery bus current estimation is off by 20%. I have validated the true current with a very accurate current shut, as well as the power supply's built-in ammeter.
[**] Only 250A once correcting for the VESC's incorrect current estimation.
[***] 150A once correcting for the VESC's incorrect current estimation.
Kubark42, that is very nice! Would also be interesting to see what happens if the inductance is calculated with the correct formula (this is not yet in the beta), then it would even be slightly higher (and therefore closer to datasheet).
About the bus current calculation, I did check it and it did seem mathematically sound to me. However, since the value seems to be 20% too high I expect there must be a slight mistake somewhere. If I have time in the coming days I will take a deeper look into this. Did you also measure the phase currents externally? The peak of the phase currents sine waves should be equal to the motor current iq (when no field weakenng is used, i.e. id=0).
For the overheating concern, you can try to attach the ESC to an aluminum plate, as large as possible, to maximize the heat exchange surface and put some good thermal paste between
@Elwin, I did not measure the phase currents. I can probably jury-rig something together to shunt across some of my 1.0Ohm power resistors. The motor probably won't run right, but it doesn't need to for a 20% error validation.
It would be ideal if I could set my power supply to some low voltage-- e.g. 12V-- and turn the FET completely on, running the current through the 1.0Ohm resistor. That would give ideal results.
@NexusG, in the above case I had attached some finned heat sinks with thermal paste. It was just thrown together so it wasn't optimal, but it does somewhat bound the problem.
Increasing mass will only buy time until thermal overload. That's not always a terrible thing-- in my case I've only got 3 minutes of batteries at full throttle-- however, in general it's not a steady-state solution and in flight applications extra mass is to be avoided.
I won't say I'm a pro in heat sinks, but I did my early graduate work in heat transfer so I've got a foundation to build on. Experience tells me it's going to be hard to triple-- or even quadruple-- heat dissipation without getting creative. Better would be to reduce the amount of heat which needs dissipating.
I have pushed the latest changes and updated the build here:
After more testing with encoders it seems that the tracking under load is best with the new inductance measurement mode.
I also updated the observer to account for motor saliency when the Ld-Lq setting is non-zero, similar to how it was done in a paper titled A Nonlinear Observer for High-Performance Sensorless Speed Control of IPMSM Drive. That seems to work quite well.
Regarding the input current measurement of the 100/250, it is much closer than 20% for me with the latest firmware that takes the dead-time distortion into account (I got around 5% error). That was with a 40 uH motor though, so maybe the current ripple of a 4 uH motor will cause more errors in the measurement.
About the bus current:
Did another check on the formulas for the bus current and it fully checks out (formulas match the correct formula: Ibus = (1.5*ialpha*valpha + 1.5*ibeta*vbeta)/Vbus but look different because the mod parameters in the firmware have the factor 3/2 and Vbus included). Also, on the VESC6 MKIII I have borrowed from a college, the current draw seems to be pretty accurate (few percent off at most).
About the latest firmware:
My motor runs fine.
Inductance: 229.55 uH, ld_lq_diff: 54.84 uH (1.48 A)
Which is a bit on the high side (+20% vs LCR meter). This makes sense because the correction for the resistance is removed, making it overestimate the inductance. I guess you did this because prioritize observer performance over accuracy of the parameters, and this gave better performance. Would not be my choice, but does somehow make some sense.
Small observation during testing: Running the measure_ind with very high duty cycle gives strange results for me:
measure_ind 0.89 -> Inductance: 52.75 uH, ld_lq_diff: 32.94 uH (-1.23 A)
Cannot be bothered to check out why this happens, but if this also happens for you maybe you shuold limit the input to the function a bit.
One more things about this:
"About writing down the math, I personally often have an easier time reading code than mathematical notations (depending on both how the code and math is written obviously). Keeping two sets of the same equations updated is also a burden, and there are plenty of sources where one can read about motor math out there that should match the code more or less. Therefore I haven't spent any time making another writeup about motor math, but I encourage anyone who is interested to go through the code and document what is going in a different format. I'm sure that makes it easier to understand and follow for many people who are not like me."
I think this is quite unwise for some of the changed we/you made: not everything follows directly from the math. Most clear example for me is the factor 2 you changed to 4 here:
It makes absolutely no sense to me to have the factor 4 there, it is nearly impossible to understand why it should be 4. I wouldn't be surprised if this would be reported as a bug, and nobody remembers why it is 4. Now that you know it, you could easily add a small reminder. But ok, it's your code, if you think it is clear the way it is, fine by me. I probably won't be using VESC much more anyhow so I cannot be bothered too much. It is a very nice HW and SW though, great job! Was quite interesting for me to have a look and correct some things, hope it helped.
I gave it a try on the VESC6 MK5, and 0.89 works for me. What was the input voltage and switching frequency?
If someone is up for writing down the math down I fully support that. For now I have put a link in the source code to your post at that place to make it less confusing. I'm just saying that I haven't given that priority myself and that it isn't that high up on my priority list.
I hope the reason for this isn't the response you got here. I really appreciate your input and assistance, it was super helpful. If there is anything I can do to encourage you to change your mind please let me know.
Happy to hear my input is helpful and appreciated!
Sorry, my response was a bit harsh. No it has nothing to do with the response I got here, although I would like more in depth discussions.
Let me clarify what I meant. I was never really planning on using the VESC as it is aimed at a very different scene than what I do. I work in the high-tech industry, mostly semiconductor, and have an interest in motor control. So I thought instead of gaining knowledge just for myself, why not take a look at the VESC and look for room for improvement. This way I was able to help and learn at the same time.
Getting back to the different scene, I see there is quite a difference in needed features. VESC has a lot of focus on sensorless, observers, hfi, hall sensor and all kind of features around skateboards. In my work we never use such features as we always simply use encoder(s), because for us performance is much more important than cost. In principle the Odrive seems to be more directed to the high tech audience, but in my opinion the VESC is more mature and more advanced. Also, I was lucky enough to have a college who lent me a VESC, so it was a no brainer to take a look.
A bit more background. I have already made an own motor controller on the Teensy platform from scratch. I really liked to see what you have done with the VESC for a comparison. Some features for example that I have implemented in my controller, and which can be used in the high-tech industry:
3th order setpoint generation
Advanced feedforward control
60 kHz loop rate
Endless tracing of 12 signals at full 60 kHz (Teensy has very fast communication to host pc)
FRF measurements (bode plot) of all control loops
Chopper resistor (not before first blowing up a drv8301 chip, haha).
Most of these do not really seem to make sense for the VESC, but do for the high tech industry. I think the only thing of these that you really could use is the FRF measurement. If you wish to implement such a feature, I can help with an explanation and some basic code.
tldr: I am still interested in checking code to help and learn, but I will probably not really use the VESC. So if you want me to take look at something, just let me know!
That makes sense, sounds like you are going in a slightly different direction. That being said, I want to move the VESC code into that direction too (the goal is to be good at all motor control applications). The main reason I have focused on robust sensorless control so far is that most people who use the VESC benefit the most from that in their applications. I would be happy to collaborate on high performance features if you are interested, and add support for all of that in VESC Tool as we go. One step in that direction would be to make some hardware with EtherCAT for high speed low latency communication - the STM32F4 should work fine for that. To make the control loop run faster there are a few things that can be done too, like optimizing the code and having a mode that skips all the observer and sensor calculations.
Some final remarks about the inductance measurement:
1) Having the resistance in the calculation made almost no difference for most motors, and Jeffs simulation shows that it can make things worse in some situations. It would be good to implement his fixed point solution if there are motors that don't work too well with the current implementation.
2) The main reason, as far as I can tell, that the inductance is over-estimated is that the current measurement amplifiers and hardware introduce some latency. When the current is sampled it has not risen to the full value yet, resulting in a slightly lower di that results in a larger inductance estimate. The reason that this works better with the observer is that the current will have the same latency there, making the apparent inductance higher. As far as I can tell the latency and the apparent higher inductance should cancel out with the observer, but maybe that isn't true in all operating points. For calculating kp and ki the actual inductance might be a better starting point, but it should not matter that much.
@benjamin, I have opened a GitHub issue as a proposed avenue toward better documenting the code:
I know at first glance they might not seem related, but it will be easier to propose the code comments if there is a public-facing searchable deeply-linked anchor to those changes. In particular, this indirect context helps document the kind of inscrutable `2 *` --> `4 *` changes that @elwin mentioned above. Maybe this could be done a the same time as https://github.com/vedderb/bldc/issues/362?
@Elwin, I'm a controls engineer and I would just love to know more about the features in your controller. As you say, VESC is the more mature platform and so I'm very keen to see it grow in my applications. (You've already made a huge input, because with the sys ident fixes running my huge motor just went from daunting to trivial.) In particular, I'm a data hound and am interested in gleaning as much as possible from the data feed. Having full resolution into the controller's sensor data means that it becomes straightforward to debug any odd behavior, as we can determinstically disambiguate the code from the sensors. I can see tremendous benefit to VESC to have a similar feature.
Yeah I am interested to try and implement features. Let's try and see how it goes.
Ignoring the resistance will cause overestimation of the inductance for motors with a short electrical time constant. I did not see how Jeffs simulations show otherwise, but let's park this for now.
To really check the actual inductance and also latency we need to implement FRF measurement functionality. I suggest this is the first thing to implement.
Actually, the first thing I would like to do is understand the 2/3 factor in the calculation from vd and vq to mod:
I would think the mod should be the same scale as duty cycle like this duty = sqrt( mod_d^2 + mod_q^2) , but it is not. What is the idea behind using the 2/3 instead of 1/sqrt(3)? I think many things would be simpler with that scaling.
On the Ethercat thing, this really would really be an awesome addition. If you could get 16 kHz communication of 10 signals or something then this would directly make it an awesome controller for my work as well. I do think however this is quite a challenging one, but Beckhoff drives are actually capable of this. We have some of these Beckhoff drives at my work, but I would prefer vesc 10 times over them due to the open source (and better sw).
About tracing, I have no idea how fast the data stream to the host pc you have with the STM32F4 chip. Teensy has a 480 Mbit usb connection, which is actually limited by how fast I can empty the buffer at the PC side, not the Teensy.
I was looking a bit more into the factor 2/3 scaling that is used for mod_d and q. This scaling makes 1 the absolute maximum voltage the drive can output, however this is with full overmodulation (meaning that you cannot make this vector with this length in any arbitrary direction, just in the directions the aligned with the phases). With this scaling all the limiting you have to do become the confusing factor sqrt(3)/2 instead of 1, because this is the maximum voltage without over modulation. My feeling is that scaling with 1/sqrt(3) and limiting the vector to 1 (or 0.95 for margin) would be more clear, and than the relation with duty_cycle is also very clear (duty cycle would be the same as the length of the mod vector). Of course the svm function would have to be slightly adjusted to make everything scale correctly.
However, I also understand that actually changing this would be quite an effort, and there is no difference for the end user. But I think it would lead to much clearer and cleaner code, with less scaling factors. I think this would be worth it, what do you think?
Benjamin, can you please confirm whether my explanation of the mod_d and q scaling is correct?
Correct, with 1.0 the svm function gives the full length of one of the six vectors it can produce. The sqrt(3) / 2 comes from the vectors in between. It is quite easy to see if you draw a line in the middle between two of the vectors:
The 2/3 then comes from the Clarke transform:
Defining it this way means that 100% modulation outputs the full input voltage in the abc-frame, which then introduces the 2/3 when moving to the alpha-beta-frame. It can be done in different ways of course, but I think this is as good as any other. Let me know what you think.