March 31, 2017

Encoder Autocalibration for Brushless Motors: Offset and Eccentricity

I finally got around to writing a position sensor auto-calibration procedure, for measuring the orientation of the position sensor relative to the stator.  Until now, I've been manually measuring that position by setting my U phase high, and V and W low to, lock the rotor to the D-axis, looking at the position sensor reading about a few of these points, and hard-coding them into my firmware.

This process should work regardless of the order the motor phases are plugged in to the controller, or how the position sensor is initially oriented.

Step 1:  Determine Phase Ordering

The purpose of this step is so that commanding positive current on the q-axis produces torque in the direction that causes the encoder angle to increase.  Basically, the process is to apply a large, slowly rotating current to a "virtual" D-axis.  The rotor will closely follow this rotation.  This is basically equivalent to driving the motor like a stepper motor with microstepping.  As the current rotates around, if the encoder count is increasing in the positive direction, then everything's good.  If the encoder count is decreasing, then swap the voltage outputs and current sensor inputs on two phases.  Now the motor will rotate the correct direction.

In pseudo-code:

v_d = 1;                                                            // Volts on the D-Axis
v_q = 0;
reference_angle = 0;                                                
start_angle = encoder.GetPosition();                                //starting position
while(reference_angle < 2*Pi){
    [v_u, v_v, v_w] = abc_transform(reference_angle, v_d, v_q, 0)   //inverse dq0 transform to get phase voltages
    wait();                                                         //give the rotor some time to settle into position
    reference_angle += .001;
end_angle = encoder.GetPosition();                                  // final position
if(start_angle - end_angle > 0){                                    // if position decreased, swap phases

Step 2:  Measure Encoder Offset

Now that the motor spins the right direction, you can measure the DC offset of the encoder.  Just like before, apply volts to the  D axis, and slowly rotate the axis through a whole mechanical rotation both backwards and forwards.

Looking at the position sensor output vs the reference angle, you'll see something like this.

Zooming in a bit, you can see some ripple in the tracking from cogging torque:

Looking at the error between the reference angle and the actual rotor angle, things get a little bit more confusing.  The error plot below has a few interesting features.

First, and most obviously, the mechanical offset of the encoder is just the average value of the error.  The electrical offset (which is what we actually care about for commutation) is just mod((mechanical error) * (number of pole pairs) , 2π). 

The high frequency ripple (which in this zoomed-out view looks almost like noise), is from the cogging torque (the ripple that showed up in the red trace above).  

Right in the middle of the plot, there's a jump downwards.  This is where the motor switches direction.  Since the motor has friction and inertia, it always lags slightly behind the reference angle, so that lag switches sides when the motor switches directions.  

Then there's some low-frequency, much larger ripple on top of the signal.  This is from eccentricity of the position sensing IC/its magnet.  In my case, it's because I accidentally messed up the design of a PCB and placed the IC 0.5 mm off to one side.  The total peak-to-peak height of this ripple is only around 0.03 radians, or 1.7 degrees, which doesn't sound like a lot.  However, I'm using a 21 pole-pair motor, which means that 1.7 degrees of mechanical error translates to 36 degrees of electrical error.  Enough to seriously throw off commutation.  The next step will go through how I corrected for the error from eccentricity.  While this won't be nearly as much of a problem once I get the new round of boards in, there will always be a little error in the placement of the IC (especially when they're hand-soldered by me), so having this feature is still definitely useful.

Step 3:  Eccentricity Correction

The first trick for correcting for the eccentricity is separating out the error from cogging and friction from the eccentricity error.  

Friction is easy.  Just average the samples rotating forwards with the samples rotating backwards.  Then plotting error you have something like this 

Removing the ripple from cogging torque took some more thought, but it turns out can be done extremely effectively.  My initial though was to just low-pass filter the signal, and hope that cogging could be sufficiently attenuated without changing the eccentricity part of the signal too much.  Honestly, for this particular motor with lots of pole-pairs and many cogging steps per electrical cycle, that would have probably worked just fine.  But you can do better, and make the result more easily applicable to other motors.

The trick is to take advantage some properties of motors and FIR filters.  Since the motor is rotationally symmetric, the frequency of the cogging ripple must be some integer multiple of the electrical frequency.  FIR filters can be designed such that they have zero gain at certain frequencies and harmonics of those frequencies.  So you can easily design a filter that has zero gain at the electrical frequency, and all multiples of it, nearly perfectly cancelling out the cogging ripple without affecting lower frequencies.  I'm using basically the simplest possible FIR filter - I just average n samples around the point of interest, and choose n such that the samples I'm averaging exactly span one electrical cycle.  This should work pretty well on any motor, as long as the number of pole pairs is larger than the highest harmonic from eccentricity.  Here's what the the filtered version of the signal looks like:

Now, with that filtered signal, you can build a lookup-table to correct the encoder output.  It doesn't even need to be particularly high-resolution, since the signal varies slowly - I'm using 128 points, and it works great.  The lookup table is generated by subtracting the average value off of the error, and then converting the error back into raw encoder counts.  I'm cheating a little bit and using the reference position as the x-axis, rather than the actual encoder value, because it's nice and evenly spaced into a grid already.  This approximation works quite well though. 

Here are three lookup tables generated independently, showing the consistency of this technique:

My latest round of motor control firmware can be found here, if you want to look through the autocalibration code.  It's fairly unpleasant to read, but it is well commented.  The code as a whole is very much a work in progress, so don't expect to put it on anything and have it work out-of-the-box.

At some point I'll throw a high-resolution optical encoder on the output to quantify the improvement in position error, but in terms of torque ripple, this process qualitatively improved things enormously.

March 30, 2017

Lathe Toolpost Spindle

I've been thinking for a while it would be nice to have live tooling for the MITERSlathe, for things like grinding and indexed drilling parts like hubs.

So far I've made everything out of random bits and pieces found at MITERS.  For the spindle, I started out with this old R8 to ER16 adapter:

I meticulously turned the shank down to 20mm.  I used sandpaper and Scotchbrite to bring the shaft to within a couple tenths down the whole length.

This was an incredibly satisfying measurement.

The bearings lightly press on the shaft - it's a tight enough fit that you just barely need an arbor press, but you can still easily un-press the bearings without damaging them.

Here's some of the housing machining.  I started out with this huge brick of mystery-steel I found on the floor of MITERS.  I got really lucky - it happened to be perfectly sized for machining a tool-holder,  and machined wonderfully.  It fly-cuts especially nicely, producing an incredibly shiny surface:

I squared up the block on the mill, cut the dovetail, and spot-drilled the spindle bore.

I did all the spindle boring on the lathe.  I started out by placing the housing on the toolpost, centering it vertically, and indicating the back face to be parallel to the travel of the lathe.  I put drills in the lathe's collet chuck, and use the lathe kind of like a horizontal  boring mill, to guarantee that the spindle bore was true to the  the lathe's travel:

To machine the bearing bores, I put the housing in the 4-jaw chuck and indicated in the drilled holes, as well as the faces, to make sure the housing was both square and centered.  I used this big indexable drill bit as a boring bar, because it has wonderfully sharp positive-rake inserts which produces a much better surface finish with low cutting pressure than the usual negative-rake turning inserts do.

I used a different bearing arrangement than I did with the tiny lathe, and I'm very satisfied with how it turned out.  I only had deep-groove ball  bearings to work with, rather than angular contact bearings, and I used a 3-bearing arrangement.  There's a pair of bearings at the front of the spindle, with a 0.002" shim between their inner races.  When the spindle housing is assembled, the outer races get squeezed together, so the shim provides a fixed preload.  This puts the bearings in a face-to-face arrangement, making the spindle somewhat tolerant to misalignment of the back bearing, if there is any.  The back bearing is a very close slip fit into the housing, and the outer race is lightly preloaded outwards by a wavy washer, to keep the balls in contact with both races.  Finally, there's a spacer between the inner races of the front and back bearings.  When the bolt on the back of the spindle is tightened, it squeezes down on the inner races and spacer, locking everything together.

Over all, I'm much happier with how this arrangement worked out compared to the lathe spindle.  Since preload is fixed, it takes no fussing to get the spindle to spin smoothly without slop, assuming the shim is sized correctly.  Also, since the front bearings are so close together, the preload will hardly change as the spindle heats up and expands.  For the lathe, I left the spindle on for a while, and pre-loaded the bearings with the spindle at normal operating temperature.

A plate on the front of the housing presses the outer races of the bearings together, to preload the bearings:

To test it out, I threw a Lovejoy coupling on the back, and a mating one on the output shaft of an old orbital sander motor, which was roughly appropriate speed.  Conclusion:  the spindle part works great, and, unsurprisingly, the orbital sander motor was terrible.  Needs more brushless servo.

Here are the two mounting configurations.  I tested it out with a 3/8" endmill, and the spindle was rock solid, producing an excellent surface finish without any signs of chattering.

Still to do:
- Find a better spindle motor, and make a motor attachment and height stop.
- Make an indexing system for the lathe spindle, so you can precisely lock the spindle to certain angles.

February 12, 2017

E-Mount to Sinar View Camera Adapter

Bayley has a pretty cool Sinar view camera.  I'm not going to go into exactly what a view camera is (go read wikipedia), but the gist is that you add a bunch of degrees of freedom between the camera's sensor (or film) and the lens, allowing you to shift the plane of focus to somewhere not parallel to the plane of the sensor or lens.

Unfortunately, using his CCD camera back requires tethering the camera to a sketchy old PowerBook G4, because that's all the software to read it runs on, and tolerating the painfully slow ~1 Hz live-view updates.  Focusing can literally take an hour if it's a tricky shot.

To improve the view camera experience, I made an adapter for mounting my own Sony α6000 camera body to the view camera's motion stages.  Because the sensor is much smaller and recessed more into the camera body, you don't get the extreme range of motion in tilt and swing like you do with the giant sensor, but it's sufficient to get the view-camera effect.

In retrospect, I should have flipped the side I thinned down to gain some extra travel, but it's not worth redoing at this point.

To get the e-mount mounting features, I bought an E-mount to Canon adapter and did some post-machining to it.  The E-mount side of the adapter was machined in, but the Cannon side screwed on.  I used the removable piece and screws to fasten the adapter to the machined aluminum plate.

The bellows locate in the pocket, and the 8 neodymium magnets around the edge hold the bellows on.

I made a stainless steel thumbscrew to replace the flat-headed setscrew the original camera used to attach the camera body mount.  The taper at the end of the screw draws the mount down onto some locating pins.  The knurling turned out poorly- I'm not sure what I was doing wrong, but one of the two wheels just wouldn't locate properly and made a double-pattern.

Here's the assembly in-action:

And here are some sample pictures:

Harbor Freight brushless chainsaw motor:

Another motor:

One of my small motor controllers.  I did something dumb and blew up the top-left FETs, which is why there's black goo in that corner:

January 27, 2017


A quick build which is incredibly useful.  Somehow I only thought of this now.

Recently Bayley and I were helping the FSAE team do some motor-debugging.  To get a good oscilloscope shot of their motor's phase currents, I borrowed the big LEM closed-loop hall current sensor from the motor dyno buck converter, wired into a ±15V supply and a sense resistor.  It proved to be extremely helpful, and in the process I realized that having a small, high-current, medium-bandwidth current probe would be incredibly handy, especially for motor/motor control stuff.

I threw together a simple current probe made from a smaller LEM current transducer powered by a 9V battery and DC-DC converter IC.  Pass your wire through the LEM, and the current signal goes out the SMA connector in the back straight to your oscilloscope.  The particular sensor I used is good for ±70A at DC to 200 kHz, making it fast enough to see effects like current ripple from switching on electric motors.

Since it's around the Chinese new year, I couldn't order boards from 3PCB in any reasonable amount of time, so I etched a board on the MITERS mini-CNC router to test the design:

I forgot to add the power switch to the Digikey order, so I tested with a piece of wire, and replaced that with a jumper later.

Here are the Digikey bits for populating one, minus the actual current sensor.  I used an LA 55-P (because I scavenged 3 a while ago),  but it could be populated with a variety of transducers depending on your desired measurement range.

I 3d-printed a little box for it:

Here are some random scope shots from an assortment of motors and controllers lying around MITERS.

This one's from someone's electric skateboard, which has an airplane ESC and a small outrunner on it.  Just spinning the wheel, the switching ripple is the same amplitude as the phase currents, thanks to the combination of slow switching frequency and very low inductance motor.  Not a good-looking waveform.

And this funny looking one is a sensorless Jason trying to commutate a small internal permanent magnet motor.

Once 3PCB comes back from holiday I'll get some proper boards made and assemble a couple more.  Here are the Eagle and gerber files if you're interested in building your own.

January 21, 2017

Motor Dyno: The Talk-To-Everything Board, and Other Updates

Here's the Talk-To-Everything Board.  The idea is that I want to be able to plug arbitrary motor controllers into the motor dyno, and control them from the UI on the computer.  There's an FTDI chip for USB to serial, with a digital isolator and isolated DC-DC converter, so that the computer is electrically isolated from whatever motor controller gets plugged in.  My favourite STM32 is broken out to a CAN tranceiver, a small buck for 0-5V output, I2C, RC PWM, TTL Serial,  differential 5V serial (like RS422), and SPI.  Even if I don't end up ever using several of these, it wasn't much extra effort to break out more interfaces, so I figured I might as well.

I moved the 'Everything-Board, plus DAQ and other sensitive analog stuff into an aluminum box with shielded cables in, to fix some more noise problems.  I also now have a big LEM closed-loop hall current sensor for measuring the buck output current.

Here's a look at the current user interface.  Notice all the extra doodads for controlling some of the test-motor interfaces.  I've also added "Profile Mode", which takes in a csv series of speeds, buck voltages, and test-motor commands with time-stamps, and plays them back on the hardware.  This way I can easily automate cycles like efficiency maps.

I'm currently in the progress of designing a new buck converter from scratch, to replace the awful pile of Prius power electronics that is the current buck.  Progress is slow, because I have very little experience with analog electronics and hardware controls (for regulating the buck voltage and current).

January 14, 2017

Return of the Snow Bike: It actually works now

It snowed rather a lot last Saturday, so I was finally motivated to get the snow bike up and running again.  Last episode, the rollers inside the treads kept jamming with snow.  I actually fixed this problem shortly after (almost a year ago now), but never quite got the electrical system to be solid.  The motor/controller was still plagued with position sensor errors, so the vehicle never lasted more than a couple minutes without breaking down.  For now, I've replaced the big 40V, 150A Kelly motor controller with a much more modest 50A sensorless e-bike controller borrowed from Dane.

Here it is wired-up and ready to go:

A closer look at the e-bike controller.  This is a 50V one Dane cracked open and modified the UVLO setpoint on:

Most of the streets were well-plowed by the time this thing got working, but fortunately someone had opened the gates to the courtyard of the Novartis building across the street from MITERS, and there was 6-8 inches of snow everywhere:

We also brought out the Atomic Thing and got to mess around on both vehicles for quite a while, until we got cold/were kicked out.

It's a bit underpowered right now - you end up holding the throttle all the way open the entire time, but still a good time.

December 21, 2016

Some Motor Math and Simulation

While waiting for boards and parts for the motor dyno to show up, I got distracted by doing some motor modeling and simulation. Having a decent motor-modeling platform is an incredibly useful tool, because it lets you quickly test out motor control stuff in simulation, before actually implementing it on hardware and testing it for real.

I wanted a model which could encompass controller effects like sample time,  switching, PWM resolution, A/D resolution, sensor noise, etc, and generally be, well, as general as possible, so that I can use it for arbitrary motors and motor controllers.  I started out doing most of the model in the D-Q frame, but realized to get what I want, it really makes more sense to do all the motor modeling at the phases, and then just use the transformations as necessary for control purposes.  You know, like how motors work in real life.

In working through this problem, I finally sat down and did a bunch of motor modeling math by hand, which I should probably have done a while ago.  Fortunately Bayley  was semi-coincidentally working through similar problems at the same time, to do some optimization for controlling hybrid car motors for a go kart, so we did some back-and forth to sanity check results.

Rather than putting it all here, I spent the time to write up a nice Latex-ed document with my notes, which can be found here.  This is just the analytical portion of the modeling - i.e. coming up with the equations that actually get solved by a computer.

Here you go.

With that part of the problem out of the way, I could actually implement the equations in MATLAB, and do interesting things with them.

Here are a couple useful sanity checks for making sure the motor equations were behaving like I expected them to once implemented, and some other experimentation.

Open Circuit Back-EMF
i.e., spin the motor with the phases floating, and measure the voltage between terminals.  This is the classic test I use all the time to quickly estimate motor parameters of random motors.  The next few plots are for the hybrid car motor being used in the go-kart.

Short-Circuit Torque Vs Speed
i.e. Short the motor terminals together and spin the motor.  For low speeds, the motor looks behaves like a resistor, so expected result is that there's a damping torque proportional to angular velocity.  But as speed increases, the motor behaves like an inductor, not a resistor, so damping should drop off to a constant value.  The crossover speed should be right around the motor's R/L break frequency.  And so it is:

The plot above, and those following are per pole pair.  This is a 3 pole pair motor, so actual torque and speed get multiplied and divided by 3 respectively.

PM and Reluctance Torque vs Current Angle
Looking at the permanent magnet and reluctance components of torque vs the relative angle of the stator current to the rotor, the expected plot looks like this.  Here's the result for the same hybrid motor (this plot's actually a little out-of-date, but it gets the picture across)

