April 3, 2020

Coronavirus Project 1: Bus Voltage Controller


Posted on April 3, 2020 by admin

Its coronavirus season, which means NO MITERS. RIP. Last week I grabbed my sourdough starter out of the fridge and we shut miters down for the long winter.

Back at my apartment with no CNC mill, I’ve been up to exclusively software projects. It turns out my list of software “back burner” ideas is actually quite long, and the insideness has given me a fair amount of time to work on projects from this list. Notable items from this list:

  • Lookup table interpolator firmware for multiple bus voltages
  • Motor controller firmware re-organization because it is currently a garbage fire
  • Boost converter firmware
  • Boost converter board layout
  • OLED screen board layout

I decided to tackle lookup table interpolator first. For those unfamiliar, the lookup tables in question are used to generate the reference currents for IPM motors at a given speed and torque command. Effectively these lookup tables command a phase angle for the currents. It just seems to be easier to encode this information in D/Q currents instead of magnitude/phase although the information is the same.

Note that there is actually very little Q current on these tables at high speeds, even at high throttles- this table is for a battery voltage of 160V so we’re field weakening quite hard to get to such high ripems.

So the general rule of IPMs is that when you field weaken, you effectively end up in a constant-power regime. For greatest efficiency, you ideally want to field weaken as little as possible: field weakening creates torque at a lower Nm per amp, meaning that more losses are generated per unit torque. Additionally, to generate maximum power on a motor, you should use all the volts you have to generate as much V*I as possible. So, a precise lookup table is important for both power and efficiency. Ideally, the hypotenuse of Vd and Vq should exactly equal the bus voltage.

However, the motor equations are complicated:

Vd = i_d*Rs – w*Lq*i_q

Vq = i_q*Rs + w*Ld*i_d + w*flux_link

The equations alone don’t seem that bad at first, but it turns out that Ld, Lq, and the flux linkage are all functions of Id and Iq because saturation is a thing. Lq changes most dramatically, decreasing by a factor of about 20-40% at high currents.

What about just finding the lookup tables by dynoing? What if we swept over all current phases every single operating point, taking 5 seconds for every phase sweep?

  • 20 speeds
  • 20 throttle positions
  • 10 bus voltages
  • = a little under 14 days of continuous dynoing. Sufficient to say you should do something a little more intelligent.

Anyway, it is sufficient to say that generating the lookup tables is hard. More on that in a later post if I ever get around to it. Back to the main content of this post: what to do once you have the lookup table.

The three axis of the lookup table matrix are as follows: speed, throttle command, and bus voltage. Again, the goal of the lookup table is to generate current setpoints such that the hypotenuse of voltages present are exactly equal to the bus voltage.

I wrote a simple little motor simulator and put in some basic lookup tables. Here I command a slowly increasing throttle value with a bus voltage of 100V.

Here is the voltage magnitude. It increases with throttle until the field weakening kicks in, and then it maintains a level 100 volts, exactly as desired.

But what if instead of increasing throttle slowly, we just bang full throttle? Here is what happens if I clip all voltage magnitudes above 105 V, as they are not physically possible:

You can see that the voltage instantly rails at 105 because it takes volts to slew the inductive load. This causes the controllers to take a poop:

Note the D axis current and its great lack of tracking.

So, anyway, we need a new scheme. The fundamental issue here is that two controllers are not actually independent, as they are actually linked by the relationship of the magnitude of the D and Q voltages being equal to the bus voltage when under field weakening. If we rigidly fix the voltage magnitude, we have only one degree of freedom (the phase) to control the two outputs (the currents).

I tried a number of schemes where you only control one current and let the other be unregulated, but none gave really good performance. Most of the plots looked like this:

After enough messing I got something that was stable at some operating points. The strategy here was to only regulate Q and let D be whatever volts was left. The problem here is that this controller could effectively get “stuck” (as it does here at 20ms in). Even with all the leftover volts on D, it still cannot slew fast enough because the increasing Q current generates a voltage which decreases D current.

So, no luck here.

I thought about this for a few more days and eventually came up with the following bad scheme:

  • run a PI controller on both D and Q as with the naive implementation.
  • Implement a slew rate limit on the throttle command. This alone helps a ton.
  • Relax the voltage magnitude constraint a bit to allow for up to 7% overmodulation. This will decrease efficiency slightly while the throttle slews, but negligibly so.
  • Implement a “bus voltage fudge factor” with a PI controller. This is the bad part.

What is this nonsense of “bus voltage fudge factor” ??????

Its pretty simple- normally the lookup tables are generated for a bus voltage of whatever is actually attached. However, in this case I generate the lookup tables with a fudge factor added to the bus voltage. Kinda bad, but also not bad! Additionally, I use a gain scheduler to integrate the integrator much faster when nearing the limit of the overmodulation. Code:

V_bus_fudge_err = np.hypot(controller.v_d, controller.v_q) - V_bus_actual 

if (abs(V_bus_fudge_err) > 3.5):
    KI_BUSV = 0.5
else:
    KI_BUSV = 0.05

V_bus_fudge_int += V_bus_fudge_err*KI_BUSV
V_bus_fudge_int = np.clip(V_bus_fudge_int, 0, 10)
V_bus_fudge = V_bus_fudge_err*0.5 + V_bus_fudge_int
V_bus_fudge = np.clip(V_bus_fudge, 0, 10)
(controller.i_d_ref, controller.i_q_ref) = LUTmapper(thr2, 100 - V_bus_fudge)

This actually seemed to work pretty well. Using a fairly fast throttle slew of 0-100% throttle in 10 ms, the peak voltage reached was 105, and it only did so for one sample. The fudge factor is the red trace.

A D term on the fudge factor controller could improve this, but I believe there is too much noise for this to really work. 105 is good enough! The current controllers track well with this scheme.

This strategy is likely what I will move forward with on with future motor control adventures. Time to implement this in C++!!


0