March 7, 2018

The Rubik's Contraption

A collaboration with Jared.

Before any build log, here's a video:

That was a Rubik's cube being solved in 0.38 seconds.  The time is from the moment the keypress is registered on the computer, to when the last face is flipped.  It includes image capture and computation time, as well as actually moving the cube.  The motion time is ~335 ms, and the remaining time image acquisition and computation.  For reference, the current world record is/was 0.637 seconds.

The machine can definitely go faster, but the tuning process is really time consuming since debugging needs to be done with the high speed camera, and mistakes often break the cube or blow up FETs.  Looking at the high-speed video, each 90 degree move takes ~10 ms, but the machine is actually only doing a move every ~15 ms.  For the time being, Jared and I have both lost interest in playing the tuning game, but we might come back to it eventually and shave off another 100 ms or so.  

Here's the contraption:

The contraption is built from:

Several cubes were broken and rebuilt in the process:

Build Log

The hardware started out with a 1-axis test setup cobbled together out of spare parts.  Below is a big ServoDisc U12 and cruft encoder found lying around MITERS, the motor controller I built for 6.131 a few years ago, with everything wired into a Nucleo for control.  I used this setup to implement the minimum time controller in hardware, and figure out a ballpark for how fast this thing could go.

The answer was pretty darn fast.  

The plot shows a sequence of 90 and 180 degree moves.  Each 90 degree move here took a little under 15 ms.  And that was only at 40 volts.

We tested with an actual cube, using Bayley's Photron for high-speed recording. The "speed cubes" we used have pop-off caps at the center of each face.  Beneath the cap is a screw you can use to adjust the amount of slop in the cube.  We machined some square-drives that press into the recess beneath the cap, so no cube modification was required.

One counterintuitive trick for getting things to work well was to make the cube really tight.  When the cube is loose (like it would be if a person were trying to solve it fast), the outer faces just cam outwards when you try to turn the center faces quickly - see the Cubsplosion video for what this looks like.  It took tightening the cube way past what intuitively felt appropriate, in order to stop the camming action from happening.

From this point, the hardware was relatively straight forward.  The big ServoDisc was swapped for six smaller ones;  2 from my old robot arm, and 4 more from ebay.  The smaller ones have the advantage of having higher torque-to-inertia, so should are capable of even greater angular accelerations, and draw much less power in the process.  I machined a bunch of motor-to-cube couplings, and threw together a box to hold everything out of some laser cut acrylic and scavenged 80/20.

To be specific, the motors are 4 ServoDisc N9M4T motors, and 2 UD9-E's.  These two motors have identical performance, but the N-ones use neodymium magnets, so they're much thinner.

Each motor has a US Digital 2000 line optical encoder strapped on the back.  While that much resolution definitely isn't necessary, we found the encoders for $14 apiece on ebay, brand new, which was an excellent deal.  The encoders only had 1/4" bores through the optical discs, and none of our motors had 1/4" shafts.  In fact, the N9's didn't have any shaft whatsoever protruding beyond the back of the motor.  We stuck each motor in the lathe, held by the front shaft, and reamed 1/4" holes in the backs.  1/4" dowel pins were pressed into the holes, to add enough shaft to mount the encoders to.  The N9's also had no features to mount the encoder sensor and housing to, so they were held on with some VHB.

I  built some custom DC motor controllers to do the servo-ing:

The motor controllers aren't anything too fancy - in fact, they don't even have current sensors on them.  Gate drive uses and external 12V power supply and bootstrapped gate drive optoisolators.  They've each got an STM32F303K8 doing all the control.  Commands are sent to each controller over a differential serial interface, and there are 2 extra GPIO pins broken out, which are used for synchronization between the 6 motors.  The controller is built out of 100V FETs, so I was expecting it to run up to 60V with no issues, but we've managed to blow up one of them twice at that voltage, when the cube locked up.  I'm not sure if it's a conventional FET-blew-up-from-too-much-current situation, or ringing on the bus causing the FETs to avalanche and die, or something like that.  It is suspicious that the same one has died twice though. I also have a set of lower-Rds(on), higher gate charge version of these FETs I could swap to, which should help on both accounts.

Here's the firmware running on them

Jared came up with an excellent system for synchronization/anti-collision between the 6 faces.  On the hardware side, this consists of the AND BOARD, which, as you might expect, basically is just an AND gate (it also does the logic power distribution).  Each motor controller has a digital output going to the AND BOARD, which ANDs the six signals, and a digital input driven by the output of the AND gate.

When a motor starts a move, it turns off the output of the and board, and turns it back on once it has completed the move.  When none of the motors are moving, the output of the AND gate goes high, signalling to the next motor in the move sequence that it's safe to start moving.

