Sunday, March 12, 2017

EMG Chase Game

I dusted off SparkyEEG recently and used it for some fun with EMG processing, as well as an excuse to do some learning with TensorFlow and deep learning (as an aside I read the Deep Learning Book by Goodfellow et al. and really enjoyed it). I placed the 8 electrodes across various muscles, mostly around shoulders.

I'm using some fairly simple (and common) preprocessing on the raw electrophysiology data to first get the EMG power. This applies a bandpass filter, rectification, and then measures the "waveform length" which is the integral of the absolute value of the difference.

Representation learning

First I wrote up an autoencoder (and then a variational autoencoder) to take some EMG data and perform unsupervised dimensionality reduction. It also helps denoise the data and remove things like EKG contamination.

And here are some graphs of the latent variables learned

Arm movement tracking

Then I used my kinect to record my arm positions while recording EMG data in parallel. I used the a neural network to predict my arm positions based upon the EMG data.

And again here is a graph of the reconstruction (the data after 150s is held back test data), sorry for the time shift.


Then sort of putting this all together I wrote a game to chase a maker using EMG activity. The way this works is first prelearning a representation using a variational autoencoder as I sit and move my arms around. These graphs are from the tensor board that shows the log likelihood of the data improving and the Kullback-Liebler divergence of the latent space increases (the total lower bound does continue to increase). I should probably run it for longer as it hasn't stabilized, but it gets a bit boring :).

Then I start playing a game which adds a linear network to the output of the latent space to try and predict the X and Y positions of the cursor. Here you can see the mean squared prediction error improves while playing the game and then stabilizes after a while. The residual noise appears to be mostly high frequency noise. The learning function initially learns just the output network but then after enough time to initialize that can back-propagate through the representation layer to try and improve the representation.

Here is a video of me playing this game and how well it does. Not perfect but not bad for a first implementation I wrote up an autoencoder (and then a variational autoencoder) to take some EMG data and perform unsupervised dimensionality reduction. It also helps denoise the data and remove things like EKG contamination.

While playing it's basically co-learning with the user, as you pick some movements to try and control the cursor and keep sticking with it, and it tries to map from those movements to the cursor position. It will take some exploring to figure out the right dynamics to try and let it optimally bootstrap this system. For example:

1. picking the set of cursor position where the decoder is most uncertain
2. how long should the memory buffer be to stabilize the system versus to allow it to evolve
3. how should the learning rate of the network adapt with time.

Sunday, October 30, 2016

Linear Gaussian Quadratic Control System

For the last year when I've had time, I have been developing an entirely new control scheme (at least as far as I know for the open source community) for multi rotors that replaces PID by expanding upon Tau Labs System Identification (see the mathematical description here).

The way linear quadratic control (LQG) works is by simultaneously estimating additional properties in the system we cannot directly measure (in this case the instantaneous torque and true rate of rotation) and the using those to control the system. I had previously experimented with something a bit ad hoc like this in the past.

System Identification

A while ago Korken proposed a nice EKF that would extract a few critical properties of the system (primarily the 'gain' of each axis and the time constant of the motors). I wrote up an implementation of this and we got it running and measuring reasonable values. Based on this, I came up with some analytic approaches to optimize the PID control parameters based on these estimated system properties.

More specifically we use a model with several parameters that are specific to each frame $\beta_1$, $\beta_2$, $\tau$. The latency of the ESC/Motor/Prop is captured by $\tau$ and is one of the most performance limiting factors in a frame. For roll and pitch only $\beta_1$ is used and corresponds to the strength of that control output. For yaw there is a component that has no latency -- $\beta_2$ -- and that corresponds to directly generating a reaction force torque by accelerating the props.

$\dot \theta =  \omega$
$\dot \omega = \beta_1 \cdot \nu + \beta_2 \cdot \left(u_c - \nu \right)$
$\dot \nu = \frac{u_c - \nu}{\tau}$

The EKF above will estimate these parameters for a frame by having it shake while flying.

Online estimation

To perform linear quadratic control (LQR) we need to be able to estimate $\nu$ which is the instantaneous (normalized) torque on the frame. In addition, we would like to estimate $\omega$ -- the true rotation rate -- although we have the noisy measured values of it from gyro.

