Last summer, I worked at MannLab: a University of Toronto lab in wearable technology, signal processing and media headed by Prof. Steve Mann. Since Prof. Mann was a very early and arguably pioneering proponent of the maker movement, the lab and lab work was almost entirely done with a solder iron in hand. This environment attracted some exceptional builders to work full-time at the lab, pumping out prototypes and spanning really all manners of hand manufacturing.
I'll single out one, who brought one of his personal projects into the lab one day: his 2-foot animatronic tail:
I hope he updates this video soon, because I had the privilege to see it as it is now, controlled by EMG (still blows my mind writing this). Each link hand-machined, its lightness and efficiency allowed it all to be powered by salvaged VCR motors. Nuts. I vastly underplayed my awe when he showed it off to keep my composure.
Having seen a variety of hyper-redundant manipulators snaking their way through the halls of high-end robotics labs, I had always assumed naturalistic tail motion was the domain of the well-endowed and well-staffed. This tail, built largely from salvaged parts, flew clearly in the face of that idea, and made me take this ambition a little more seriously.
In mid-December I was pinged by a close friend from high school that a group of them were planning to go to Tomorrowland 2020. I really enjoy electronic music, so it didn't take any more convincing than that, not to mention the fact that one of the group members was drawn in a lottery and secured a ticket for the four of us. A crazy stroke of luck really, given tickets sell out conventionally in a few seconds. We quickly settled place, flights and travel, and were just left with the wait.
Along with a ticket to have sound thrown onto your face, attending a festival was also ticket to wear whatever (law-abiding) thing I wanted. I went to my first few dressed in a solid-color t-shirt and jeans, which is fine, but ironically, where I've never dressed to stand out, I sure was in that crowd by virtue of being so aggressively bland. I wasn't planning to spend the wait idle anyways, with time like never before from a fresh 9-5 working life after graduating last May. I thought I'd make my first bit of clothing, and let the fur in me make its first decision about an engineering project.
It didn't take long to decide on a 3DOF arm. One [excellent] robot modelling and control course in university made up all the robotic formalism I had at the start of the project, one with a bevy of 3DOF arms that needed solving. In particular, simple and precise inverse kinematics was a design priority for me, since position precision was one of the aspects lacking in other designs I had seen once I began to do my research. Additionally, I knew that my limited build abilities and control knowledge would limit me to a rigid-body design, so an arm with rigid links fit the bill.
I also had to make a decision about the orientation of the joints. Specifically, I was deciding between the two orientations shown below:
- The left, as it is today, allowed much greater up-down range and more interesting configurations, since none of the joint pairs were locked into a common plane.
- The right was conducive to naturalistic wagging at the base of the tail, and has better vertical articulation due to the redundancy of the last two joints. Additionally, the first joint wouldn't as much load since the elevation angle would be offset from vertical (I expected by about 20°, removing 7% of the max load); not to mention that the worst-case load for the first joint at 90° would become an edge condition. However, fixing the elevation angle of that first link felt very limiting: indeed the feasible region of this configuration is understandably much smaller, especially since I would limit the second joint excursion so it didn't hit my legs (or worse).
I opted for the left design as I wanted as much range to start with as possible to get a feel for the tail's motion, and aside from tweaking the length of links (where originally the last link was 4cm longer and the first was 4cm shorter), the design has persisted through the process.
Sizing and motor selection
I threw together a paper prototype (bottom) to get an idea for sizing, and arrived at 2-2.5 feet based on my body height, with the tail just grazing the ground. I eyed some hollow aluminum tubing from Home Depot, which at <1" diameter typically weighed in at around
3g/cm, or 200g for the whole length. The skin seemed to be the heaviest component, as based on shipping weight, faux fur clocked in at around 0.1g/cm2, and the pelt I was making was 2750cm2, so ~240g for the fur alone.
With a 2-foot tail, the COM was around 30cm from the first joint and progressively shortened for the later two. The second joint would rarely see full load and the third would have so much less load than the others thanks to the quadratic MOI along the tail length, so I expected these two to never see loads more than this dead weight at 1ft: i.e. <15kgf·cm. Servos in this torque range are quite plentiful, small, and cheap, mostly for steering rock crawlers. Typically standard servos with metal gears, they tended to weigh between 50 and 80g, bringing my total driven weight to around 600g.
Allowing for the stuffing weight and extra margin, I anticipated the worst-case static load of the first joint to sit around 800g at 30cm, or 24kgf·cm. Unlike the second joint, this first joint would see its max load routinely: whenever the tail needed to be lifted above horizontal. Ideally, I'd have at least double the driving capacity, so I aimed for actuators at 50kgf·cm. On the plus side, I was not as constrained for size since this joint would be affixed to the base board. I did restrict this joint to free-spinning gearboxes so the first joint could be adjusted unpowered, which ruled out worm gear drives.
At first, I didn't expect this joint to need to be very fast — at the time I expected most of the interesting motion to be confined to wagging and flicking on the second and third joints respectively, with the first joint acting more like an adjustable hinge. Actuators in this torque range are much rarer for hobby markets too, where the only pre-built systems I could find that weren't going double my total cost (e.g. industrial brushless servos) were geared stepper motors, so I opted for a 51:1 geared stepper rated for 120kg·cm stall torque at 24V, and expected roughly 40kgf·cm at 8V.
I began looking at springs for the first joint, since gravity would always pull the motor in one direction when the wearer is standing. A torsion spring lifts the load curve vs. angle by a linear slope, plus offsets in springs with more than 180° range. As a result, the average load of the motor is reduced, as seen in the plots below.
There are two critical points on the load curve that factored into the spring selection: the resting position (zero intercept), and the worst-case torque. However, upon further thought, the multiple zero-intercepts that came with springs with more than 180° range was not good: the upper resting point was unstable, and if pushed above that, the tail would be stuck upright when unpowered. Not great. So I restricted the search to 180° springs.
McMaster-Carr's torsion spring collection is quite impressive, with many choices around 15kgf·cm. The plot above shows the 21lb·in spring (18.251kgf·cm) I ended up choosing (with its different degree ranges), shaving 35% off the max load.
After this first round of selection, I bought the necessary mechanical interconnects — flanges, servo horns, etc. — and waited for the parts to ship to my house.
Electrical and compute: initial steps
Along with the first shipment of mechanical parts, I bought basic electronic component kits off Amazon: protoboards, resistors, caps, a soldering iron and so on. I decided to use STM32 as my MCU platform from good experiences I had especially with its debugger and peripherals at MannLab. I opted for a chip with plenty of IO to start with: the STM32 BlackPill by RobotDyn.
Aside from motion, the plan for the electronics was to command the lighting and control. Initially, I designed with a microphone mode in mind for lighting and motion as well, so I ordered a MEMS I2S mic from Adafruit. I expected the bulk of computation to be centered on mic processing alongside inverse kinematics actually. In fact, my very first bit of work in software for the project was to make a simple integer-math kick detector based on a piecewise chirplet wavelet (seen in blue):
Also seen are its response to a trance kick (from Mitchell Claxton's Wuxia) on the right, compared to a fixed-frequency Morlet wavelet (orange)
(Unfortunately, I wouldn't end up being able to hack SPI to do my bidding with the I2S output for reasons that still escape me, so reacting to audio continues to be on hiatus.)
Most of the initial electronic planning was dedicated to the wiring on protoboard, which was somewhat nightmarish thanks to the 28-pin AS1130 LED driver that I already chose to reduce the bus sizes. I had also made the mistake of buying two-sided protoboard, where everything now needed more margin to avoid twice the opportunity for bridging. Initial layout was done slightly unconventionally on Inkscape (and I'm still unsure of a better way to plan protoboard; maybe it's already a misstep to put "plan" and "protoboard" in the same sentence...)
The board to the far left was the original plan for the power circuit, with two low-voltage supervisors for two separate power supplies (3.7 for lights and 7.4 for motors), as well as voltage converters for communication with the servo monitor circuits shown on the far right. The middle board contained all the logic, with the STM32 MCU, AS1130 and mic, with the array of 12 screw terminals on the bottom for the lights.
I ordered 20mA through-hole dome-topped LEDs with a 55° viewing angle — on the narrow side to counteract the expected diffusion from the fur. I tacked the LED driver, low-volt supervisor and mic on the first DigiKey order, and placed a separate order for 2S and 1S LiPo batteries on Hobbyking.
Attempt #1 at mechanical: getting to know the motors
The Hitec servo and geared stepper arrived first, and I immediately set to work testing the speed-torque behaviors of each.
With the stepper, it didn't take very long for me to really appreciate just how slow a 51:1 step-down was, and how badly the kV and coil resistance of the stepper were working together to choke the power output. The step count and intense magnetic coupling pushed the back EMF to rails at the mildest of output speeds and destroyed all the torque, leaving me with raising the rails voltage as my only option — a hassle of stepping down for the servos that I didn't want to deal with. Furthermore, it was dangerous to put such an efficient voltage generator in the circuit — when the tail would be moved around unpowered, it would easily generate ten[s] of volts on the stepper driver outputs.
Unfortunately, the input gear from the stepper seemed to be heat-fitted and impossible to pry off the shaft. Not one to test that theory without even a vice clamp or workbench at my disposal, I turned my attention to destroying the stepper and cutting the shaft out to mate with a BLDC using a shaft coupler. My stubbornness to avoid buying a vice grip just to extract this one gear ledd to some... creative setup to make this happen:
Only a single ball bearing protected the gearbox from metal dust, so in many senses this was a terrible idea that I got lucky with. To my credit, I knew the magnetic stator would wick up the magnetic dust, but I sure wouldn't do this again.
I apologize for any pain this image causes.
With the gear extracted and everything thoroughly cleaned of metal dust, I started looking for suitable motors to replace my stepper. The performance point of 1/12 sensored motors that were plentiful in the market seemed adequate, rated at about ~100W, which would give me about 180rpm at 50kgf·cm load.
I was disappointed by the lack of detail from sensored ESC specifications, though I understood their emphasis on features like drag braking and controller mapping rather than my primary concern in stall behaviors. Still, I decided to take a risk to keep the momentum of the project going and ordered a sensored ESC alongside my sensored 1/12 motor from Hobbywing, and waited for them to arrive.
In the intervening week, things started out poorly for the servo as well. To test the torque-speed curve, I did the first thing that came to mind and clamped it to a table, sent a sinusoid in, and fixed the output to a 1ft aluminum bar. This turned out to be a terrible idea to go through without enough foresight: I didn't center it or measure the endstop ranges beforehand either, so with the massive inertia of the load and my impatience to immediately turn the nominal torque to rated, the momentum of the bar started to take it farther and farther past the peaks until it slammed against the endstops. Despite my panicked button mashing to stop the test, the damage was done and 5 of the teeth on the second-last gear in the gearbox had shorn off.
I also apologize for any pain this image causes.
For the $20 worth of replacement gears, I got a lesson using these servos to drive high-inertia loads. While this was a product of recklessness in this test setup, I gained an appreciation for the sensitivity of hitting end-stops and fast direction changes, so as the paranoia set in I began the thinking for the control loop on the master MCU as it is today. I decided not to test the limits in isolation, and instead decided to start with conservative control and slowly increase its aggression, and waited for the replacement gears to arrive.
Starting with the skin
After measuring the dimensions of the paper prototype described earlier, I made a table of radii and section lengths, which gave me a collection of circular sections to cut out of my fur stock as a set of conic sections. I took a trip down a conveniently-located textiles shop in North San Jose, where I found a stock of dark brown fur for a mere $15/yd, though no appropriate salt-and-pepper stock unfortunately. For that, I hopped online and found some stock on Amazon from Shannon Fabrics.
Being the first time working with fur, I made the obvious mistakes. Of course, I paid no mind to the grain of the fur and instead tried to pack the nets as close together as I could. The outcome was of course that the grain was mismatched and awful:
I recut all the nets again over another afternoon — luckily these were the last pieces that I would need to make. I came to the realizization that I was lucky none of my nets were very curved in 2D, because the grain would change within the net and probably look terrible once bent into 3D.
Then came the lighting. The first tests with lights embedded in the fur showed that the diffusion was fantastic in the gray fur and decent in the brown fur.
Originally, I wanted to solder wires to all the LEDs in advance, then string the wires through the fur and solder them together at the end, so I could get the repetitive soldering out of the way more easily and avoid burning the fur. Unfortunately, the fur stitching was too fine to pass the wire through. I was stuck with soldering the LEDs in the fur itself. Not surprisingly, this ended up the most error-prone and time-consuming tasks of the entire project. For reference, below is the charlieplexed array schematic for the AS1130 driver, with one row highlighted and the two pins that are take turns being common on each row. In retrospect, I should've ignored this grid and soldered a whole row of common cathodes for each pin and remapped it in software later — in the end, this ended up being necessary anyways thanks to the mistakes I made trying to respect the layout.
Each junction was wrapped in heat shrink to avoid shorts from the movement of the skin. This was an additional pain point, since all of the junctions had at least three wires, leaving much of the heat shrink wrap loose and wires poorly covered.
To add to the mountain of problems, the joints between the wire and LEDs were exceptionally weak because the LED pin was so much stiffer than the wire I was using. As I soldered more segments, the oldest joints started to break, which was most pronounced only after I had finished most of the soldering work. Exasperated, I decided to just add redundancy and double up the wires coming from the input port, then sew everything down in the hopes that when it was all packed in, the wires would move less.
After about 30 hours of work, the LED array was finally complete for each of the three sections of skin. I waited with baited breath to finish the mechanical stuff so I could put the skin together and hopefully freeze the wires in place.
Attempt #2 at mechanical: now with mounting brackets
My initial inclincation was to support the off-axis load of the servos with the servo body alone braced against the aluminum bars. However, it quickly became apparent that in fact the servo tabs weren't going to take it, as the screws gripping the tab were constantly slipping out. I needed a bracket to mate the BLDC with the stepper gearbox anyways (left), so I decided to model a couple of servo holders as well (center and right).
Following a friend's recommendation, I ordered from Cape_Olive on Treatstock, and requested PETG for its improved stiffness, especially for the motor mount which I needed to resist vibration. A few days later, the parts arrived and the fit was good, so I got to work assembling the arm. The ball bearing seen in the image didn't actually end up being needed, as the stiffness of the motor axle with and shaft coupler sufficed to keep the gear centered.
With all the motorized parts connected, I could finally move onto the endless fun of designing the...
As I mentioned earlier, I rely on the control algorithm to protect the servo gearboxes from the momentum of the loads and possibly from their own motors — in particular, I wanted to enforce a torque and speed safe operating area based on the motor specs to bound all of the trajectories. I wanted the wearer to control the tail tip directly, which meant that the current pose would usually be far away from the target and the arm would be spending most of its time interpolating between the current and target poses. As a result, I needed to bound the torque and speed of the interpolation spline that the arm would be travelling along.
The spline changed over time: from a cubic in
t, to a piecewise smoothstep, to a quadratic B-spline. One common property of the splines is that they are all completely parameterized by the endpoint velocities and positions, as well as the total travel time. The velocities and positions were constrained to be continuous, so the torque and speed constraints would be enforced by selection of an appropriate travel time.
The splines are detailed more thoroughly in the control post.
Initially, I attempted a closed-form solution optimal travel time based on the worst-case speed and torques from each spline, which I thought would be straightforward enough since the splines scaled isotropically with time, with just one kink. I needed the excursion to be bounded, to avoid any nasty end-stop behaviors.
It turns out this complication made a closed-form solution very difficult, since the sensitivity to timing changed suddenly at the points where the spline hit a bound, and I would need to switch solvers every time the solution fell out of range of one mode. For example, with the current quadratic B-spline, the curve hits two boundaries when the time stretches long enough for each endpoint velocity crosses a fixed excursion limit (seen as Δ0 above):
Things were further complicated by the fact that the safe operating area is an area at all: e.g. inequalities with ellipses and diamonds, whose distinct quadrants make closed-form intersections very inconvenient. Meanwhile the worst-case speed and torque of the spline was not necessarily symmetric about 0 due to the various combinations of polarities of the endpoint velocities and position differences.
To summarize the details from the control post, the current design uses a very crude Newton's method — as it turns out, despite the discontinuities at the excursion bounds, the behavior is usually monotonic and smooth, so the solver usually finds the solution if there is one.
Abandoning the exact solution was not a quick realization, since I was unsure if I could afford a numerical solver on the MCU. I also expected the problem to be "pretty simple", with just a bunch of quadratics and absolute values to be solved. Instead, I spent almost two months debugging an exact solver for a smoothstep-based spline before giving up.
Sure, hindsight is 20/20. But it really seems like it should have been obvious that inverse kinematics weren't necessary at all for this design, given how coarse the acceptable precision was: it would have been acceptable to calculate a grid of forward kinematics spanning all three of the motor ranges, then snap control inputs to the nearest configuration. Nevertheless, it seemed at the outset to be straightforward enough to solve the inverse kinematics exactly with only three joints.
I think I was slightly misled in school, having been
fed nice arms to chew on given easy arm configurations to solve. My own attempts at the geometry of this arm weren't very good. You'll see why, once I actually finish writing this section.
Attempt #3 at mechanical: joint 0
With a control algorithm flashed to the MCU, I began to run my tests on interpolating between poses. At the time, the gearbox+BLDC combo in the first joint was driven by a sensored ESC, so I broke out the hall sensor outputs to a separate board to measure posibion and wrapped the speed control of the ESC in a position controller. However, I started to notice that the first joint was vibrating at high loads — vibrating a lot actually. I started testing the first joint in isolation, lifting the whole tail at full length, and the vibrations got worse with more load. I started to suspect that this degree of load revealed some behavior that I hadn't seen when testing the motor outside of the gearbox: that the ESC didn't hold position above certain loads. This made sense, since these motors never needed to apply a static torque in an R/C car, aside from perhaps the rare instance of parking on a slope. In the video below, I count through progressively larger pulse widths (though I forget what scaling I used). You can see (and hear) the vibrations from the commutation skipping.
It was clear this wasn't going to fly. I was going to need a programmable 3-phase driver.
I was in fact introduced to the STM32 ecosystem for the first time through the Discovery kit for their STSPIN32 3-phase controller at the Robosoccer UofT team. Leafing through their evaluation kits, I found they stocked an even more inexpensive and compact module now, the G431B-ESC. I threw one in the cart, although I was wary with the STM32G line because I had burned several hours earlier that month trying to flash an STM32G071 Nucleo board. (I've since concluded that it was mysteriously broken, perhaps by an inopportune flake of metal.)
As a backup, I threw together a three-phase driver with some of the spare FETs and FET drivers I had leftover from the first DigiKey order. After a few evenings, I powered the homebrew driver up with a basic position control loop in an STM32F031. The video below shows my reaction to the results:
Yes, it was able to hold up the whole skeleton weight of ~350g powered by a 7.4V battery, and far below its PWM limit too! When I received the G431B-ESC, I was also very happy to find that I could flash it without issue, and that my previous problems with the G071 Nucleo board were probably a fluke. Everything was looking up for this prototype.
That feeling was rather fleeting.
I couldn't find a reference for the 6-pin Hall cable that came with my first motor (a Hobbywing Quicrun 21.5T), so I discovered the pinout by measurement as it was plugged into the ESC, and found it was straightforward enough: rails at the edges, then a temperature sensor and the three Hall outputs in the middle. What could go wrong? Polarity of course, that's what.
Originally, I powered the hall sensors with the Hobbywing ESC while I was still writing the first bits of code for the G431B-ESC. When it came time to cut the Hobbywing ESC out for good, I needed to provide power for the Hall sensor myself. I had measured the polarity at the output pins of the Hobbywing ESC, then flipped the orientation thinking that I needed to account for some mirror imaging based on the orientation of the cable. Of course, instead I should have just measured the voltages broken out from the cable, and matched it to the power supply.
A few seconds after powering up, the hall sensors were rendered completely useless by the reversed polarity. In an undramatic way luckily, but at midnight that evening it was all the same to me. A few foreheads to the table later, I called it an early night, and the next morning I ordered another motor, this time with slightly higher turn count, to at least give me some variation in my stock. To my slight consolation, I discovered later that the Hall effect module can be interchanged between Quicrun motors, plus with the 21.5T providing ample torque, and the waste power at stall being quadratic in stall torque, going with a higher turn count seemed to be an improvement to the design. And of course, I flipped the polarity on the hall sensor power supply. The setup hasn't caused issues since.
There was some extra fun nestled in the ESC board as well.
This was not the first time I had soldered to bare pads, and I had some idea how easy it was to tear them straight off. I was a little wary of the fact that all the IO to the main board was broken out on bare pads as can be seen in shot of the board above. Still, I had figured that if I were gentle enough with them, I could overcome. Unfortunately, the wire I ordered was stiffer than I was expecting thanks to its high-temperature sleeve, but I tried to pay little attention to that and soldered them to the board anyways.
There are only 8 raw IO ports exposed to the pads on the board: 2x USART, 3x Hall, a switch, a PWM input, and a potentiometer. I wanted to use five of them for the Hall sensors: three to trigger the hall sensor interrupt and two to plug into a timer as a quadrature encoder. Despite my best efforts to tape the wires down as quickly and gently as I could, the first few attempts were just not gentle enough, and three of the pads ripped after some gentle wiggling as I was assembling other components on the base board. Now that everything is properly secured, I'm down to 5 raw IO pads, which has forced me to disable the Hall interrupts and poll the hall sensor on a timing loop. Luckily, the ESC is barely doing anything most of the time, so this is not much of an issue, but I'm wary of the day the one of the pads will turn on me. (I suppose the right approach was to go with something like 30AWG wire and let that be the weakest link.) All in all, my BOM might incur a $20 expense yet.
Closing up the skin
As I touch upon in the build post, the skin is slightly more complicated than meets the eye, with a few internal walls made from an old t-shirt to keep the stuffing from clumping. I designed a set of fins running the length of the tail to keep the middle cylindrical sleeve centered (the one that would house the skeleton) and compartmentalize the stuffing radially. This ended up involving some more thought than I expected because of the critical diameter of that inner sleeve due to the joint bending. I'll admit the joints aren't very compact, and bending close to 90° require a sleeve diameter close to the maximum width of the joint to avoid tearing. After doing that paper thinking, then fabric cutting, I stitched the inner walls together and readied the pieces for stiching to the outer skin.
Although I'd love to be proven wrong, I believe that the wiring for the LEDs makes it impossible to sew the inner layers to the outer layer with a sewing machine. After a few hours of hand sewing then, the skin was fully stitched up, and going back was now going to be a big pain! That wire better not break™.