After the solve sequence is found, the sequence of moves is sent to all the motor controllers at once over differential serial.  Each motor controller has a different ID number, so they can pick out the moves corresponding to their ID number.  Using the output of the AND Board to keep the count of elapsed moves, the six motor controllers step through the sequence of moves until the cube is solved.

Two PlayStation Eye cameras are positioned pointing towards opposite corners of the cube, so that all 6 faces can be seen with only 2 cameras.  Jared was able to get these things running at ~150 fps under Linux, and at very low latency compared to your typical webcam

Here's part of the software stack ;)
Up top is a monitor showing the camera's views of the cubes.  Jared's software allows you to draw polygons on the screen, and assign them to different faces of the cube.  The pixels inside each polygon are averaged and compared to some thresholds to determine the color of each face.  We found that distinguishing the red faces from the orange ones was finicky, thanks to the low-quality of the cameras, so we sharpied the orange faces black.  Apparently that's allowed though.

Once the faces are identified, the solve sequence is computed using the min2phase algorithm.  This returns a solution which we've observed to be 19-23 moves for a well-scrambled cube.  The fastest solve we've done was actually a 21-move solve, so just getting a little lucky could easily shave off another ~30ms from the time.

And like all good projects, this thing requires a tall stack of power supplies to run - 5V logic power, 12V gate-drive power, 24V LED power, and motor power.

For consistent lighting, there's a bright LED next to each camera, lighting up the cube from the corners:

Here's a better view of how the motors interface with the cube.  Popping the cap off each center face reveals an adjustment screw, and a square cavity which the shafts interface with.  Each of the shafts can be loosened to slide out of the cube, so that the cube can be removed from and inserted into the box.

For added legitimacy, here's a scope shot of some signals during the fastest solve.  The blue trace is one of the pins on the AND Board.  When it toggles high at the end, the motor drivers think they are all done moving.  From the high-speed video at the beginning of the post, you can see that the AND board actually turns on a few ms after the cube could be counted as being solved.  The yellow trace is connected to the DTR pin on the USB FTDI adapter going from the computer to the motor drivers.  When the solve button is pressed, the first thing the software does is toggle this pin, before even capturing the images from the webcams.

Here's another entertaining scope shot.  The yellow trace is the AND board, blue is the DC bus current measured with the LEM-Stick.  The purple trace is the integral of the blue trace.   During this particular test, the contraption drew 6 Amp-seconds of current (i.e. 6 coulombs of charge) at 40 V, meaning that the solve took 240 Joules of energy, and peak power was ~1300 watts.

February 17, 2018

Small motor controller with integrated position sensor

A while ago I added the hall effect encoder IC I've been using directly to the motor controller PCB.  The controller sits directly on the back of the motor (with a magnet added to the motor shaft), and the phase wires solder straight in.  I also have a pair of board-mounted XT30 connectors on the DC bus for easy daisy-chaining.  Otherwise, the board is basically identical to the previous version of this controller.  I've now built over a dozen of these, and have had no problems.

I plan on doing another revision of this controller using the new replacement to the DRV8301/2, the DRV8323, which Austin has played around with already.  That should let me shrink the controller even more.  I also plan on designing some general-purpose mounting brackets that fit a wide variety of hobby motors, to make it really easy to strap these controllers on the back of basically any small motor.  In a similar vein, I've also mostly written an auto-tuner for automatically measuring motor parameters for tuning the current loops.  I've verified that the measurement works, but haven't integrated it with the rest of my motor control code yet. 

Once I get those things done, it should be super easy for me to throw all the small motors I have onto the motor dyno and fill out the motor database quite a bit.

For testing and easy dyno-ing I machined some mounts for attaching the controller on the back of my T-Motor U8's and U8 knock-offs.  This isn't the intended use of this controller, but I'll get into that in a later post.

Parallel XT-30's for power, and parallel SPOX for the CAN bus.

Same old serial and programming connectors up top:

The whole reason for the linearization step in my autocalibration procedure was because I misplaced the encoder IC in my first revision of these boards, so the encoder sat .5mm off-axis.  So rather than waiting on the board revision to get things up and running, I fixed the problem in software.  The version of the controller here is fixed though.

Here are the eagle files and gerbers for the boards if you're interested, although I'll hopefully get to the DRV8323 version soon, making these obsolete.
4-layer (2 extra layers on the power planes are way cheaper than 4-oz boards.  Helpful for daisy chaining)

December 31, 2017

Controls Ramblings: How to get from Point A to Point B very fast (and stop)

