Freesteel Blog » 2011 » December
Over three day-long digging sessions in Ogof Dydd Byraf in November and December 2011 I failed to film anyone filling the rubble sacks with sand.
I did make tea and sandwiches (when they weren’t ruined by James spilling them all over the mud).
The big idea is to use this 3D printer makerbot as a 2D plotter (by the application of a rubber band and a felt tip pen) to draw a representation of someone’s face on a post-it note.
We have the printer driver technology, we have a camera. All that is in between is software. So it should be easy, right?
Computer vision is a field that has been worked over for decades. Unfortunately it hasn’t resulted in a straightforward bitmap to vector drawing function I can call from Python. What I do have, however, are a lot of specialized CAM algorithms that I can apply to the problem. Specifically, the Pencil Milling algorithm. (That’s the one where you run a blunt object along all the surface to surface corners as if in the gutter of a kerb.)
First off, we use the FIND_EDGES filter which converts a bitmap into another bitmap with the sudden changes in brightness highlighted.
(The code which follows is as much for my reference — once I forget exactly how I did this — as much as it could be for anyone else’s benefit.)
If you want to do something interesting, like make shapes without the use of a crappy STL file or adjust the geometry in response to build feedback mechanisms (the way CADCAM has got to go, but can’t so long as machine tools continue to operate as non-interactive batch processors), then you need to get behind this prescriptive interface.
This we have done, after an enormous amount of dead time. The secret was getting the baud-rate right. (There is this USB to serial port wire too.)
I finally was able to write a program to operate the way I want — without a batch process. [the single file code for everything below is here]:
def gospiral(): go(0, 0, 0) for th in range(0, 500, 2): rth = math.radians(th*1.8) c, s = math.cos(rth), math.sin(rth) res = go(th*c, th*s, 0) go(0, 0, 0)
The go() function transmits the position vector to the machine and pauses if the buffer is getting full. (Buffer is 512 on this device.) It took ages for us to identify the code using the ReplicatorG debug mode and the documents — as though it wasn’t the most important function in the whole darn set-up.
def go(x, y, z, cycletime=5000): while getbuffsize() < 300: time.sleep(0.5) return runcommand("x81"+struct.pack("<iiii", x, y, z, cycletime))
To communicate with the device you need to install pyserial, which gets everything done.
We connect to it as easily as this:
import serial ser = serial.Serial(11, 38400, timeout=1)
The command form is byte “xd5″ followed by the number of bytes in the payload of the command, then the payload bytes themselves, and finally the cyclic redundancy check byte calculation.
def runcommand(payload): ser.write("xd5"+chr(len(payload))+payload+crc(payload)) first = ser.read() if first == '': raise Exception("No response") if first != 'xd5': raise Exception("Bad leading byte") length = ord(ser.read()) rec = "".join(ser.read() for i in range(length)) rcrc = ser.read() if rcrc != crc(rec): raise Exception("Return crc mismatch") return rec
Don’t forget that cyclic redundancy check byte:
def crc(payload): b = 0 for c in payload: b = (b ^ ord(c)) & 0xff for i in range(8): if b & 0x01: b = ((b >> 1) ^ 0x8c) else: b = b >> 1 return chr(b)
And finally here are some random other functions to help work it out:
# prints 'Cupcake' def getversion(): return runcommand("x14x1cx00") def getpos(): res = runcommand("x04") return struct.unpack("<iiis", res[1:]) # setpos(0,0,0) would set current point to home def setpos(x, y, z): return runcommand("x82"+struct.pack("<iii", x, y, z)) def abort(): return runcommand("x07") def getbuffsize(): res = runcommand("x02") return struct.unpack("<i", res[1:])
What to do now?
Well, printing plastic is quite complicated and I know I’d run out of enthusiasm short of what can be done using ReplicatorG. So I thought about drawing something.
If we can replace the plastic extruder with a felt tip pen, and the build platform with a post-it note, then all I need is for a way to convert a photo into a sequence of lines which can be drawn.
The Python Image Library has an edge detection algorithm:
import PIL.ImageFilter import PIL.ImageOps i = Image.open("PB250118.JPG") j = i.filter(PIL.ImageFilter.FIND_EDGES) j = PIL.ImageOps.invert(j) j = PIL.ImageOps.grayscale(j) #j.save("test0.bmp"); j.show()
How do I convert this into a sequence of paths? I was wondering about turning the above into a simple 3D triangulated relief surface and running the pencil milling algorithm against it.
If the pencil milling algorithm can be called from Python (or is implemented in Python), then we can have a full end-to-end process from photograph to drawing the picture in a single readable file, which means the process is hackable and can be experimentally adapted to other purposes, such as etching, artistically plotting with different colours, or cutting chocolate.
Personalize your mobile phone by scratching your picture on it. First it needs to touch-probe the shape of the surface, and then wrap the image onto it in 3D.