We can take the above system dynamical equations and convert it to a continuous time state space representation. Because the output may be slightly biased we also need to include this into the state estimation $b$. In addition, this estimator will not estimate the angle as we will take that from either the complementary filter or the INS.

$\mathbf x = \left( \begin{matrix} \omega \\  \nu \\  b \end{matrix} \right)$

We can the write down the equations above (including bias) as

$\dot {\mathbf x} = \mathbf A_c \mathbf x + \mathbf b u + \mathbf w$

$\mathbf A_c = \left( \begin{matrix}
  0 & \beta_1-\beta_2 & -\beta_2 \\
  0 & -1/\tau & -1/\tau \\
  0 & 0 & 0
 \end{matrix} \right)$

$\mathbf b_c = \left(  \begin{matrix}
  \beta_2 \\
  1/\tau \\
\end{matrix} \right)$

Of course since we are going to be implementing this in discrete time, we need to convert from the continuous time representation.

$\mathbf x_{t+1} = \mathbf A_d \mathbf x_t + \mathbf b_d u_t + \mathbf{w}_d$

$\mathbf A_d = e^{\mathbf A_c T_s}
 = \left( \begin{matrix}
  1 & A_{d12} & A_{d13} \\
  0 & e^{-T_s/\tau} & e^{-T_s/\tau} - 1 \\
  0 & 0 & 1
 \end{matrix} \right)$
$\qquad A_{d12} = \beta_1 \tau - \beta_2 \tau - \beta_1 \tau \exp(-T_s/\tau) + \beta_2 \tau\exp(-T_s/\tau) = (\beta_1 - \beta_2) \left(\tau - \tau e^{-T_s/\tau}\right)$
$\qquad  A_{d13} = - T_s \beta_1 + \beta_1 \tau  - \beta_2 \tau - \beta_1 \tau e^{-T_s/\tau} + \beta_2 \tau e^{-T_s/\tau} = - T_s \beta_1 + A_{d12}$

$\mathbf b_d = \int_0^{T_s} e^{\mathbf A_c s} ds \, \mathbf b_c
= \left(  \begin{matrix}
  -A_{d13} \\
  1 - e^{-T_s/\tau} \\
\end{matrix} \right)$

$\mathbf c = \left( \begin{matrix} 1 \\ 0 \\ 0 \end{matrix} \right)$

Because this system is a linear time-invariant Kalman filter, the covariance matrix will converge to a constant regardless of the observations and can be used to pre-compute a kalman gain. This requires solving the discrete algebraic Ricatti equation (DARE) to estimate the steady state posterior noise distribution (details omitted). With this we get an equation for the estimator.

$\hat {\mathbf x_{t+1}} = \mathbf A_d \hat{\mathbf x}_t + \mathbf b u_t + \mathbf l \left( y - \mathbf c^\mathrm{T} \hat {\mathbf x}_t \right)$

$\mathbf l = \mathbf P \mathbf c \left[ \mathbf c^\mathrm{T} \mathbf P \mathbf c+ r\right]^{-1}$

Which means once we have solved the DARE and for $\mathbf l$ the kalman filter only requires several multiplications and addition operations to update. When controlling attitude we use the angle estimate from the original complementary filter or INS algorithm.

Online estimation running during an autotune session. The green line in the top plot shows how much the gyros are denoised by the Kalman filter. The bottom plot shows the online instantaneous torque estimation, which is used to replace the original derivative term in PID.

Control algorithm

Armed with the state estimate we can use a linear quadratic regulator (instead of traditional PID) to compute the desired output. Here we talk about controlling the attitude. This controller has the form

$u = -\mathbf k^\mathrm{T} \hat {\mathbf{x}} + b$

Where $\mathbf k$ is optimized to minimize a cost function $J$ and $x=\left( \begin{matrix} \theta \\  \omega \\  \nu \end{matrix} \right)$.

