Tekton one three


(Toby Harris) #1

Concept

TEKTŌN is an on-going collaboration between Mike Faulkner and Paul Mumford. A series of virtual machines are driven parametrically to make light patterns in space.

TEKTŌN ONE THREE’s mechanism is two horizontal bars of light points that animate up and down. At time of writing, this mechanism is modelled and animated in Touch Designer by Paul, realised on-screen in an audio-visual recording Paul made, and realised in physical space… with what you see here, by me, Toby Harris.

This physical machine was developed to be a centrepiece of the D-Fuse gallery show “Pattern & Noise” at Wood St. Galleries, Pittsburgh, USA. This meant we – D-Fuse, our artist collective – had a budget to work from, but also a hard deadline to ship to.

Tekton One Three - Touch Designer Simulation from Labmeta on Vimeo.

Design

Initial discussions of models, lightweight structures and hacking filming equipment fell to the wayside as desire for the aesthetics of industrial machines aligned with the forces of filling the large gallery space and running reliably for three months. The brief that formed was for a machine that could move two LED filled bars ~1.5m long vertically for a 2m run, to paint light in space without any structure visually interfering with that image. We knew dynamic constraints of a physical machine couldn’t get us to where the screen based simulation was, but we wanted to get as close as we sanely could.

The project was well-timed to take advantage of the new APA102 individually addressable LEDs, ‘dotstar’ as sold by Adafruit. Unlike previous WS2812 ‘neopixels’, they can be driven by a Raspberry Pi, and driven fast. Using a Raspberry Pi was interesting as amongst other things having a full wifi stack and linux software for, say OSC could mean easy sync between multiple controllers and realtime control from Touch Designer.

Mechanically, research led to either buying a pre-made linear slide or assembling one ourselves from a system of parts. Assembly ourselves also implying disassembly for transport, we opted for the latter knowing that shipping was going to be a much easier problem if we could break down the machine into standard, smaller packages. There was also the benefit of no lead time, as they’re parts taken from stock rather than requiring a factory to make the slide to length.

There are countless Aluminium profile systems but only the MB Building Kit for Mechanical Engineering from Item had the range of niche accessory parts to solve whatever need I had while spec’ing up options for the machine. And they had a US operation, too. The option that won had 3-4m long floor-ceiling bars that we would buy locally rather than ship, with a 2.3m bar we would mount slide rails on, and a 1.6m bar to cantilever out with LEDs. The mechanical fittings for the belt drive we could remove for transport, giving quite an efficient pack: two tubes and a flight case or two.

Item don’t supply motors but Zapp Automation – who we almost bought LEZ slides from – had the full range of keenly priced Leadshine Easy-Servo motors in-stock which seemed a good bet, particularly in having a driver unit qualified for the motors that would isolate any controller I made from the realities of motors, mechanics, and what would happen if something went wrong there.

Screen Shot 2015-05-18 at 21.01.55

The design process was mostly scribbles and an ever-growing spreadsheet, but it did at some point need an accurate drawing as sanity check for dimensions. And so, the above.

Build

22nd May

2015-05-22 14-48-00 tbz-minime-025

The mechanics arrives. Seeing the boxes is quite a moment, but also reassuring: we can ship this to America. Life never being as simple as it could be, this was a day I had to look after my 15 month old daughter, and so childcare had to be within 100m of the makerspace, which itself was still a building site, abound with danger.

slms-tobyspark-may2015-18

It’s big! That’s quite a cantilever. Eeeeek.

I spent a long time figuring out what was best to run the LED bar up and down. Roller wheels on a solid bar came out best, as roller wheels are fast and being at the corners good for torque, and if what they’re running on is incompressible then the carriage should hold square. After asking some specific questions about the various Item linear bearing parts to their staff, got a duff answer and what came out the box had ball bearing races. This part was chosen as being aesthetically the neatest, so there’s still good in having them, but there’s more friction than I’d like, and ball races have turned out to be fragile. If there was to be a Tekton One Three mkII, this is something I’d change.

slms-tobyspark-may2015-03

A real win for reading datasheets and being picky: what a beautiful fit. These LEDs could just have been taped to a bar, but higher standards for this.

26th May

slms-tobyspark-may2015-04

Preparing the slide bar #1: preparing for the end fixings. I should have got these factory modified. This involved a bike race across all of south London’s hills to get a 20mm drill bit before the shop shut. But: machining! It starts!

