ord

Mindstorms 51515 Plotter

Recommended Posts

Hi,

I am working on a classic XY Plotter that is controlled by the new Mindstorms hub. It's a work in progress and hopefully I can show some improvements here over time.

I made it with the Robot Inventor Hub instead of the Technic Hub simply because I wanted to try programming with the bundled Mindstorms MicroPython software. I was unable to achieve some crucial things with this software (most importantly a simple way to run multiple motors in parallel?) so I switched to Pybricks but kept the Mindstorms Hub. The code was adapted from my Cartesian Parallel Robot - easy!

51330109889_1282784316_c_d.jpg

Features:

  • Lightweight XY carriage with all motors mounted off the moving parts
  • Pen raising and lowering
  • Programmed to 0.1mm accuracy
  • Programmed with backlash compensation (0.9mm in X, 0.5mm in Y)

I hope to reduce the mechanical backlash some more. For the moment though, programming it out seems to help. Also, I have implemented the method described by Didumos69 here to maintain tension in 'loops' going through the chains and remove backlash in them (I found this to be a better solution than tensioning the chains with springs). In this video the hub's display shows when backlash is compensated for in each axis:

 

Plot #1:

  • File: Gaudi's Honeycomb (SVG)
  • Paper: A5 200gsm
  • Speed: 20mm/s
  • Plot time: ~30min
  • Pen: Rotring 0.5mm graphic pen

I found a good website with some free SVG files to start with (link above). This particular file required around 4000 lines of Pybricks code to plot. I'm approaching the point where files are too big for the Inventor Hub (and its ~252KB of available RAM). Perhaps someone could tell me if there's some way to load larger files onto the hub using Pybricks or is this something that's only possible with the Mindstorms software or am I stuck with splitting larger files into multiple plots?

51329484621_6386da810f_c_d.jpg51330110164_d8cdc20ee7_c_d.jpg

I'm reasonably happy with the result. There are some obvious defects though and I would like to make it faster. Here's a short real-time video.

 

What's next?

I have a bricklink order on the way with parts to increase the plotting area to A4. I think this is the maximum size that this design can be, given the 32L axles along the Y-axis and for the pen raising/lowering. Aside from that, I will try to further reduce the backlash and increase the speed. Mostly though, I just want to plot some different things (which I will post here).

Thank you for reading. More to come soon...

Share this post


Link to post
Share on other sites

Wow, this is very impressive! I will check out the video later.

I am not sure how to load bigger files.

Share this post


Link to post
Share on other sites

Amazing! It is really cleverly designed plotter which seems to be rather precise - for Lego plastic components. I look forward to your next steps and hope you are going to show us some construction details.

Share this post


Link to post
Share on other sites

This is really stunning...
And fast.

Now up for a LEGO 3D printer that makes Lego that makes 3D printers that...
Hey wait... Von Neumann arrived in the building:laugh:

Share this post


Link to post
Share on other sites
Posted (edited)

Thanks all! @Jonas I plan to make a Studio model of it once it's finished - it's still very much a work in progress.

A small update: I found a very cool tool called SquiggleCam - it converts any image into a bunch of wavy lines for plotting. I love it. Here are two plots I've done with it so far...

Plot #2:

  • File: My desktop background (The Matterhorn), in squiggle format
  • Paper: A5 200gsm
  • Speed: 40mm/s*
  • Plot time: ~30min (divided into 2 roughly equal parts)
  • Pen: Rotring 0.5mm graphic pen

51335481517_6ea45eaa07_c_d.jpg

 

Plot #3:

  • File: A portrait of a friend of mine, in squiggle format
  • Paper: A5 200gsm
  • Speed: 40mm/s*
  • Plot time: ~35min (divided into 3 roughly equal parts)
  • Pen: Rotring 0.5mm graphic pen

51336208046_6349b9c386_c_d.jpg

51337222665_37344d7643_c_d.jpg

*The programmed speed of each linear movement, not necessarily the overall speed, as I'm still working on timing between linear movements

Edited by ord

Share this post


Link to post
Share on other sites

Very nice machine! Those last pictures are really nice. Most accurate Lego plotter I've seen so far.

Share this post


Link to post
Share on other sites

Wow! We're impressed by what you've made (again!)

Glad to hear you found the Pybricks motor features useful.

And indeed, we still have to enable file system access. Then you can make drawings up to 30 MB.

 

Quote

This particular file required around 4000 lines of Pybricks code to plot

I don't think we've ever tried this. Thanks for testing these edge cases :)

For now, maybe it's already possible to do larger images by storing the image data in a different way. How do you currently do it?

Share this post


Link to post
Share on other sites

Thank you Jos and Pybricks!

5 hours ago, Pybricks said:

Then you can make drawings up to 30 MB. 

That would be great!

As a side note, I am continually impressed by the power of Pybricks and its many advanced features. So thank you @Pybricks for the work you've done to make such useful software and the documentation to go with it :)

6 hours ago, Pybricks said:

For now, maybe it's already possible to do larger images by storing the image data in a different way. How do you currently do it?

I convert the SVG file to a sequence of move(), head_down() and head_up() commands which I paste into Pybricks. Here is the start of such a sequence:

def program(speed):
	move(581,10,speed) #Move to x = 58.1mm, y = 1.0mm at some speed
	head_down()
	move(583,16,speed)
	move(585,17,speed)
	move(587,13,speed)
	move(587,10,speed)
	head_up()
	move(594,10,speed)
	head_down()
	move(594,11,speed)
	move(596,17,speed)
	move(598,17,speed)
	...

