Friday, February 7, 2020

Closed-loop adjustable speed drive for flywheel blasters; new digital signal protocol and SimonK variant.

SimonK code for the impatient

Closed-loop speed control of flywheel drives is, if you ask me, an indispensable and critical feature and a huge step above BLDC operation. I would never want to design or play without it. However, the usual sort of drive that offers this in a form that actually performs well for flywheels is SimonK with its stock safety governor reset to a desired speed. The main shortcoming with that is that the governor was never originally meant to be used as a service control and had to be set at compile time.

In the old days, this would be more than fine - having a blaster that you can reflash to change velocity certainly beats a blaster where you have to change hard parts to change velocity, and hell, much of the world is still living in that era with DC motors and Stryfoid hosts. Also, a specific reason to not implement adjustable speed is that drives with hardcoded speed limits tend to make game organizers happy by thwarting cheating and preventing blaster manager firmware shenanigans and battery shenanigans from being able to affect safety.

However, there is a lot of interest in convenient user-adjustable speed and in the system programmer being able to control speed from the blaster manager's end along with everything else. This has kept a lot of brushless adopters using open-loop voltage command instead of closed-loop speed command in their work. That, is unfortunate.

Fixing this was the subject of several experiments of mine:
  • A SimonK variant which straightforwardly used the stock throttle signal input code to operate the governor.
  • A venture into using external control loops on the blaster manager.
  • A SimonK variant which uses a digital signal protocol to configure the governor.
The last is obviously the success.

Why using analog PWM throttle signal is not the answer


Motor drives in hobby space have long accepted the "1-2ms" analog PWM protocol, which encodes a command signal as a pulsewidth, nominally between 1000us and 2000us for idle and floored respectively (or perhaps bidirectional, centered on 1500us, for reversible drives) with a nominal carrier frequency 50Hz. This set of timing parameters arises from how the controllers of early analog RC servos worked and how early RC receivers multiplexed radio commands to multiple servos prior to the use of digital radio gear and high-power motor drives in those hobbies (i.e. when they were dominated by combustion engines and crude mechanical motor controls, probably before you were born).

Since then, those protocols have received a few updates. The first has been that the carrier frequency is allowed to be much higher than the painfully slow 50Hz. SimonK, for instance, has a quite general pulse capture handler and allows any frequency that it is possible to fit the configured maximum pulsewidth into, so with a full throttle pulse length of a little under 2ms, a usual nominal value is 490Hz for the maximum update rate of the 2ms protocol. Following on from that, several standardized-ish "protocols" have been defined that use shorter ranges of pulsewidths. These are often called OneShotnnn. By using shorter pulses, they allow higher carrier frequencies and accordingly allow a drive to have more control bandwidth - which is why multicopters, which have offboard control loops steering the drives which continue to improve in computational performance, have been the main field pushing the use of faster throttle protocols. SimonK has preconfigured support for OneShot125, the most common, using a 125-250us pulsewidth range.

Most recently, the drone world has ditched them completely in favor of digital protocols. The standard here is called "DShot". You can go read all about it elsewhere - but the matter of going digital is not (just) about faster signals and more throttle bandwidth, necessarily. Rather, it is a matter of removing error inherent in using a relatively short pulsewidth to encode a value that might be expected to have 10-bit or higher resolution. The oscillators on our sending and receiving MCUs aren't perfect and so these protocols are always nondeterministic and plagued with drift and jitter and other artifacts.

Another wrench thrown into the matter is that often, a drive itself - powered by a simple and cheap MCU - has limited resources while managing a motor at high speed, and there is inevitably a 1/x delinearization that needs to take place somewhere between a speed command (fundamental motor frequency) and a period (1/f), which is what motor-aligned code actually works with. This is, in short, why that old analog variable-speed mod was junked - the AVR MCUs it runs on make the delinearization math too computationally expensive to do on the drive's end while possibly spinning a motor - and doing the delin on the other end results in the analog signal's error margin having an exponential impact on the speed command. This is an ender problem.

Overall though, regardless of workarounds that could be used for that, what we want if we are going to configure speed setpoints over the wire in blasters is definitely not an analog pulsewidth signal. Too imprecise, too much trouble, we know better now.

What about offboard control loops for flywheel speeds?


This is another direction that comes up.

In short, it is an assumption that a motor drive itself would contain the main control loop for an entire mechatronic system, and perhaps not even expected.

Look at multicopter drones - they use a flight controller, which is in the same role as the blaster manager in a software-defined AC-driven blaster system, to take operator and sensor inputs, do all vehicle dynamics computations and output the resulting throttle values to multiple motor drives.

Look at industrial automation - a servo drive for a robot or CNC axis or automated assembly line gizmo is probably taking a torque (current) command. The dynamics math for what is being moved is then done by "something else".

We do have access to speed signals from the drive, so why not just bang up a PI or PID loop for each wheel and call it good?

Well, I tried that. It works (of course). But like most PID controllers for various processes, heating and motion systems that you may know, it requires a lot of tuning to perform acceptably. The control loop parameters end up being specific to a certain drive system and load. Set them wrong and you're going to have your drive overshoot, oscillate, or have shitty transient response. What worked on one rotating assembly would be completely clobbered by a different motor or wheel.

A lot of this comes from latencies and bottlenecks in the offboard control loop approach, including the throttle signal protocol as well as the tach signal being only 1 pulse per electrical rotation.

Then, resource contention again becomes a challenge; this time on the blaster manager, which is itself a motor drive for the feed system in my case and is now tasked with keeping track of a 100-pole motor turning at 1000rpm while running multiple control loops. Bit of a headache, and a bigger hammer (such as moving to a fancy, expensive MCU) is not an ideal answer.

SimonK's magic governor


So what is the deal with the SimonK safety governor? What sort of controller IS it, and why does it work so well?

It is a brutal approach if nothing else - on every timing update, if the setpoint speed is exceeded, the voltage command limit is sliced in half with a LSR/ROR operation. The voltage command limit then "more slowly" ramps back up toward maximum (filtered by the throttle setting) via the same logic which controls the voltage apply rate from idle. It isn't bang/bang or PWM because it isn't 1 bit, but nevertheless it involves dithering between cruder steps to create some intermediate value - at steady-state, these two bits of code are swatting the voltage command back and forth about whatever equilibrium it actually ought to reach rather than it ever settling precisely there. Takes effectively no math to implement so is fast and lightweight, and importantly, is directly part of the basic inverter control itself and doesn't have any latencies that aren't also latencies in controlling the inverter/motor. There is a better control-theory-based way to abstractly explain this, but it doesn't overshoot - and doesn't need damping to not overshoot, and doesn't go haywire from having an implicitly rather high P gain, and thus doesn't need any integral effect to compensate for lack of P gain - because it is exactly as fast as the commanded system.

It's a sort of case you would wish for that can make a simple control loop work well. It's like some idealized heater control situation where the thermistor can be directly at the hottest part of the heater, has no thermal mass of its own, the thermal conductivity to the functional parts is infinite, and there is no lag, and the thermistor is monitored and control calculated on every single PWM cycle of the power stage. You can just use a P control with a gain of one metric shitload and be fine. That's why it's magic.

Put the Simon governor on a flywheel assembly, ANY flywheel assembly, any motor, any inertia, any drag torque/windage load, and apply any operating load, and you get pretty damn crisp speed control. Not absolutely perfect, mind you (you might get a tiny startup overshoot at certain speeds for instance), but hell, it's WAY more than good enough for government work. You can hear how well it works every time players slow down video of T19s firing.

...working name: FlyShot


So the way forward is laid before us. Now all we need is a digital signal protocol.

Ideally, we would want something quite fast in data rate, and ideally, we would also want error detection, like DShot has, but it's not like it is necessary to program the governor setting with any speed - it is, or can be, a configuration operation.

Do we USE DShot itself? Well, canonical DShot doesn't carry a big enough payload for full-resolution governor updates in one frame. Also, DShot's typical timing parameters are too fast thus WAY too noise-vulnerable for my taste.

The protocol should try to be backwards-compatible, and the motor enable signal should be simple to generate on all platforms with a basic PWM peripheral - which means that it is NOT itself a digital packet that needs to be sent constantly just to spin motors and keep them on, but a single pulse or a constant logic level or something of that nature.

What I came up with as a quick proof of concept, but have found quite robust, is a 4-level protocol that encodes digital packets using a train of consecutive sub-750us pulses (nominal T0H of 100us, T1H of 400us and TL at least 500us between). The 1000-2000us "normal PWM" pulse range is treated as a binary enable/disable command, but it could also be an analog voltage command if anyone wanted and the throttle code was stuck back in place. Packets are 16-bit, sent MSB first, consisting of a mandatory leading 1 and a 15-bit governor value, preceded and terminated by throttle-range pulses. Bits sent beyond 16 left-shift all previous data off the end of the 16 bit buffer. Data received between throttle pulses without leaving a 1 in the MSB of the 16-bit buffer is ignored and the buffer cleared. A redundancy is that updates should always be applied repeatedly as with Dshot commands, and throttle pulses should be continually sent at all times while not transmitting packets, which temporally precludes receiving any phantom packets. But really, I should have CRC like Dshot does on future iterations. This will be mandatory if the data rate is increased to avoid noise problems.

Here's the SimonK fork with this implemented. Remember to change your TIMING_MAX safety governor to something higher for higher speed setups like Ultracages and Hurricanes.

An example FlyShot transmitter is in this very much alpha S-Core firmware. There is plenty of commented out debug code and trial features in that, a few bugs (I don't think the tournament lock actually works when booted without the trigger down), plenty of non-final UI behaviors, ROF input isn't linear because I didn't bother yet, and also, there is some hackness going on with how that governor interrupt is turned off which is all either fixed or getting fixed right now - that was me trying to get variable speed and closed-loop STC (etc.) on the field right before a war.

The signal protocol works very solidly in general though. There are currently two adjustable speed T19s out there running it.

I'll have real example code and timing diagrams and whatnot later. I've got code to write, boards to design and darts to burn at the moment.

4 comments:

  1. Question about using this on afros. When I try to compile it I get an "afro_nfet.asm(2529) : Error : Found no label/variable/constant named rc_do_scale", and another on "afro_nfet.asm(2542). Disabling "USE_I2C" and "USE_UART" in afro_nfet.inc (they're already 0 by default in bs_nfet.inc) allows me to compile, but I thought I'd ask if they did anything important on Afros before I put this on hardware.

    ReplyDelete
    Replies
    1. USE_I2C and USE_UART are for some early multicopter systems in which you hooked all your motor drives onto a single addressable data bus. What's almost certainly going on there is that some throttle handling code has been axed for the sake of streamlining things, reducing overhead, etc. and thus several such routines and the label rc_do_scale no longer exist, while USE_I2C and USE_UART have not been updated to mesh with the new changes. They would hook directly into the old throttle scaling code hence that call. Disabling them is fine.

      This SHOULD work on Afros just fine, but I have NOT tested/used it on anything except bs_nfet boards by chance. Afros generally use the Timer1 ICP pin for the signal input whereas bs_nfet boards use an external interrupt pin - but the pulse input handler that has been modded here is abstracted away from that and ought to work the same with either. MOTOR_DEBUG (tach) should also work fine on Afros, find the MOSI pad.

      You do have regular discrete drive Afros, correct? I seem to remember a conversation with you a long while back that involved an Afro HV20. If your board has 3 SO8 packages (gate drivers) it is not an afro_nfet board, it is an afro_hv.

      Delete
  2. After that conversation an ebay sale popped up for about 15 regular afros so I never ended up getting any HVs. All mine now are 20FS or 30FS, some BEC, some non-BEC. At least I think. I have a few used ones without the label which I think are 30FS, but I plan on leaving those until I run out of everything else.

    I'll try the tach-based STC probably on my next blaster, but my plan right now is to retrofit an existing blaster with FlyShot and have it choose one of two speed setpoints based on trigger state at boot. Don't want to have to run any new wires right now, so I'll just deal with delay schedules.

    Sorry if I spammed your inbox earlier. My comments weren't showing up and blogspot didn't actually tell my why until like the third attempt so I thought there was some bug that was eating them.

    ReplyDelete
    Replies
    1. Huh, I only got one comment on this end. I guess they were being eaten by a bug. Sorry that I have comments set to approval only, but the robot spam rate was too intense.

      Delete