Pages

February 3, 2016

Motor Control Progress: Working Hardware, and a Field Oriented Control Implementation

Over winter break I finished EAGLE-ing up a first revision of motor control hardware, and sent out for boards from 3PCB first week in January. This version is shaped like a Nucleo for ease of modification and debugging. Once I get motor control stuff solid, I'll redesign it with the microcontroller on the same board.

I wasn't particularly aggressive with component-packing, but there's still lots of empty space on the board. Layout turned out okay, but because of the unfortunate shape of the Nucleo I had to rout the PWM traces around the entire board. Should be tolerable since they aren't super sensitive signals.

A week later, boards and a Digikey box appeared:

Here's an assembled board. For FETs, I'm using these ones from NXP. 40V, 2.5 mOhm, 35.5 nC "100 Amp" logic level fets. The TI-Everything-Chip is particularly awful to assemble by hand. I did it with a nice Weller station and a microscope. I found one mistake on the board, which was that I routed the gate drive enable pin on the DRV8302 to a pin that gets used by the Nucleo for other functions. I cut the trace and jumpered it to a different pin.

Here's the bottom of the board, minus electrolytic bus caps.

I threw together a motor test jig out of scrap. It's a random optical encoder coupled to the motor. Eventually the giant encoder will get replaced with either a small optical encoder or a hall-effect based angle sensor (attractive for their absolute position sensing)

At this point I started working on the motor control stuff. After much struggling, I've now become a little more comfortable with STM32F4 register-twiddling.

First thing was making sure the gate drive worked and getting center-timed PWM up and running. I implemented a sinusoidal pwm block for my code, which I'll later replace with proper SVM for better bus utilization. Hey, it works!

After adding some encoder code borrowed from my closed-loop subwoofer project I was able to do some voltage-mode commutation. Here's the motor running under "voltage mode FOC", if you will. Basically, at this point I could specify d and q axis voltages and do the appropriate (inverse) transforms to convert those to 3-phase motor phase voltages, but didn't have the current transforms/control loops wrapped around everything yet:

Around here I observed some serious "crunchiness" in my commutation. At first I thought it was something wrong with my sine generation, as it happened periodically at a multiple of electrical frequency, but it turned out to be a hardware issue. Remember those PWM traces I had to route around the outside of the entire board? Turns out, when two PWM edges aligned (meaning two switching events coincided), the extra noise introduced everywhere was enough to sometimes falsely switch the input to the gate driver. This means that for small portions of each electrical cycle, the motor would draw more current and produce much less torque, since it was fighting itself. Here's the scope freaking out at one such instance:

The fix was to add some capacitance on the inputs of the gate driver. A tiny 0603 120 pF cap on each input completely solved the issue. Fortunately, all three traces had a via directly by a ground plane, so the fix was very easy. I think improved layout with a micro on the same board should also fix this problem.

Problem-solving caps circled in red

Next step was to get synchronous current sampling working. Since I'm using low-side shunts for current sensing, the voltage drop across the shunt is only representative of phase current when the low side transistor is on. The rest of the time there's no current through the shunt. This means sampling of the current sense amplifiers must be synced with the PWM. This has the added benefit of reducing switching-induced noise on the current signal.

Another day and a half of register-twiddling later, I got that figured out. Top three traces are the high-side PWM duty cycles, and the bottom trace is the current sampling - in other words, toggle pin high (for timing only), sample both ADC's, do ADC conversions, toggle pin low.

A close-up of the sampling period. The cycle takes 400 ns, which means if it starts at the center the PWM alignment, I can get ~98% duty cycle at 20 kHz before the sampling period starts overlapping switching.

Here's what a couple amps of phase current looks like, writing the value from the ADC straight to the DAC to look at it on the scope. No filtering at all. Pretty darn clean.

Now with proper current measurement, I could finish the FOC implementation and wrap current loops around everything. I spent another day an a half trying to figure out why my current transformations (from phase currents to d-q axis currents) weren't behaving like I expected.

The symptom was that, instead of DC values for d and q current (while commanding constant d-q voltages), I was observing a constant-amplitude sinusoid at the electrical frequency, for both d and q currents. I was doing all my debugging on the oscilloscope, by writing signals to the DAC. Everything looked right on the scope, so I spent a long time staring at my transforms to figure out where I had made a typo. Then I actually printed some values to my computer over serial and quickly figured out the problem - a huge DC-offset in my current measurements. I didn't see this on the oscilloscope, because the values written to the DAC rolled over and appeared to still be centered about zero.

The problem was in my current-zeroing method. When the microcontroller powered on, it averaged 1000 samples from both current channels, to find the zero-current offset. The problem was that I was doing this before enabling the gate drive on the DRV8302. Turns out, the enable pin enables both the gate drive and the current sense amplifiers, so my zeroing procedure was zeroing to a floating value. Enabling gate drive and then zeroing fixed everything.

Here's a video with q-axis current displayed on the scope, with a constant q-voltage commanded. As expected, it's a DC value in steady-state:

Sweet! Now it was current control loop time. First step was to go and measure my motor's q and d axis inductance (assumed to be the same, since this is a surface permanent magnet motor). To do this, I put a shunt in series with the motor, applied a voltage step to the shunt/motor (phase A positive, phases B and C grounded), measured the voltage rise time across the shunt, and used the total resistance and rise time to calculate inductance. Here's a good reference for measuring motor parameters.

In this shot, turqoise is the voltage across the shunt (proportional to the current through the motor) and magenta is the voltage step (done by plugging in a power supply lead).

This came up with a synchronous inductance of ~33 µH. With the d/q inductance and resistance, I could design a current loop.

I designed a discrete-time current controller for a 10 Khz sample rate. I haven't (yet) been particularly aggressive with sample rate or controller design, so I could probably push crossover significantly higher. The designed controller (used for both d and q axis currents) has a crossover at ~ 600 Hz with 56 degrees of phase margin. This corresponds to (electromagnetic) torque bandwidth.

Here's what I expected the step response to look like.

And here's the actual q-axis step response, after implementing the controller. Whoah, that actually looks like what I was expecting! Response is slightly slower and more damped than expected, but results are pretty close. I didn't even have to adjust my loop gains at all to get this response. Thank you, 2.171. I'd guess I either miscalculated a hardware gain somewhere or my inductance measurement is a bit off, or something like that. As expected, I could likely push the loop a bit harder without needing to sample faster, to squeeze some extra torque bandwidth out (if I really need to).

Here's the motor doing ±.7 q-amp steps:

So what's next? Well, first, the MultiStar Elite motors I loved in my motor roundup dropped down to $42.15 on Hobbyking (once you log in), making them a clear winner. I've already got some more on the way for multi-dof leg building.

Motor control is not quite done. I still need to do some more stress-testing to catch any glitches. I also need to decide on a final position sensor soon. Small optical encoders can be had cheaply, but I don't like the necessity of rotating through the index line on the encoder on startup to determine absolute position. Hall-effect rotation sensors are nice for their absolute position, but I already know how to use encoders and I've never messed with the hall rotation sensors before.

Next hardware steps are laying out a new version of the motor controller with the hardware patches I made and an STM32F446 on board. Ideally, I'd be able to stuff everything in a board with the same footprint as the motor, so it could bolt straight on the back.

I also need to start thinking about the mechanical bits soon - mostly how to build a single-stage planetary gearbox around these motors inexpensively.

3 comments:

Hi Lad, Love the blog! The cheapest and easiest way to do the planetary gear box is to sticking with the RC field. Inside the Diff box of RC monster trucks are four smaller gears that you can use to make a planetary gear boxhttp://www.hobbyking.com/hobbyking/store/__37702__Central_Diff_Box_Assembly_Nitro_Circus_Basher_1_8_Scale_Monster_Truck_SaberTooth_Truggy.html http://www.hobbyking.com/hobbyking/store/__14162__Diff_Pinion_Gear_S_3Pcs_Bag_110BS_A2003T_A2010_A2027_A2028_A2029_A2035_A3011_and_A3007.html

Thanks! It's a really bare-bones implementation right now - independent d/q axis controllers, no feedforward decoupling yet. I'm a little hesitant to add in decoupling just yet - for both decoupling and impedance control of the motors (for robot leg control), I need to sit down and think harder about getting clean velocity estimates out of my position sensors (achievable joint damping depends on how clean your velocity measurement is).