I would like to see an FRF measurement functionality implemented in the VESC. This functionality gives you the amplitude and phase response of for example the current loop.
Required "ingredients"
1. Noise injection.
2. Trace functionality.
3. Averaging.
4. FRF calculation.
5. Bode plot functionality.
1. Noise injection.
A noise generator needs to be implemented, which can then be connected to various parts where you want to inject the noise. The first points to add the noise would be:
Vd and Vq, such that the current loops can be measured. Later other injection points can be added.
PRBS is a good and easy way to generate noise. It has a flat frequency spectrum and all frequency components are in there. It also has an optimal crest factor of 1. Here is code I am using that makes a repeating sequence of 2047 samples:
unsigned int NdownsamplePRBS = 1;
unsigned int downsamplePRBS = 1;
uint16_t PRBSstate = 0xACE1u; /* Any nonzero start state will work. */
uint16_t noisebit; /* Must be 16bit to allow bit<<15 later in the code */
float PRBSnoise;
// noise generation using PRBS, see https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
downsamplePRBS++;
if ( downsamplePRBS > NdownsamplePRBS) {
downsamplePRBS = 1;
noisebit = ((PRBSstate >> 5) ^ (PRBSstate >> 7) ) & 1; // taps: 11 9; feedback polynomial: x^11 + x^9 + 1
PRBSstate = (PRBSstate >> 1) | (noisebit << 15);
}
PRBSnoise = 2 * (noisebit - 0.5)
The PRBSnoise variable can now be used as source for the noise.
The downsampling is not really needed and could be left out. This would be used to lower the frequency content of the noise signal, but since the VESC cannot make large traces at the full frequency this would cause aliasing to appear. Still this could be kind useful, but one has to be careful when interpreting the results. An interesting thing is that Siemens S120 drives actually implement it like that, with the aliasing problem in there.
To inject the noise is simple:
float Vq_distgain; //These you need to be able to set from the SW
float Vd_distgain; //These you need to be able to set from the SW
Vq += PRBSnoise * Vq_distgain;
Vd += PRBSnoise * Vd_distgain;
In principle the prbs generator can always run. To enable the noise just make any of the distgain variables non-zero.
2. Trace functionality.
Samples need to be taken at the full sample frequency of the VESC.
Trace of the following signals is needed (when injecting in d the q can be skipped and visa-versa, but maybe it is simpler to allways trace both):
- PRBSnoise
- Vd and Vq, directly after the noise is added.
- Id and Iq
The trace needs to be started after starting the noise and letting the system settle for at least 1 second (or settable parameter amount of time)
3. Averaging.
To get a good quality measurement, averaging is required. Typical is 10 to 20 averages. Much more doesn't help much, as averaging improves the SNR with sqrt( N_averages ).
Since the PRBS is a repeating sequence of 2047 samples, this exact same length should be captured, but it does not really matter what starting point you take. To get averaging in the FRF, simply sum in the time domain. Dividing by the number of averages is not required, as this will drop out when calculating the FRF.
Example:
sample 1 to 2047 simply go into the buffer values 1 to 2047
sample 2048 to 4095 add the values in the buffer at location 1 to 2047
and so on.
4. FRF calculation.
This can be performed on the host pc.
Get the data from the buffer, which should be exactly 2047 samples long.
Simply take the FFT of all the signals, whereby the signals need to be exactly 2047 samples long. You should see that the FFT of the noise has a flat magnitude.
FFT will give the response for both negative and positive frequencies. In this case the negative frequencies can just be ignored. The frequency vector of the FFT will have a spacing of Fs / 2047 (Where Fs is the sample frequency).
To get the electrical plant of the system: Plant_q = FRF_iq / FRF_Vq. (or d if you injected noise in the d axis) .
If the current loop was turned on during the measurement, you can also get back the sensitivity as follows: Sq = FRF_Vq / (PRBSnoise * Vq_distgain)
From the sensitivity you can get back the Open Loop response, which is typically used by control engineers to tune controllers and check margins: OLq = (1/Sq) -1
5. Bode plot functionality.
Typically the FRF data is represented in a Bode plot, with a logarithmic frequency scale in Hz, the magnitude in dB and the phase in degrees.
When plotting Plant_d and Plant_q you should clearly see the R L behavior (1 / (R+L*s)), i.e. a flat 1/R line which then transitions into a -1 slope of the L*s. The graph is the inverse of the impedence.
-------------------------------------------------------------------------
What do you think, is this something you could implement?
Looks perfectly doable, I will get started on it for the next release.
A question: as we already measure R and L, we should be able to derive the RL behavior already and derive the current controller gains (as is done with the time constant). Do you know of any situation where this is not enough and the FRF measurement can provide some additional insights?
Good to hear!
Good question. In an ideal world with an ideal system you don't really learn anything new. However there are many things that you can find:
1. Delay in control loop. You can directly see the delay in the phase characteristics. This phase is essential for the feedback performance. The measurement can also nicely show the effect of the low pass filtering on the phase and magnitude.
2. Check whether your controllers function the way you think they function.
For example, I have already seen that the integrator in the current loops is added in the next loop, causing phase loss. I have already done some checks on this, and it causes some performance decrease for high bandwidth current loops on motors with a low time constant. For systems where very high performance is required this would have to be improved. The FRF measurement can show the effect.
3. Unexpected frequency components in measurement. The current measurement in a 3 phase amplifier is delicate, and sometimes unexpected frequency components show up. This helps to identify this.
4. Accurate mapping of inductance versus current. You can easily repeat the measurement with various offsets on the d or q current and see how the inductance changes. Especially interesting is probably the saturation in the q axis, which can destabilize control loops because the Lq decreases. (Of course you need to physically lock the rotor for this measurement). When you have the Lq versus iq relationship, you can gain schedule the q axis feedback loop to stay stable.
5. The same method is easily extended to measure the other control loops. This allows for tuning of the controllers without trial and error. It gives an insight in what dynamics limits your performance. Also for example notches could be tuned with this.
6. Systems with long cables (let's say 10 meter or motor). At some point the capacitance and inductance of the cables start to influence the control loop. I guess this is quite a niche for the VESC, but this can help for such scenarios.
I'd love to be involved in the programming aspect of this. In theory, I wrote a dissertation in control systems so it would be fun to return to some of my roots.
@benjamin, if that's cool with you, lemme know and I can start working with @Elwin to implement this into code.
If Benjamin is not working on this yet then I would say let's do it.
The tricky part about this is how to use the part of RAM on the other bank that is already used for the samped data plot. The implementation in VESC Tool also is a lot easier to make with some knowledge about the inner workings.
I'm still focusing on finishing this release, after that this will be one of the first new features. You can start on it now of course, but I think it will be a lot easier if I make the groundwork and you start helping out from that point.
I'm fine to start nowish. I'm well-versed with Qt/QML, so stand a decent chance of figuring out the VESC Tool side. Worst case scenario, Elwin and I do a UI spike and then you can fill in the back-end.
First outputs:
More work to be follow. This graph is pieced together, noisy because of lacking averages, and out of phase, so plenty of room for improvement.
Made some first measurements. Everything seems to be pretty much spot on and with little noise. There is a bit more phase lag at high frequencies than what I would expect, but nothing to really worry about. Open loop is a nice straight line like it should be due to the current controller canceling the zero of the L R combination. Great job Benjamin!
Measurement made at 10 kHz PWM (VESC calls this 20 kHz), 20 kHz current loops , sampling in v0 and v7 enabled.
When measure v0 and v7 is disabled the frequency response shows a drop at high frequencies which I did not expect. it turns out this is caused by the fact that the vesc already sets the pwm in v7 from the measurement in v0. Red line is where I set the PWM in the next v0 instead. Thought the behavior is quite interesting, but I think the VESC implementation is better (more phase):
Nice work! Looking forward to get this and the plotting implemented in the next release.
Did you just send the samples to vesc tool, store them in a CSV and then process them in something else?
Yes for now I use Matlab. But doing it in VESC tool should not be that complicated.
Made a new picture with v0 v7 sampling turned on. The phase is actually bang on with expectations, by correcting the expectation. I forgot to add a sample delay for the calculation time (and the wait until the next PWM value is taken from the preload buffer). Looks very good!