In an out-of-character move for me, this post has no actual hardware in it.  Sorry to to disappoint you, dear readers.

This a simple but interesting controls problem relevant to a project I'm working on:

Problem: How do you move a thing from somewhere (point A)  to somewhere else  (point B) in the shortest possible amount of time, and stop dead on point B?  Elsewhere, you might see this called "minimum time control", "time optimal control", or something similar.

Let's say the thing is a mass \(M\), and we can apply a force \(F\) to it, and the problem is 1-D: the mass moves along a line.

Ignoring the shortest possible time part of the question for a second, the obvious linear control approach to this is a PD controller.  If the controller gains are chosen so that the closed-loop response isn't underdamped, \(M\) will converge to B without ever overshooting it.  By cranking up the gains, it will converge faster and faster, assuming the system is perfect (i.e. it's a perfect mass with no other dynamics, and you can instantaneously take perfect measurements and apply perfect force.  Not that these are realistic assumptions).

Here's what that looks like, with gains chosen such that all the closed-loop responses are critically damped.  The legend shows the closed loop natural frequency.  As that increases, response gets faster and faster, but the force required also increases.

And here's an animation of the performance of the ideal PD controller, for the Wn = 2 case.  The box is the mass, and the red arrow is the force applied to it.

Now add in one simple constraint:  Limit to how big \(F\) can be.  Here's how those PD controllers perform on the otherwise ideal linear system.  Now they all start off the same, since they saturate the force limit.  And though the Wn = 10 case is higher gain, by most metrics its step response looks worse than the others.  So why not just pick the best looking one (probably red or yellow in this case), and run with those gains?

Here's how they look taking a smaller position step:  Now the high-gain green curve has the best performance, because the controllers are barely saturating the force limit, and still behaving mostly linearly.  So with this linear control strategy, there isn't one single control law that gives identical performance for different step sizes.

Fortunately, in this limited-force case there's a very intuitive answer for the original question of How to get from point A to point B as fast as possible and come to a complete stop.  Clearly, you should apply maximum force for as long as possible while accelerating, then part way there, apply maximum force in the other direction to come to a stop as quickly as possible.

Here's what that looks like:

So how do you write down the control law that gives you this behavior for any starting position, starting velocity, and step size?  It turns out to be pretty straight forwards by taking a look at the system in the phase plane.

This system has 2 states, position \(x\) and velocity \(\dot{x}\).  The phase plane plot is just a parametric plot of \(x(t)\) and \(\dot{x}(t)\).  The goal of the controller is to bring the mass back to the origin, i.e. position = 0 and velocity = 0.  To figure out the control law, work backwards from the origin: First, what happens when a constant force \(F\) or \(-F\)  is applied to the mass?  Starting with \(F = Ma\), and the stopped-at-the-origin initial conditions \(x = 0\) , \(\dot{x} = 0\) , we can integrate to get velocity, and again to get position, giving the result \(\dot{x} = \frac{Ft}{M}\) , and \(x = \frac{Ft^{2}}{2M}\) .  To plot these trajectories in the phase plane, we need to get rid of the time variable, so we get \(\dot{x}\) as a function of \(x\).  This gives \(\dot{x} = \sqrt{\frac{2Fx}{M}}\), so the resulting solution curves are sideways parabolas, expanding to the left for \(-F\) and to the right for \(F\).

The following plot shows these two trajectories, with arrows indicating the direction of motion.  The orange trajectory corresponds to negative force, and the blue trajectory positive force.  The bold areas of the trajectories are the paths to the origin - i.e., if the mass is on one of these trajectories, it will reach the origin.

The bold curves form a switching surface, and nicely divide the state space into two halves.  When the mass is to the left of/below the switching surface, the control action should be to apply positive force.  This will move the mass such that it reaches the bold orange curve.  Then, once it has reached the orange curve, the controller should switch to applying maximum negative force, so that it follows the orange curve and stops at the origin.  Similarly, if the mass starts to the right of/above the curve, the controller should apply maximum negative force, until the state reaches the bold blue curve.  Then it should switch to applying maximum positive force, until the state reaches the origin.

Here's a shaded version of the above plot, indicating those two regions.  Where it's shaded orange, the controller should apply negative force, and where it's shaded blue, the controller should apply positive force.

The explicit control law that implements this is:
$$F = F_{max}\cdot sgn(-F_{max}x - \frac{M\dot{x}|\dot{x}|}{2})$$
Which you can get by solving the equations above for  \(F\), and carefully paying attention to signs. And to get to some point other than the origin, just replace the \(x\) on the right side with \(x-x_{desired}\).