slms-tobyspark-may2015-06

Preparing the slide bar #2: pressing in the slide rails. While everything looks better with *spark packing tapeof course – this was holding wood pieces to not damage the metal. Holding the end there is @stefanoromano, who was in the space and stayed to help. This was the first of many sessions with his help; grazie mille and then some.

27th May

DSC_0142

Verifying the modifications required for the carriage to fit the baton, the belt bar, and the controller. 11 holes from M3 to M6, some with recessed heads, some tapped.

slms-tobyspark-openingnight

slms-tobyspark-openingnight

Boom! Aesthetically it’s a shame the carriage plate comes with four existing holes, but at least the two under black bar will shortly have end-stop posts in them.

This work was also done on the same day as Makerspace had it’s official opening. The effect was a little like a zoo exhibit: “look at that maker making things in the back of a makerspace, how exotic”

30th May

2015-05-30 14-41-06 tbz-minime-019

PCBs came back from http://hackvana.com – highly recommended, run by somebody who cares – and it’s time to turn a PCB, a bag of parts, and a Raspberry Pi into the machine’s controller. Lurking in this task is a 0.7mm pitch FFC to solder down, and this being a new thing to me this photo is taken on the bench of ninja friend Arron Smith.

2nd June

slms-tobyspark-3jun2015-2-DSC_0286

Controller assembled and in-situ. Also see in the back: the low-density LED strips are fitted. ‘Dotstar’ 60/m LED is ordered by the meter but comes in reels of up to 5m. We needed 2x 1.5m, so went to cut in two but instead found solder joints of 0.5m strips. Desoldered and separated, wired on my own power and data lines, and pulled through the bar, into place. An action liberally aided by silicone lubricant. Lots of silicone lubricant. The gotcha here is that the rubber sheath stretches as you pull the assembly through, so woe betide you if you need to adjust afterwards or pull out again (which, this being both prototype and first assembly, you’ll end up doing a few times).

3rd June

slms-tobyspark-tekton-jun2015 - 9

Lights! Made up the 3m ribbon cable from PSU to controller, extended Adafruit’s strandtest.py. It’s alive!

…but, this is only the rear LEDs. The high-density (144/m) LED strips on the front have caused trouble. 144/m strips can only be bought in 1m lengths, already made up with connectors. Cue an afternoon spent removing all the end-caps, then the sillicone that sealed them on and is gumming up where the wires are soldered on. Then cue a night trying to solder these strips together and them test good afterwards. To remedy this took a trip back to ninja friend Arron Smith, for whom it wasn’t straightforward either. Suffice to say I went shopping the next day and bought his lovely – poisonous – solder (Multicore 3096095-M and 3096525-M)

9th June

slms-tobyspark-tekton-jun2015 - 12

Yet more wiring up the LED baton, now with high-density LEDs. Did I say this is fiddly and time consuming. It is. Very. And I balked at the price of the crimp for the connectors I chose, something I now regret for time and quality reasons.

slms-tobyspark-tekton-jun2015 - 1

To this point, everything has been to plan and as per researched data sheets. The motors do not prove that way, in any way. Mechanically mating them to the pulley end-unit proves to be fraught with issues; likewise for their control.

The particular motor chosen has a shaft that matches the diameter of the bore in the pulley, this I had checked. What I had overlooked, is that unique to this particular motor in the range, the shaft length is much less than the others. So short, that engaging into the pulley is an issue. This requires making a motor mating plate as thin as possible, and some horrible work arounds with offset grub screws, taps and holes where they shouldn’t be, and access holes where they shouldn’t be to be able to tighten the result up. Eventually it was done, but it was a bruising night.

Having got a mechanical fit of motor to frame and pulley, then onto control. A quick implementation in the python script running the controller, and the vertical position is being read from the text file and translated into stepper pulses to sync the mechanical position. Code

So… it’s alive! It works…

slms-tobyspark-jun2015-10-DSC_0499

…but actually it doesn’t. The control isn’t giving smooth operation. It hurts just thinking about the jittery movement and how the cantilevered LED bar amplifies that.

11th June

slms-tobyspark-jun2015-5-DSC_0523

Time to break out the logic probe to examine the control signals – thanks again to Arron. The result is not good. Stripping the control script down to create a constant turn, the pulses would often be regular as expected:

