## Freesteel Blog » 2017 » April

Wednesday, April 19th, 2017 at 11:41 pm - Flightlogger

I’m not going to learn too much from myself, so I’ve got the raw data from Tim’s flight as measured by his Kobo/bluefly IGC logger.

As you can see, he ranged a good deal further than I did from Mam Tor, reaching an altitude of 1500m, and he reported scoring glide angles of 10 to 1 on his glider much-fancier-than-mine-of-which-I-am-now-jealous.

Let’s see if we can corroborate this from his data with the power of pandas and matplotlib.

pIGCtim = fd.LoadIGC("Tim-1492470000.igc") pQ = pIGCtim.resample("1S").interpolate(method="time") # fill uneven records dstep = 30 # rolling windowed measurements in seconds vario = (pQ.alt.diff(dstep)/dstep) # m/s averaged over 30seconds groundvelocity = numpy.sqrt(pQ.x.diff(dstep)**2 + pQ.y.diff(dstep)**2)/dstep

Here is vario:

Here is ground velocity:

The glide slope (normally called the glide-angle, even though we don’t quote it as an angle) is the groundvelocity divided by the descent rate. When the descent rate is close to zero, you get stupid numbers, so we need to throw those out and only quote for the parts where there is reasonably direct gliding flight.

Friday, April 14th, 2017 at 4:32 pm - Machining

Building on the weighted servo motor (having spent the first 20 minutes of the day trying to find where I left my code) I’ve tried to code it to run on a sine wave as an oscillation.

It’s not easy, of course.

How did I get this?

From the fixed voltage experiments I computed the function from the voltage and velocity to the acceleration for motor1 as follows:

cv0, cm0, cv1, cm1, cvm, cvc = 53.202, -1036.634, 56.027, -974.375, -7.497, -1521.571 acceleration1 = (velocity1 - (min(volts1-cv0, 0)*cm0 + max(volts1-cv1, 0)*cm1))*cvm + cvc

Therefore, given the velocity, if you want a particular acceleration, you need the following voltage:

tq = velocity1 - (acceleration1 - cvc)/cvm if tq < 0: volts1 = tq/cm1 + cv1 else: volts1 = +tq/cm0 + cv0

The above was the result of trying to make the motor oscillate like a pendulum by setting the acceleration to a negative number times the displacement:

acceleration1 = -acc1fac*(position1 - position1c)

Of course, one source of the problem is knowing the velocity. I’ve bodged this by recording a couple of positions up to 0.2seconds in the past and measuring to them.

if currenttime - prevtime > 0.1: position1prev2 = position1prev position1prev = position1 prev2time = prevtime prevtime = currenttime velocity1 = (position1 - position1prev2)/(currenttime - prev2time)

Unfortunately, this produces lots of bumps, so I think I’ll have to do this properly by applying, say, an exponential filter to the position value which delays the position, and use the difference between that and the current position as a measure of velocity.

Like all such runtime velocity measurements (unlike the ones done in pandas that have been calculated by picking a position on either side of the current time, like so:

(df.shift(-10).position1 - df.shift(10).position1)/(df.time.shift(-10) - df.time.shift(10))

…it’s also going to be delayed by a certain amount and be invalid under accelerations.

Thursday, April 6th, 2017 at 11:22 am - Flightlogger

On my last sorry flight I was carrying a total of four GPS units. One of them is a pile of poo. It’s the bluefly one I programmed to read at the frequency of 10 times a second, so greedy was I for precision and data.

Why do they have that option if it’s a complete waste of my time? All that work learning how to remotely step up its baudrate so that it could transmit data at this higher rate.

Here’s how the error comparisons line up, including the minus 3second correction on the 6030 vario’s timestamps. The “**u**” value is the number of milliseconds from midnight according to the GPS satellite timestamp.

gnames = [ "gps6030", "gpsxcsoar", "gpsTopShelf", "gpsBlueFly" ] print("samples, gpsA, gpsB, stddev") for i in range(len(g)): for j in range(i+1, len(g)): qA, qB = g[i], g[j] dt = 3000 if i == 0 else 0 # observed timeslip on 6030 unit dx = qA.x - numpy.interp(qA.u-dt, qB.u, qB.x) dy = qA.y - numpy.interp(qA.u-dt, qB.u, qB.y) k = (dx**2 + dy**2)[tf0:tf1] print(len(k), gnames[i], gnames[j], math.sqrt(k.mean()))

samples | gpsA | gpsB | stddev |
---|---|---|---|

400 | gps6030 | gpsxcsoar | 6.086 |

400 | gps6030 | gpsTopShelf | 7.914 |

400 | gps6030 | gpsBlueFly | 17.040 |

550 | gpsxcsoar | gpsTopShelf | 7.845 |

550 | gpsxcsoar | gpsBlueFly | 19.095 |

4120 | gpsTopShelf | gpsBlueFly | 17.451 |

Now I have to program the BlueFly to sample at 200ms like the spare top-shelf GPS does, and see if that fixes it.

Meantime, has is that the accelerometer doing with this not-crap GPS?

(more…)

Tuesday, April 4th, 2017 at 1:29 pm - Machining

Suppose we apply a random series of fixed duty cycles to a servo motor, like so:

A 50% duty cycle means that the volts are applied half one way and half the other at about a frequency of 100kHz, so it’s equivalent to zero volts.

I can plot the position from the encoder at the same time, like so:

There’s about 1000 ticks per revolution of the wheel and it’s wired backwards so a positive voltage makes it spin backwards.

Amazingly, with the control loop written in Python on a beaglebone controlling the H-bridges, it makes 4500 samples per second, which is perfectly adequate and I have not had to resort to any fancy tricks with C++ or the PRUs.

With a pandas timeseries Series object **pos**, we can calculate the velocity over a window of +-100 samples (about 1/20th of a second) like so:

# I'd like a better way to shift and difference a timeseries index. vel = (pos.shift(-100) - pos.shift(100)) / \ (pos.index.to_series().shift(-100) - pos.index.to_series().shift(100))

And then there is the acceleration:

acc = (vel.shift(-100) - vel.shift(100)) / \ (vel.index.to_series().shift(-100) - vel.index.to_series().shift(100))

We can plot them all up like so:

Here’s what the first two seconds look like.

(more…)