A probably more intuitive way to get the same result is to look at energy.  The kinetic energy stored in the mass is \(\frac{1}{2}M\dot{x}^{2}\).  The energy the controller can remove from the mass before it reaches the origin is the integral of force over distance, or in this case just \(F_{max}x\).  So the controller should switch at the point where the energy it can remove is equal to the kinetic energy stored in the mass - as in, the "stopping distance" is the same as the distance to the origin.  This gives you the same control law as before.

The phase-plane version of the previous animation looks like this.  The trajectory the mass follows is shown in red, the blue parabola shows the trajectory it starts out on, with positive force, and once it hits the orange trajectory it switches to negative force, to to stop at the desired position.

So that's pretty cool.  But what if there's a different limitation than a maximum force?  For example, say the mass is driven by a real actuator like a DC motor, and there is a maximum voltage \(V\) that can be applied to the motor terminals.

Ignoring inductance, the DC motor's performance is described by the following equations:
$$V = Ri + K_{t}\omega$$
$$\tau = K_{t}i$$
Where \(i\) is the current through the motor,  \(R\)  is the motor's  resistance,  \(K_{t}\)  is the motor's torque constant,  \(\omega\)  is the motor's angular velocity, and  \(\tau\)  is torque.

Now the controller is voltage limited, rather than force or torque limited.  But we can take the same phase-plane view of the system and derive the switching surface which goes to the origin as fast as possible.

Before actually solving for the switching surface, I found it helpful to use a little intuition to predict what I'd expect the constant-voltage solution curves to look like.  Most obviously the maximum voltage constraint imposes a speed constraint - the no-load speed of the motor.  So in the first and third quadrants, velocity should level off to a value of \(\omega =\frac{V}{K_{t}}\).   
For the second and fourth quadrant, it's not intuitively clear what the exact behavior should be, but as speed increases, force also increases, so the curve should slope more steeply than the constant-force parabola.  Around the origin, where the motor is at low speed and behaves mostly like a resistor, the curves should look more or less like sideways parabolas, like the constant-force case.

Here's a sketch of what I thought it would look like:

To find the new switching surface, like before, write down the dynamics with a constant voltage applied, and calculate out the solution curves.  To keep notation consistant with the constant force example, here \(F = \tau\), \(\dot{x} = \omega\).

$$F= K_{t}i = K_{t}(\frac{V-K_{t}\dot{x}}{R}) = M\ddot{x}$$

Solving the differential equation with initial conditions of \(x(0) = 0\),  \(\dot{x}(0) = 0\):

$$−M\ddot{x} − \frac{K_{t}^{2}}{R}\dot{x} + \frac{K_{t}V}{R} = 0 \tag{1}$$
$$\dot{x}(t) =  \frac{V}{K_{t}}\left(1 - e^{-t\frac{K_{t}^{2}}{MR}} \right) \tag{2}$$
$${x}(t) =  \frac{V}{K_{t}}\left(t +  \frac{MR}{K_{t}^2}\left( e^{-t\frac{K_{t}^{2}}{MR}} -1\right)\right) \tag{3}$$

Jeez, that took way too long.  Can't remember the last time I actually had to explicitly solve a differential equation and didn't use a computer to numerically integrate it for me.

To get the phase plane solution curves, solve (2) for \(t\):

$$t = −\frac{MR}{K_{t}^{2}}\ln\left(1 − \frac{K_{t}\dot{x}}{V}\right) \tag{4}$$

and plug (4) into (3), and you get the equation for the curves in the \(x\), \(\dot{x}\) plane:

$$x = -\frac{MR}{K_{t}^{2}}\left(\dot{x} + \frac{V}{K_{t}}\ln\left(1-\frac{K_{t}\dot{x}}{V}\right)\right) \tag{5}$$

Here are the actual curves.  Pretty close to what I was expecting.  Again, the switching surface is in bold:

Getting the actual control law requires more futzing around with signs and absolute values to pick the correct quadrants of the solution curve, but that works out to be:

$$V = V_{max}\cdot sgn\left( -x  -\frac{MR}{K_{t}^{2}}\left(\dot{x} - \frac{sgn(\dot{x})V_{max}}{K_{t}}\ln\left(1+\frac{k_{t}|\dot{x}|}{V_{max}}\right)\right) \right)\tag{6}$$

And finally, here's the controller in animated form:

I've already tried this out on hardware, and it works very well, but that's for another post.

November 2, 2017

Stall Torque Test Stand

To better characterize the big kart motor, I threw together a stall torque test stand.  This was actually several months ago, but I'm just now getting around to documenting things.