$J=\sum_{t=0}^{t=\infty}{\mathbf x_t' \mathbf Q \mathbf x_t + u_t r  u_t}$ -- this uses $u$ without the bias added in.

$\mathbf x = \left( \begin{matrix} \theta \\ \omega \\ \nu \end{matrix} \right)$

Again converting the continuous dynamics to discrete time gives a system

$\mathbf A_d = \left( \begin{matrix}
  1 & T_s & A_{d13}  \\
  0 & 1 & A_{d23}  \\
  0 & 0 & e^{-T_s/\tau}
 \right) \\
\qquad A_{d13} = \tau (\beta_1 - \beta_2) (\tau e^{-T_s/\tau} - \tau + T_s) \\
\qquad A_{d23} = \tau (\beta_1 - \beta_2) (1 - e^{-T_s/\tau})$

$\mathbf b_d = \left(  \begin{matrix}
  T_s^2 \beta_1 / 2 - A_{d13} \\
  T_s \beta_1 - A_{d23} \\
\end{matrix} \right) $

We can then plug these dynamics into DARE, as well as the parameters $\mathbf Q$ and $r$ from above to which determine performance tradeoffs. From that we can solve for $\mathbf k$.

$\mathbf k^\mathrm{T} = (r + \mathbf b^\mathrm{T} \mathbf P \mathbf b)^{-1} (\mathbf b^\mathrm{T} \mathbf P \mathbf A)$


The parameters in the $\mathbf Q$ matrix ultimately determine the system performance -- how aggressively it attempts to drive the state to the setpoint (which will be discussed below). In this system we use a diagonal $Q = \mathrm{diag}(q_1  (1-s), q_2, q_3)$ which allows us to write the cost function a little bit more naturally.

$J=\int_{t=0}^{t=\infty}{q_1 \theta^2 + q_2 \omega^2 + q_{3} \nu ^2 + u_c^2 r \, \mathrm{d} t}$

This expression makes it clear that by having $q_1$ greater the system will more aggressively try and correct angular errors. By having $q_2$ greater it will also try and keep $\omega$ closer to zero when doing that, which has the effect of smoothing out the response. Similarly setting $q_3$ greater will attempt to drive the error to zero with the minimal amount of torque possible. Both of these will smooth the response out and lessen overshoot.

Because we are optimizing the feedback gains ($\mathbf k$) to minimize this function, we can divide out r -- or without loss of generality set it to 1 -- without changing $\mathbf k$.


To use LQR to control either rate or attitude (as opposed to drive both to zero) we need to redefine error from setpoint as

$\mathbf x_{r} = \mathbf x-\mathbf r$

which leads to us having a new control rule

$u=-\mathbf k^\mathrm{T} \mathbf x_r + b$

In order to have an attitude controller, we want

$\mathbf r_\theta = \left( \begin{matrix} \theta_r \quad 0 \quad 0 \end{matrix} \right)^\mathrm{T}$

In order to have a rate controller, we similarly change the setpoint reference to control the angular rate state and set the angle to zero

$\mathbf r_\omega = \left( \begin{matrix} 0 \quad \omega_r \quad 0 \end{matrix} \right)^\mathrm{T}$

and then set $q_{0,0}=0$ in order for the LQR controller to not penalize the angle not being at zero. This could be equivalently done with a two state system but would just complicate the code (and prevent doing things like smoothly transitioning from rate to attitude control, like in horizon mode).

Frame invariant behavior and costs

Without getting too much into the derivation, by slightly tweaking the cost parameters so that $q_3 = \beta_1 q_4$ we can ultimately generate responses that are much more similar across frames regardless of their parameters. It will never be possible to get exactly identical performance if we are pushing frames to the limits of what they can do as some performance simply is not achievable on a more sluggish frame. 

I simulated the closed loop response when changing parameters and using this representation of the cost and show that fairly consistent closed loop responses in rate mode would be achieved across a wide variety of frame types.

The response and control output for a step response with $\beta=8$ and $\tau=-3.5$ is shown here.

This shows a set of simulated step responses in rate mode when sweeping the $\beta_1$ parameter. Although there is a slight change in the shape, it is fairly minimal.

Similarly as we sweep $\tau$ there is also only a small change in the simulated closed loop performance.

Having this type of invariance has the benefit that once some more serious pilots and tweakers find a set of costs that work well for them it should apply more -- both when using them on different frames and sharing them amongst other pilots.

Real world results

As usual when testing these kind of control algorithm, I threw Freedom on a frame, which is a flight controller that has an Overo Gumstix on it that lets me log all the sensor data in the flight. I can then analyze the filter offline (as well as reprocess the data to refine things like the filter).

Here it is tied up to isolate one axis so I could test extremely high rotational speeds that I couldn't otherwise indoors.

A video posted by peabody124 (@peabody124) on 

And from that I can download the logs and look at the performance when doing fast movements in attitude mode as well as in rate control.

Here you can see me switching between rate and attitude control. In both cases the responses are extremely crisp.

Here is some data from a free flying frame.

The green points in the top trace show the desired angle and the blue curve is the actual angle. The system responds very promptly to the input stimuli. The bottom trace again shows the estimator that again smooths the gyro down.

This graph shows that the system responds very promptly. Because of the smoothed gyro and torque estimates, LQG can tolerate much greater effective gains than can work with PID control and no smoothing.

So why LQG?

After going through all this, it is worth taking a moment to ask why do we want to implement this. There are two main benefits corresponding to the two halfs of LQG. 

The first part is the benefit of the estimation component. By running the Kalman filter we fuse together the inputs that we are sending to the motor and the noisy gyro estimates to get the best possible estimate of what is really going on. Superficially, this has the benefit of significantly denoising the gyro signal -- after all that very high frequency noise is physically implausible but applying a low pass filter to it introduces latencies that people are always trying to chase out. In addition, it allows us to directly estimate the instantanous (normalized) torque being generated by the motors. This comes from the latency of their responses and allows us to properly damp the control algorithm rather than the ad hoc derivative term typical in PID. The derivative is critical for good performance and those who have been in this game long enough to remember when the bandlimited derivative from APM was ported over to OpenPilot recall how much of a performance boom it was. However, that band limiting is required because of the amount of noise when directly taking the difference of gyro measurements. The state estimation reduces the amount of noise there allowing much greater derivative terms. When looking at the parameters as the corresponding PID terms we can use much much higher PID parameters when using the state estimator. In practice I'm using a factor of 10 higher values than without this estimator.

The second half is the actual calculation of the gains. As I referenced in the beginning after we got the system identification going I came up with some analytic equations to optimize the PID coefficients from this (derivation is here). This has worked well and feedback on Tau Labs autotuning  has always been quite positive. However, LQR is a much more principled and mainstream approach to the same problem. It is more computationally intensive, but by only having to calculate the DARE equation once given the system identification parameters, we can do this before take off and avoid this cost in real time. Thus the costs of running this LQG is essentially the same as PID.


LQG is a more modern control scheme than PID. It flies much better. This implementation simply requires a regular autotune flight, but instead of using the math I previously developed that is now used by TauLabs/LibrePilot/Dronin it directly computes LQR coefficients. Here I show it flying on 3 frames of much different sizes without issue:

Tuesday, May 12, 2015


Sparky2 is available here

In case you want the video first:

After the amount of fun I had putting a VTX on Brushed Sparky, it got me asking what else would benefit from integration? In the background, I've also been plodding away at a few various revisions of TauOSD. This was inspired by BrainFPV resurrecting and vastly improving the OSD code. The video overlay of that was working fairly nicely, so adding a VTX directly on board seemed like a fun idea.


  • Fully graphical OSD with crisp black and white OSD and double buffering
  • Integrated 32ch 600mW 5.8ghz VTX. Channel selection provided via GCS.
  • Powered by STM32F4 with enough CPU power to redraw every frame
  • 5V and 12V switching supply that can be used for camera and external VTX. Nice clean power.
  • Switchable camera input (can be controlled via transmitter)
  • Runs up to 4S batteries.
  • Gets data from flight controller via CAN
  • Serial port available for mavlink telemetry (not implemented yet)
  • standard 36x36mm footprint
Back with 32ch module

This creates a really compact setup as you can stack a Sparky2 on top of this and have everything you need for flight, telemetry, OSD, and VTX in a 36x36 footprint (although I'd recommend a heatsink on the OSD). I still don't find an OSD incredibly useful, but the minimal set of things like altitude, battery, current and flight time really are quite useful.

Minimal hardware required for an OSD and FPV system. Sparky2 is powered from the TauOSD+VTX and sends all the information for the overlay.


I installed both Sparky2 and TauOSD+VTX in my havoc for testing. This is using the 12V supply to a PZ0420 board camera mounted on the front.

Here it is flying. This is running on a 4S battery directly into the OSD, which is then also powering Sparky2.

So far I'm quite happy with the video, which looks really crisp and clean. The OSD is stable and easy to read (thanks BrainFPV)!

The previous VTX used a TX5813 module followed by an HMC406MS8G. It was a learning experience to get all the video traces routed carefully at 50 ohm (working out for OSHpark specs), with components placed at various phases along the transmission line. I was happy to see it seems to work well, although I really could do with a VTX power monitor to see if it really hit the spec'd power.

The latest version uses a really nice 600mW 32ch module which is convenient for supporting all googgles.

Future plans

  • Sync the settings back and forth between Sparky2 and TauOSD to allow the awesome settings configuration menu that BrainFPV developed to be used.
  • Do more flight tests showing off features like video input switching.
  • Embed telemetry information in the audio stream

Thursday, May 7, 2015

Tau Labs and CyPhy "Level Up"

It was exciting to see another player adopting Tau Labs at their core with the announcement of the CyPhy LVL 1 kickstarter. This is a really neat drone project that removes the need for a gimbal while flying by being able to navigate around and remain level. This is a neat idea but traditionally multirotors fly by tilting to generate lateral acceleration. While they are using Tau Labs, they haven't yet released any code, so we can't look there. So how do they do it?

Note: I am affiliated with Tau Labs not CyPhy or LVL1 so this is all inferred from their press information.

My vectored thrust hexcopter
Figuring this out seemed like a fun evening challenge, and also highlights that Tau Labs is flexible enough to really make things like this easy. The best way to understand something is to replicate it, so let's dive in.

Traditional system

So a traditional quadcopter has four motors. The speeds of these four motors are computed based on four inputs: roll torque, pitch torque, yaw torque, and finally total thrust. This is a fully constrained system and has no possible other inputs. There are some ways to permanently alter it. For example many people are experimenting with tilted motors to generate horizontal force while staying closer to level. This allows faster FPV flights while keeping the camera more level (e.g. TBS Gemini, which also uses Tau Labs).

You can also make this more flexible but tilting the motors dyamically using a tilt mechanism. For example I put this on TriBlivion, which allows you to fly forward controllably while staying level.

Once you go to a hexcopter, you have an overactuated system: where you have more control outputs than you have desired inputs. For a traditional arrangement of motors (all horizontal, such as Y6 or flat 6) there is no way to use a different mixer and generate more forces. This can be written more formally: there is a matrix that translates from motor RPMs to torques (3 rotation directions) and thrusts (in 3 directions, forward, leftward, and upward) and they cannot be dissociated.

A more controllable hex

This does not have to be the case. If we do not have all the motors parallel, then is it possible to separately control thrust forward (like with tilted motor racing quads) separately from the thrust upward. I saw this image online:

Which shows the side profile in a more informative manner. You can see the front motor is tilted back and the middle motor is tilted forward. If you were to speed up the middle motor while slowing down the back and front motor, you would create no net rotational force while generating a forward force. If you speed up the front and slow down the back motor while speeding up the front motor, you would create a pitching upward torque. If you speed up all three, you create an upward force. So for these three motors you can separate forward force, vertical force, and pitching torque.

TL;DR: 6 motors means 6 controls!

As an aside, basically anyone that has been flying a V-tail has been doing this type of vectored thrust for years. With a V-tail you have two motors at the back that are angled and fighting each other. Combined they create a pitch force. Their difference creates a yaw force. However this is not an over-actuated system -- it has four motors and four controls.

Let's math it

Now let's try and extend this to a general solution with 6 motors and also while at it solve for the mixer that would allow us to control things.

Each motor will have a position -- in this case spaces at 60 degrees along a unit circle (although since this is a general solution we can change this later and re-derive it). The front two motors will be tilted backward and slightly outward (adding some yaw and lateral maneuvering). The middle is tilted inward and force, and the back are only tilted inward.

Put these into quaternion notation, we can then do a little bit of math to convert their thrust into the frame reference plane:

for i = 1:N
    q(i,:) = RPY2Quaternion([mot_r(i), mot_p(i), 0]);
    % the last column of the matrix that rotates from the body (motor)
    % to the earth (hexcopter) reference frame gives the force it generates
    % in each axis
    Rbe = Q2Rbe(q(i,:));
    force(i,:) = Rbe(:,3)';

Now those thrusts can be used to compute the lateral forces. However, they also create torques on the frame which we want to solve for. The yaw force from each motor is a mixture of two components. the first is from the drag of the blade on the air creating a rotation force on the frame that will be proportional to the Z force. the second and larger will be the cross product of the position of that motor relative to the center of mass. Since I plan to use this with tiny props, the thrust component of the yaw will be dominant and I will ignore the drag.

k1 = 0.0; % the drag component
k2 = 1;
for i = 1:N
    torques(i,:) = cross(force(i,1:3)', pos(i,:)');
    torques(i,3) = k2 * torques(i,3) + k1 * CW(i) * force(i,3);

We can visualize these forces and torques in 3D:

From this, we can generate a matrix that takes the set of motor RPMs and computes the torques and accelerations:

k1 = 0.0; % the drag component
% W maps from 6 motors to six control outputs
% roll, pitch, yaw, forward, sideways, up
W = [torques'; ...

Then finally using the pseudo-inverse (or since this is 6x6 we can use true inverse) we can calculate mixer matrix that will let us take the desired roll, pitch, yaw rates, and forward, sideways acceleration and solve for the motor settings.

k1 = 0.0; % the drag component
M = W'*(W*W')^-1;

The result gives us a nice sensible mixer matrix
k1 = 0.0; % the drag component
M =

    0.0068    0.0034    0.0084   -0.6356   -0.9976    0.1764
   -0.0068    0.0034   -0.0084   -0.6356    0.9976    0.1764
   -0.0060    0.0034    0.0101    1.2963   -0.0296    0.1764
    0.0011   -0.0068   -0.0115   -0.6433   -0.9510    0.1785
   -0.0011   -0.0068    0.0115   -0.6433    0.9510    0.1785
    0.0060    0.0034   -0.0101    1.2963    0.0296    0.1764

So basically we multiply this matrix by the desired behavior (roll, accel, etc) and get the motor speeds. BTW as a sanity check, if we remove the tilts the result says singular matrix and makes no sense. That's because a traditional (non-tilted hex) cannot do this.

Let's build it!

Ok, so now we know these motor positions create a controllable system, let's build it! I went ahead and whipped this up in no time in OpenSCAD. You can download your own (or change the motor parameters) on Thingiverse.

Ninety minutes of printing later, I had my motors in position. Luckily I'd be planning on making a hex version of BrushedSparky using a 3d printed attachment and already have the 6 necessary brushed outputs. So let's go ahead and mount this.


Next we use the custom mixer calculated in matlab with arbitrary scales applied to get normal ranges. This scaling is equivalent to changing the PIDs and we will autotune this at the end to get it better. At this point, it should not have as much unrequested forward and sidways motion. However, this isn't fun yet! We are still flying like a traditional system.

I then set up the slider on my transmitter to map to Accessory0 and added the entries into the mixer so that controlled forward thrust. Later if this starts flying really well I can actually make the slider provide roll and pitch and use the roll and pitch sticks on Accessory0,1 to fly around purely level.

At this point there was no more excuse to not fly :). It still can do with some tuning, but here are the initial flights. Here I'm using Brushed Sparky's native OpenLRS support. It has a tiny chip antenna mounted which is convenient for keeping it low profile.

You can see when I enable forward mode, there is a tendency to climb. At this point I think I'm hitting issues because of the linearization of the mixer. For small changes where the motors are nearly running at the same speed this is appropriate. However, for large forward inputs that makes the middle motor run quite high (which creates excessive thrust because that is super-linear with input) and the decrease in the front and back motors is not sufficient. This might be fixable with some static non-linearities in the mixer (e.g. sqrt) or could require a more sophisticated controller.

A second issue that is related but probably more about weight-to-thrust is that as when I generate enough forward thrust to be entertaining, it starts rocking in pitch a bit. I need to get some logs to confirm this, but I'm pretty sure the middle motor is running and practically shutting off the front and back motors.

Anyway, it doesn't fly wonderfully yet - but this is 12 hours after I got the idea ;-). I think it is a pretty solid demonstration of how you can take funny motor positions and generate a mixer to get new control dimensions, and a pretty nice proof of principle.


CyPhy have a really cool frame design, and it really shows off the versatility of Tau Labs that we can control airframes like this that weren't conceived of when we wrote the code. I definitely need to play with mine more to get it better tuned in and explore doing some level FPV flying. It might be that something a bit more substantive with brushless motors might be required to have sufficient thrust. I'll also be interested to see about flight times and efficiency. Having the motors fight each other (some pushing forward and some backward) is definitely burning batteries.

Speaking of thrust, I will be really really curious to see more videos of their frame flying in windy conditions. By using thrust vectoring like this to maneuver, the maximal amount of forward thrust is much less than traditional hexcopter navigation. For example, with a whole hex tilted at 30 degrees you get essentially 0.5 * gravity acceleration forward. On a vectored design like this for max forward thrust (while keeping the thing level) is substantially less than that.

Also behavior in wind will be interesting. An advantage of a gimbal is you have a relatively small mass that simply has to not rotate (it's natural state). For example I took this video at the beach in extremely stable conditions with Sparky Brushless Gimbal Controller:

Even though it is getting hammered by wind, the video is rock solid. I'm not sure you can ever get a stabilization controller so tight that you won't have wind moving it, so lacking that gimbal to isolate the motion might be tough.

I'm sure there are some clever tricks that CyPhy developed to get really nice performance. The clips in their demo reel look really nice. Tau Labs is GPL, so hopefully they will release their code changes at some point so we can see what they were!


So as much as vectored thrust is fun, I really like traditional flying. The control authority of pointing the motors in the direction you want to haul ass is huge. However, this motivated me to work out something I've wanted forever: how to take an arbitrary set of motor positions and angles and calculate the mixer

This is useful for some simple reasons: got your custom shaped frame, weird H, V tail, etc? Now we can solve for the mixer. Even more excitingly, this is exactly what is required to recompute the mixer on the fly for something like a tilt rotor airframe :). TriBlivion might be getting pulled back out of storage.

Friday, March 27, 2015

BLHeli OneShot Quantitative testing

I previously did some tests with KISS ESCs comparing the performance with OneShot to normal PWM modes showing Tau Labs implementation has a nice low latency from sensor update to output pulse, as well as the fact that OneShot results in better system performance.

Since then, I've wanted to replicate it with BLHeli's OneShot mode and see if it is has similar benefits. I got some BearHug ESCs, which unfortunately are quite unreliable and needed a few to be replaced which slowed things down. (In retrospect I wish I'd used these four in one ESCs). Anyway, I eventually got a working set of four and soldered the programming cable to them. I used my KISS PDB board again which provides a 5V BEC as well as current/voltage monitoring.

I put them on a ZMR250 frame using and this pretty nice cheap FPV system that seems to work fairly well. I'm also using these new plastic motors that are dirty cheap (30$ for four). They are spinning 5 inch props.

Of course, it's running a Tau Labs Sparky2 for the flight controller and TauLinkModule for control.

The PDB and ESCs fit quite nicely under the flight controller and leave room for the programming cables. And here it is all put together with the FPV system.

The antenna placement is probably a terrible idea ... the VTX is connected with velcro so can pop off in a crash. I'll redo this once some cloverleaf antennas arrive.

Flashing and Configuring

Once it was all put together, I followed this nice video by AKFreak on how to flash the ESCs (using a VM). This was actually the first time I'd played with an Arduino other than the one in my Shapeoko.

I programmed all of them with BLHeli 13.1

I went ahead and enabled Damped Light mode:


I used the Tau Labs system identification performance to measure the latency of the ESC responses with regular PWM and OneShot125 mode.

Regular (PWM High)

One Shot (PWM High)

One Shot (Damped Light)

This was done over two batteries so a range of voltages, although one of the slower outliers was actually with a fairly charged battery.

Regular (Damped Light)

At this point the BearHugs were becoming incredibly unreliable and dropping out of flight during autotune so I couldn't repeat it 3 times. Eventually I smelt the magic smoke and just gave up.


BearHug ESCs are really not reliable :( I've burnt so many hours trying to get flying on these. Can someone recommend alternatives that run BLHeli and are good for fast switching?

Average Tau:
Regular (PWM High): 0.034 (s)
One Shot (PWM High): 0.028 (s)
Regular (Damped Light): 0.038 (s) -- note this is only one trial
One Shot (Damped Light): 0.028

I'm not comfortable running statistics on this since the ESCs failed and I didn't get enough trials. There was more spread in the data than I'd have expected that wasn't explained by battery sag. One of the good regular runs was 0.029 and a two of the One Shot (damped) runs were greater than > 0.03. However, on average OneShot definitely seems to be trending in the right direction.

That being said, enabling OneShot consistently improves performance with these ESCs. The performance improvement was actually greater than for KISS (17% improvement) although it is hard to say if that is because of greater improvement for OneShot or worse performance for regular. I'd have to compare on the same motors / frame.  I'd be curious to see if Damped Light makes more of a difference on larger props.

The autotune settings were really snappy and I just wish I could trust this thing to stay in the air for five minutes (or now to even power up).

TL;DR: OneShot with BLHeli seems like a worthwhile improvement if you want that last ounce of performance.

Wednesday, March 25, 2015

BrushedSparky v0.2

BrushedSparky version 0.2 arrived a few days ago. 


There are a number of improvements over the previous revision.
  • A bigger motor mount hole allows for stronger mounts.
  • Using a 1.2 mm PCB shaves a bit of weight off. 
  • Changed the 5V step up regulator for a new one that is more efficient (and doesn't hum).
  • Using a more available VTX module so I can build more :)
  • Shuffled some components around for a slightly slimmed profile (also got a stencil to make it faster to populate).
  • Current monitoring (enables mAh consumed calculation)
  • Switch to disable VTX for flying LOS
  • VTX channel selection via the FC
  • LEDs on the arms for following via FPV
  • Two additional PicoBlade connectors for clipping arms to it and making a hex. Also two more buffered outputs on the back such as for LEDs.

I'm still controlling it via Tau Link Module, which gives me voltage, current, mAh remaining (as well as logging) on my phone relayed using the module in the transmitter. See the previous writeup for more information. Quite convenient since it gives me audio alerts. The new revision of Tau Link Module also has SPort support so hopefully I get this information on the Taranis soon.


The bare board assembled (without VTX on bottom) is only 12 grams:

And 41g ready to fly (without battery):

And now I have a nice row of micros above my desk:

Landing gear

One failure point is hitting the bottom of the motors and damaging them. I tried to design a more robust motor mount that protected them better and served as a landing gear:

Unfortunately it coupled vibrations too strongly into the frame and resulted in bad attitude estimation (not hovering level). I went with the original design for the motor mount and a landing cup that clips onto the arm and protects things.

They do pop off in crashes, but that's fine since it still saves any serious damage. I want to get my friend to try printing these in different materials - abs or flexible material - and see if it works any better.


Here are the parts I'm using for my preferred assembly. You can either buy all the parts from multirotorsuperstore or in pieces.


I'm really happy how it flies as well as the video quality (which the Dom V2 built in DVR doesn't do justice to). I started playing around in acro mode too and got some flips in, as well as a number of crashes. It takes abuse quite well, typically in a really bad crash just cracking the motor mount which takes about 2 minutes to replace.

 (Update) I also got telemetry on the Taranis via the TauLinkModule S.Port