Later, I call program() with speed as a parameter. The first two parameters of the move() function are X and Y coordinates. They are stored in 10−1mm units, which allows for 0.1mm accuracy while storing them as integers (this reduces the program size by about 75% by my measurements, compared to storing them as floats). Without an in-depth knowledge of how programs are sent to the hub though, it's hard to know how to make it more efficient. Would trimming down the move() function itself help, since it is called so many times?

Share this post


Link to post
Share on other sites

Thanks for sharing that. That is quite good already --- I was indeed curious about whether you used floats or not.

Since speed is mostly constant, maybe it's worth removing it as a function argument. You could leave it constant altogether or use a global variable. And then you might be able to drop the program() function as well, so you're just left with a sequence of move/head calls.

Scripts are compiled to MPY format and then sent to the hub. It might be possible to optimize more by storing the data differently or even compressing it, but this is probably not worth the effort.

We better get that file system going! :classic:

 

Share this post


Link to post
Share on other sites
Posted (edited)

Just some wild thoughts as I don't know much about data saving, and how you could convert it, but can't you like store the X / Y / Head position in a list?

Just add every X Y and the Head position at the end of that movement (or beginning, just switch around the order of motor movements) to a list.

speed = 700
position_list = [58110True58316True58517True58713True58710False59410True59411True59617True59817True]
 
for x in range(len(position_list) / 3):
    move(position_list[x * 3], position_list[1 + (x * 3)], position_list[2 + (x * 3)])
 
def move(x_posy_poshead_down):
    global speed
    motor_x.run_target(x_pos, speed, then=Stop.HOLD, wait=False)
    motor_y.run_target(y_pos, speed, then=Stop.HOLD, wait=True)
    if head_down == True: motor_z.run_target(500, speed, then=Stop.HOLD)
    else: motor_z.run_target(0, speed, then=Stop.HOLD)

Position 0,3,6,9,... in the list will be the X_coordinates (Integers), 1,4,7,10,... Y_coordinates (Integers), 2,5,8,11,... head positions (Booleans True/False). If needed you can do just X/Y in the list and make head in a seperate list.

This is just a basic example ofcourse, as you will need to adjust the speed of 1 of the motors to make them end at the same time, but that you'll know already how to do.

Edited by Mr Jos

Share this post


Link to post
Share on other sites
Quote

I made it with the Robot Inventor Hub instead of the Technic Hub simply because I wanted to try programming with the bundled Mindstorms MicroPython software. I was unable to achieve some crucial things with this software (most importantly a simple way to run multiple motors in parallel?) so I switched to Pybricks but kept the Mindstorms Hub.

There's also this internal Hub API you can use within the official app.

Share this post


Link to post
Share on other sites

Thanks for the suggestions. They all sound good and I look forward to trying them.

36 minutes ago, Pybricks said:

There's also this internal Hub API you can use within the official app.

Is that different to what's shown in the official app's documentation? I'm confused.

Either way, I checked out the Motor class on that page and couldn't see anything similar to run_target() with wait=False as a parameter, like what I use in Pybricks.

Share this post


Link to post
Share on other sites

Back to the mechanical side of things... I now own four of each of the tread link sprockets and need to decide which will be best for the Y-axis.

The smallest ones would make the whole MOC nice and slim but would be more prone to chordal action (variations in speed and position of the chain), so I investigated to see just how much...

51339423430_b055a21b96_o_d.png

My conclusion: the speed and hence positional accuracy of the Y-axis will vary by a maximum of 13.4% if using the smallest sprockets, cyclically every 12mm. This isn't a huge amount, but combined with a radius variation of 1.6mm (chain will move up and down by this much every 12mm) I think I'll stick with the 10-teeth sprockets, or maybe even use the 14-teeth ones for maximum smoothness/accuracy.

Share this post


Link to post
Share on other sites
Posted (edited)

I've tried the suggestions for reducing file size and here are the results (only tested with one SVG file):

I called mem_info() at the very start of the code (after imports) and at the very end of the code. Shown is the memory used at each point.

  • Original code, program(speed): start 78KB, end 188KB
  • Speed parameter set as constant, program(): start 73KB, end 80KB
  • 'Program' function removed, move() calls only: start 73KB, end 78KB  (speed constant)
  • Positions stored in list, position_list[]: start 106KB, end ?  (speed constant)

I didn't wait for the plot to finish with the positions stored in a list, as it was taking a very long time (I think due to motor_z.run_target() being called with every move) and the memory used at the start of the program had already exceeded the memory used with the other suggestions.

So I think setting speed as a constant will be the most helpful thing to reduce file size. I am now, however, running into errors such as ValueError('incompatible .mpy file') and MemoryError: memory allocation failed when simply pasting all of the moves in twice (which I thought would use <2x80KB<252KB). Maybe I'm making some other mistake here though.

Anyway, it's not a huge problem to run a few programs for one plot if I need to.

Edited by ord

Share this post


Link to post
Share on other sites

Glad to hear the speed parameter helped a lot. That might be the best for now.

Another thing to keep in mind is that both the program and all your variables share the same memory. This is especially tricky if you have one giant list, as you have found.

This will no longer be an issue once the file system is enabled. Then you can read coordinates from a file, one at a time instead of loading the whole file at once.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.