The stall-tester consists of a precision-length stick attached to the motor shaft, and a 50 kg load cell on the end of the stick.  I added ball joints on each end of the load cell, to ensure that it is in pure tension or compression.

To visualize and log the data, I made a pared-down version of my dyno control gui:

The basic goal of testing was to brute-force determine the motor torque vs D and Q current.  Ideally, torque should be easy to calculate from the D and Q axis inductances and permanent magnet flux linkage, by the classic torque equation \(\tau =\frac{3}{2}\cdot p \cdot i_{q}(\lambda_{pm}+(L_{d} - L_{q})i_{d})\).  But we're driving the motor hard enough that it saturates fairly substantially. This changes both the inductances and flux linkage, and makes analytically solving for torque (and modeling the motor in general, without FEA) challenging.  Using our various motor-models, we've gotten decent results, but a bit of low-end torque has been eluding us.

To take the measurements, we swept current phase angle from pi/2 to pi, and current magnitude from 0 to 180A.  At each phase setpoint, current was ramped up from zero to 180 over about 2 seconds.  Torque was logged from the load cell, and D and Q axis currents were logged over serial from the motor controller.

The time-series data looked like this:

After a little processing (3-sample median filter to throw out some single-sample glitches from the strain gauge amplifier, and torque-constant sanity check to throw away points between each ramp), the 3-D scatter plot of the points looked like this:

A problem I seem to run into all the time is trying to fit a smooth surface to time-series data.  In the past I've usually used MATLAB's griddata to do it like this, but this approach has a number of annoying problems, like that it can't extrapolate, and the surface generally ends up kind of "lumpy".  Fortunately, I finally found an excellent script, gridfit, which worked beautifully for this problem.

Finding the phase that produces the most torque for each current magnitude, you get the following trajectory.  As you'd expect, at low currents most of the current is on the Q-axis (phase equal to pi/2), and it shifts onto the D axis as current increases.

Superimposed on top of the surface of all the data, the trajectory looks like this.  It's pretty coarse looking at low currents, but that part isn't too important anyway.

This is the maximum torque per amp (MTPA) trajectory, which is what the motor should operate along until it reaches base speed and you no longer have enough voltage to operate along the trajectory.

Excitingly, the motor seems like it still has plenty of torque capability left, even at 180 amps.  Torque constant is dropping off somewhat, but it looks like breaking 60 N-m should be no problem.  On one motor we should be able to hit just over a G of acceleration, and with 2 motors, easily break traction on command.

October 26, 2017

A brief introduction to the big go kart, and machining some PolyChain sprockets

Brief Introduction:
Go read Bayley's introduction.  TL:DR:  Racing kart frame with hybrid car parts based powertrain and custom motor control.  We've done a lot of motor modeling, characterization, and control over the last year, and the go kart is getting quite performant.  But we're not quite squeezing all the possible performance out of the KIA HSGs yet.  And the kart's going to get a second motor.

Right now the go kart uses Gates Micro-V belts, because the HSG's come with those pulleys pre-attached.  However, we're driving the motors a fair bit harder than the car they came from did.  The v-belts, even with a huge amount of tension, slip at ~45 N-m of torque, and we've been able to hit nearly 60 N-m at 180 phase amps.  So we're switching the kart over to Gates PolyChain GT Carbon belts, which are basically the best synchronous belts you can get.  The inch wide Micro-V belt is getting replaced with a 12mm wide, 8mm pitch PolyChain belt, which should be good for substantially more torque.

You can download CAD models of the sprockets from Gates, but, as they warn you on the website, the tooth geometry of the sprockets is not actually accurate, since the tooth profile is proprietary.  My first plan was to try to figure out the tooth profiles from the patents (1, 2), but it turns out if you just ask nicely, Gates will send you the tooth profile drawings  drawings for the sprockets you want.

We went for a reduction of 4:1, with a 20 tooth motor sprocket and an 80 tooth sprocket on the axle.  The big sprockets were too big to fit on the MITERS CNC mill, so I did them on my lab's Haas SMM.  For fun I GoPro'd the entire machining process for one of them:

Getting a good finish on the teeth required many very shallow passes - the minimum curvature radius in the grooves is very close to matching the 3mm end mill I was using, so I had to go really slowly to avoid chatter at the high-engagement areas.

The motor-side sprocket was done on the MITERS CNC.  I still need to broach the keyway in it, and turn a clamping hub for the big sprocket.

Here's one of them partially installed on the go kart.  Still needs a hub to clamp the axle.

So low profile!

Back to the subject of squeezing all the performance out of the KIA HSG, next kart post will be about a stall test stand we through together for motor characterization.