Screen Shot 2015-06-11 at 21.45.24

But then scroll along in time and suddenly there’s patches like this:

Screen Shot 2015-06-11 at 21.45.56

No wonder the motor doesn’t have smooth motion, with the shuddering action making horrible noises and vibrations through the structure. Realisation one is that it’s not possible to get the timing precision to get near the pulse frequency to drive the motor at a decent speed, and realisation two is that the timing accuracy is just not there. Running this machine from a python script on a Raspberry Pi with it’s stock linux installation was an experiment, and this is where “let’s try the future” fell down. Simply, it’s not a realtime environment. However, a bare bones sketch on an Arduino, flipping a digital out between delayMicroseconds() of 5 and up did work beautifully. Somehow a middle-ground had to be reached.

Screen Shot 2015-07-01 at 22.31.22

The first stage of that was to simulate the 60fps commands of the driving animation while being a stand-alone program. A sin calculation also takes ~120µs on an Arduino, considerably longer than the minimum pulse time of around 5-10µs. The beautiful step signal above is the output of calculating sin(time) every 1/60s, and for every loop in-between, calculating a linear interpolation between the last two sin points, comparing that with the current position, and issuing a step and/or direction change if needed.

15th June

Arduino loops to the microsecond to give smooth motion weren’t the only performance-critical bit of code required. The animation sequence wasn’t playing far too slowly - nowhere near the 60fps the animations were authored for, and now we can see it moving, it was clear it needed to be that fast and smooth to play with your perception as you looked on. Updating the OLED display and addressing the LED pixels was taking too long. I knew you could drive this kind of OLED crazy-fast as I’d written my own library from scratch for the mixer hardware, and I knew there was a lower-level way of driving the LED strips. So: abandoned updating the OLED for the time being, and advanced the controller python code to construct a bytearray per strip rather than set each LED individually… and voila! Naked-eye illusion. Code. Tho’, somewhat by definition, the above video doesn’t quite capture that (tho’ watch the LEDs as the bar accelerates up…).

slms-tobyspark-jun2015-9-DSC_0504

What the above skips over is that if an Arduino is now going to sit by the stepper and drive it, the main Raspberry Pi controller now needs to control the Arduino, not the stepper drive’s pulse pins. And so, perhaps inevitably, the custom board I made to extend the Pi gets a hack to disconnect two direct control lines and rewire them to the Pi’s hardware serial pins. At least the PCB design was good, it’s just that the spec changed!

To get this working, the Arduino just interpreted the last byte received as a 0-255 value and scaled this up to the step range, around 20,000. That was pretty coarse and certainly less than the precision of the float value supplied in the animation sequence, and so while the machine was in transit I developed a protocol to pack an integer that could represent that kind of number into a series of bytes, keeping things as efficient as possible.

In python, on the controller: from a single byte, coarse and with no messaging capability –

arduinoSerial.write(chr(int(float(items[meta_vpos_index])*255)))

Which the receiving Arduino handles like so…

To three bytes each with two bit command type and six bit payload –