Auto-Generate "Optimal" Current Trajectories
Unlike for the classic surface-permanent-magnet "brushless" motor, where you typically want to just put all the current on the Q-axis (neglecting field-weakening), internal permanent magnet motors (like those in most hybrid cars) needed to be treated differently.  I wrote a script which sweeps operating speeds, and for every speed and current setpoint finds the current phase angle which produces the most torque.  A pretty cool thing about this method is that it automatically produces the correct field-weakening trajectory for operation above base speed. No need to think about how to move through current circles and voltage ellipses.

Here's the whole positive-torque, positive-speed operating region, for the same hybrid car motor as above, with a 160V bus and 200 phase amp limit.  This map can be used to generate a lookup table for motor controller - for a given speed and torque command, what are the best d and q axis currents.  In this case I've formatted it as current magnitude and phase, instead of d and q current, because that makes the table a little more convenient to generate.

(currents numbers are larger than 200A peak just because of the way I've defined my dq0 transform, but this corresponds to 200 peak phase amps)  The dark-blue regions are unreachable operating points.

Switching Effects
I added a naive implementation of switching to the model.  I implemented this very much like how a microcontroller does PWM, with an up-down counting counter, like this.  Unfortunately this method means I have to simulate way more timesteps than necessary - 40 kHz pwm with 12 bits of PWM resolution, this method has to simulate 1/(40,000 * 2^12) steps per second, which is way faster than the actual dynamics of the motor behave.  There are cleverer ways around this, but I haven't gotten around to trying anything else yet.

Here's what phase currents look like with 40 kHz switching on one of my MultiStar Elite 5208 motors.  You can see the fuzz on top of the sinusoids from the switching.

Zooming in, you can see the double-peaked ripple caused by the center-aligned PWM:

Here's a link to all the repository of MATLAB code:  Beware, it's kind of an organizational disaster right now, but eventually it'll get organized and commented better, I promise.

Don't worry, I'll be back with more dyno progress and more building physical things soon.