## Weighted servo motor story

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

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. The oscillations in the acceleration can’t all be from the jolt of the voltage change. Hopefully I can ignore them.

Now, to turn this into information we need to find the relationship between the voltage input and the velocity with the acceleration. We hope the absolute position is irrelevant if there are no sticking points in the motor on each revolution.

```# selection for voltage being constant for the brief duration around the measurement
cv = (volts.shift(-200) == volts.shift(200)) & ~numpy.isnan(acc)

# plot velocity against voltage coloured by direction of acceleration
plt.scatter(volts[cv], vel[cv], color=numpy.array(["r","b"])[(acc[cv]>0)], marker=".")
``` What this shows is that when the voltage is set below 50% and the motor is spinning slowly, it speeds up (blue dots) until it reaches the critical velocity for that voltage.

After a short interval with a fixed voltage, the motor spins at a constant speed. If you plot enough points it clearly fits 3 piecewise linear segments (the middle section being zero in the dead zone) and the function approximates to this:

```limitvel = minimum(volts-48.979, 0)*(-1267.168) + maximum(volts-51.356, 0)*(-1328.962)
```

And now if we plot the difference between this function of volts and the actual velocity against the acceleration, we get:

```plt.scatter(vel - limitvel, acc, color="r", marker=".")
cvm, cvc = scipy.stats.linregress(vel - limitvel, acc)[:2]
# -> cvm, cvc = -16.391, 269.977
``` It’s not a perfect linear fit, but it’s as good as we’re going to get. My theory is therefore that the acceleration is related to the torq, and we can set:

```torq = (vel - minimum(volts-48.979, 0)*(-1267.168) + maximum(volts-51.356, 0)*(-1328.962))*(-16.391) + 269.977
```

Here’s how the torq plots against the acceleration. The slope of the acceleration and the backspike (due to the velocity) are artifacts of the 200-wide sample window. If we narrow the region we estimate the velocity to something like “vel = (pos.shift(-40) – pos.shift(40))/…” the noise increases and the artifacts diminish, and the torq appears to fit the acceleration on a wider range. Let’s repeat the same thing, but with a metal weight on a string, like it’s shown in the video: The voltages are all above the 0 (50%) mark because there is a load pulling the string out.

Now, let’s calculate the torq and plot it against the acceleration in the regions where the voltage is constant and colour it by whether it is going up or down:

```cv = (volts.shift(-200) == volts.shift(200)) & ~numpy.isnan(acc)
plt.scatter(torq[cv], acc[cv], color=numpy.array(["r","b"])[(vel[cv]>0)], marker=".")
``` There is agreement, which puts the acceleration at (0.434*torq + 27550) ticks per second per second.

The 0.434 is probably the inertia of the metal weight, and the 27550 is the acceleration due to gravity.

Therefore to hold the weight steady with an acceleration and velocity of zero, we need a voltage of cv1-(-cqc/cqm – cvc)/cvm/cm1 = 54.282, which tallies up.

Next we put the system back in place over its pulley first to see how this messes up the calculations, with its friction and all that.

The trick is to compare the steady state velocities against to see if we can resolve the friction on the eyelet as a linear function of velocity.

The big questions are whether it all works linearly enough outside of the motor’s dead zone enough to infer and make predictions from the constants of acceleration and friction throughout the system. And, if that works, produce an underactuated polar controller that does not depend on PID.

If I actually knew it would work it wouldn’t be worth trying.