#Command 01 = Sequence run vpos start
vpos_steps = int(meta[2]*drive_max_steps
arduinoSerial.write(chr(0x40 + ((vpos_steps >> 12) & 0x3F)) + chr((vpos_steps >> 6) & 0x3F) + chr(vpos_steps & 0x3F))

Which the Arduino handles like so…

slms-tobyspark-jun2015-b-6-DSC_0538

Aaaand did I mention there’s two of everything? The model is actually two floor-ceiling bars each with the rails of slide bar, carriage running on them, and LED bar cantilevered from the carriage. Being way over schedule and needing to ship the model to the gallery in the US, here is the excellent David Meckin helping to wire up LED bar #2.

16th June

slms-tobyspark-jun2015-c-12-DSC_0559

Last possible ship date is tomorrow, and here is one of the new motors… not the best timing, I’d admit. This would have happened at least a week earlier, but it was hard to verify the suitability of the motor while the motor control wasn’t reliable. We seemed to be at the edge of the 3Nm’s ability: it could do that sinusoidal cycle video’d above, but that took a few iterations of settings and was slow and only travelling half the range. The 3Nm choice was made by calculation and looking at the torque curves, and reassured by having same shaft size as pulley it mates with… but this was out, an 8Nm was in.

Screen Shot 2015-06-21 at 00.01.56

And with a new motor, new mounting issues. Things are painful for a while. Here’s the motor mating plate that never happened, as after various false starts guaranteed-good bested over-engineered and we used the Item 50Nm coupling and assembly, with some machining from Gary at Zapp Automation.

slms-tobyspark-jun2015-c-3-DSC_0634

Nonetheless, the machine needs to ship to the gallery in the US, and so it’s disassembled and packs down into two 2.5m long tubes and two peli cases. Pretty compact, and standard sizes for UPS / FedEx / etc.

Thanks to Trotec for the laser cutter at makerspace: that perfectly sized and zip-tie slotted clear piece of perspex replaces a bit of wood I had to hand as I first mounted the ribbon cable. See the rear shot below for it in-situ and semi-invisible.

slms-tobyspark-jul2015-a - 3

Aside from the ongoing motor issues, the last thing to be done in London was upgrading the test Arduino used to drive the motors to two production units, complete with 12v boost circuitry to run the induction sensor used to detect the half-way point of the belt. Thanks to Artists & Engineers who made these white custom Arduinos available to us.


Proposed (confirmed!) use of Makerspace for AstroPi Autism and Tourettes friendly sessions
CNC Mill Proposal Autumn'17
Crimping Tool
(Tom Newsom) #2

Anticipation intensifies


(Toby Harris) #3

Baby intervenes


(Tom Newsom) #4

[quote=“tobyspark, post:1, topic:215”]A real win for reading datasheets and being picky: what a beautiful fit. These LEDs could just have been taped to a bar, but higher standards for this.
[/quote]


(Tom Lynch) #5

I can win this game…

Pepsi can’s fit perfectly into a Heinz Baked Bean tin, with that small air cushion effect of a new apple product.


(Toby Harris) #6

6th July

dfuse-tektononethree-tobyspark-c-31-DSC_1508

To the US! Wood St. Galleries, Pittsburgh. That massive banner was put up while we were inside, it was quite a shock coming out the building and one of us seeing it out of the corner of their eye.

7th July

patternnoise-tobyspark-a-4-DSC_1217

Reassembling the slide and carriage.

patternnoise-tobyspark-a-3-DSC_1214

Mounting that onto the 4m+ upright bars, before…

8th July

patternnoise-tobyspark-a-6-DSC_1234

…fixing each bar assembly in position.

patternnoise-tobyspark-a-5-DSC_1227

The upright bars should have been in place on my arrival, being the pre-requisite for me being able build the installation. As things were, the now partially pre-assembled units were only going up two days later, with only two days to go. This wasn’t good, but it did at least force some pleasant down-time where I got to do some things I’d wanted to do for ages, but motor issues had kept me from. So here is the OLED finally looking nice, and not impairing the machine running at 60fps. Drawing any text using python’s imaging system was way too slow, but with a little inspection of the Adafruit driver library, I saw I could swap out the image buffer and flush that to the screen, which would be near-instantaneous. So with a little pre-rendering, the OLED could now update while the controller waits for the next frame’s start time. Controller code, now at ~9th revision

Pre-render before starting the animation sequence, capturing each rendered buffer instead of sending the buffer:

def render_buffer_cache(prefix):
  for i in range(10):
    draw.rectangle((0,0,oled.width, oled.height), outline=0, fill=0)
    draw.text((0, 0), prefix + str(i),  font=font, fill=255)
    if host_upsidedown: 
      oled.image(image.rotate(180))
    else:
      oled.image(image)
    buffer_cache[i] = list(oled._buffer)

For each frame of animation, point the buffer at the next pre-rendered one and send the contents to the display:

oled._buffer = buffer_cache[frame_index % 10]  
oled.display() 

patternnoise-tobyspark-a-9-DSC_1242

Finally, two uprights and the two slide assemblies installed. I was about to spend a lot of time up that ladder.

10th July

dfuse-tektononethree-tobyspark-c-33-DSC_1672

PSUs installed. Belt installed. Motor and driver installed. Ribbon hung and wired. Controller electronics installed. No explosions.

Peow! The carriage from the front. I invested a lot in the overall look of this, and, well, I’m proud of how sharp this looks.

dfuse-tektononethree-tobyspark-c-37-DSC_1722

Peow! The carriage from the back, complete with maker’s mark. Without the orange cast of the work-light above, here you can really see how sharp the colours are too: rainbow ribbon against cold metal and contrast black of the drivetrain parts.

dfuse-tektononethree-tobyspark-c-23-DSC_1368

With hours rather than days to go, finally get to sit in front of the machine and start to think of it as art. Although this is not finessing: the motors are still not behaving, not all eight LED strips are working, I’m still working with test sequences. Soldering iron and ssh connections in hand, time to pull the rabbit out of the hat.


(Toby Harris) #7

Driving motors

In the down-time while shipping and the gallery install, I’d spent time working through options to drive the motor better. The machine would run smoothly – purr, even – if I drove it with something known-smooth like a sine wave, but whenever I switched from this a problem appeared. The intention had always been to add some kind of inertial smoothing to the control, so that whatever input the controller took would translate into something that was mechanically viable for the machine. Accelerating the LED bar puts torque on the linear bearing, and the weight of the whole assembly will simply stall a stepper motor pulsed from still to a constant turn. The answer should be to employ a physical model in the code, which with a little research the accelstepper library effectively does. You can command a step position, and it will take the motor there within a defined envelope of speed and acceleration. Every stepper project should use this… except it turns out the maths is too computationally expensive to achieve anything close to stepping fast enough for this project on an 8Mhz Arduino.

Problem was, any other strategy I tried in my own stepper driving code proved insufficient – I had high hopes for a simple median filter. This may have been partly due to the other side of driving the stepper smoothly: the microsecond consistency of pulse timings (as discovered originally, trying to drive direct from the Pi). I suspect the serial read of commanded step position from the Pi was interfering with this and/or disrupting the exact regularity between received commands that the linear interpolation relied upon.

In hindsight, two things stand out. One is that the Raspberry Pi is incredibly fast and powerful compared to the Arduino, so running such a library fast enough would present no issue, if it ran a realtime operating system akin to the Arduino. This required more Linux-fu than I had headspace for at the time, but is surely the correct way to go. The other is a little more embarrassing, in that research since has shown the stepper driver – the hardware box that takes the logic level step pulses and makes it so – has an optional smoothing filter that is configurable by software. I never got to this, as mentally the driver was sold as not needing software configuration, the very point being it was pre-tuned for this series of motors, and physically, the interface cable was bespoke and I didn’t buy it. Just perhaps breaking that mental mould to make up that cable could have saved me nights worth of grief.

To cut back to sitting in front of the machine with the opening in hours not days, anything that wasn’t that sinusoidal self-driving Arduino sketch was causing horrible, horrible sounds (something like a resonant frequency in the half-coupling and splined shaft that slotted into the pulley), and so running sinusoidal was what it was going to be.

And to underline just how crazy the whole motor episode was, simply adding a debug line to send the current position of the motor to the serial console when the belt mid-point marker passes the sensor caused mechanical vibrations that made you want to turn the whole thing off. That’s how sensitive the pulse timings are. Throughout, it has been the worst case of observing changing the observed.


(Toby Harris) #8

v15 - webserver. glitches operation (timing), including screen flipping at some point
power down ability - enable line from pi to arduino
power down ability - multiprocess to have run and webserver
power down ability - arduino only does a sin cycle if enable == true

[counterweight]


(Tom Newsom) #9

My suspicion is that the maths is trivial, but the implementation is lazy. Embedded computers have been doing this sort of thing in real time practically since their invention.


(Toby Harris) #10

Don’t think so, tho’ it depends what you mean by lazy. I looked at the implementation and the maths, and it’s a sensible Arduino-esque implementation of optimised maths: it’s calculating surprisingly little for what it’s doing. It’s just the timing window of <100µs is so short, and you have to do all your other business in the gaps.

It is lazy in not doing its pulse timing through interrupts, and not being in assembler, but that’s truly another level. Arduino is c, that’s already pretty damn low-level.

It’s probably me and perhaps a few roboticists who need to go unusually fast, so this on a stock Arduino is good for 99.99% of users. There are reports of running this library on 80Hz Arduino compatible boards and getting the 4k pulses a second, which is 250µs and probably fast enough.


(Toby Harris) #11

Also, do I win a prize for this?


(Tom Newsom) #12

You win a prize, and that is a comically low cutoff
-> admin panel
I’ve added a zero on the end :smile: