Week 10 & 11 - Mechanical & Machine Design
Class Notes: mechanical, machine
Assignment
(group assignment)
- make a machine, including the end effector
- build the passive parts and operate it manually
- automate your machine
- document the group project and your individual contribution
This was a 2 week assignment to build a machine in a group. I was in Team-1 along with:
We built a 2-axis multi-fab machine (plotter + lathe).
Highlights
- Started building a lathe, moved to building a 2-axis machine with changeable tool-heads (plotter + lathe-tool-bit in the first iteration)
- Salvaged inkjet printer parts from local scrapyard
- Some parts are 3D printed
- Custom-built acrylic stages
- RAMPS controller running a g-code interpreter available over serial interface
Our machine-building journey is described in detail here: Team 1: 2-Axis Multi-Fab Machine.
My Contribution
I was the “software guy” on the team and built the control system for the machine.
We did not have Gestalt or Fabnet boards in the lab, so we had to come up with an alternative.
Sibu had a Arduino Mega + RAMPS board (RepRap Arduino Mega Pololu Shield) + A4988 stepper driver, which he generously loaned for the project.
RAMPS + Motor Test
Next step was to check if RAMPS was working fine and could move a single stepper motor.
We hooked up the boards and motor and used RAMPS test code to test it (I used Arduino IDE to compile and upload the code).
It was good to see that working!
Research
I then started looking around for approaches to drive the machine and provide a convenient interface.
The overall approach I settled on was to have a g-code interpreter on the board and talk to it through its serial interface.
The host machine would talk to the board using some serial-capable software like screen / pyserial / etc.
The G-code itself would be written to a file or piped to a command, which would interact with the board.
Some of the options considered were:
This is a very convenient way to provide a UI, with composable steps that can be used to load an image, transform it, generate a toolpath, generate g-code and send it to the machine over serial.
This is a wonderful wall-hanging mural-drawing robot, which was inspired by Hektor, the spray-painting robot.
Makelangelo provides a nice UI written in Java, which can be used to load images, transform them and print them on the robot over a serial connection.
The author of Makelangelo has open-sourced all the code and firmware. I also found an article written by the author on How to build an 2-axis Arduino CNC Gcode Interpreter. This was exactly what we needed!
G-code interpreter
We took the g-code interpreter from the article (github repo) and noticed that it also had support for RAMPS.
It supported line (G00, G01) and arc (G02, G03) drawing G-code commands.
The full source is included here:
Here is a code-snippet showing the main g-code interpreter function:
/**
* Read the input buffer and find any recognized commands. One G or M command per line.
*/
void processCommand() {
int cmd = parsenumber('G',-1);
switch(cmd) {
case 0:
case 1: { // line
feedrate(parsenumber('F',fr));
line( parsenumber('X',(mode_abs?px:0)) + (mode_abs?0:px),
parsenumber('Y',(mode_abs?py:0)) + (mode_abs?0:py) );
break;
}
case 2:
case 3: { // arc
feedrate(parsenumber('F',fr));
arc(parsenumber('I',(mode_abs?px:0)) + (mode_abs?0:px),
parsenumber('J',(mode_abs?py:0)) + (mode_abs?0:py),
parsenumber('X',(mode_abs?px:0)) + (mode_abs?0:px),
parsenumber('Y',(mode_abs?py:0)) + (mode_abs?0:py),
(cmd==2) ? -1 : 1);
break;
}
case 4: pause(parsenumber('P',0)*1000); break; // dwell
case 90: mode_abs=1; break; // absolute mode
case 91: mode_abs=0; break; // relative mode
case 92: // set logical position
position( parsenumber('X',0),
parsenumber('Y',0) );
break;
default: break;
}
cmd = parsenumber('M',-1);
switch(cmd) {
case 18: // disable motors
disable();
break;
case 100: help(); break;
case 114: where(); break;
default: break;
}
}
Here is the RAMPS module:
//------------------------------------------------------------------------------
// CONSTANTS
//------------------------------------------------------------------------------
#define M1_STEP 54
#define M1_DIR 55
#define M1_ENA 38
#define M2_STEP 60
#define M2_DIR 61
#define M2_ENA 56
// limit switches
#define SWITCH1 3
#define SWITCH2 14
//------------------------------------------------------------------------------
// GLOBALS
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// METHODS
//------------------------------------------------------------------------------
void m1step(int dir) {
debug("X-step ", dir);
digitalWrite(M1_ENA,LOW);
digitalWrite(M1_DIR, dir == 1 ? HIGH : LOW);
digitalWrite(M1_STEP,HIGH);
digitalWrite(M1_STEP,LOW);
}
void m2step(int dir) {
debug("Y-step ", dir);
digitalWrite(M2_ENA,LOW);
digitalWrite(M2_DIR, dir == 1 ? HIGH : LOW);
digitalWrite(M2_STEP,HIGH);
digitalWrite(M2_STEP,LOW);
}
void disable() {
debug("!disable", 0);
digitalWrite(M1_ENA,HIGH);
digitalWrite(M2_ENA,HIGH);
}
void setup_controller() {
pinMode(M1_ENA,OUTPUT);
pinMode(M2_ENA,OUTPUT);
pinMode(M1_STEP,OUTPUT);
pinMode(M2_STEP,OUTPUT);
pinMode(M1_DIR,OUTPUT);
pinMode(M2_DIR,OUTPUT);
}
G-code Interpreter Test
Next step was to test if the firmware was working as expected and that we could talk to it over serial.
In the video below you can see the interaction being demoed:
- connect to the serial port using screen
- the gcode interpreter on RAMPS outputs an intro message and a help menu of supported G-code commands
- enter G-code command: “G00 X800”. This rotates the shaft clockwise 90 degrees (each step is 1.8 degrees and we are using 16 microsteps per step).
- enter G-code command: “G00 X0”. This rotates the shaft anti-clockwise 90 degrees.
In the video, the motor is moving very slowly because of the debugging output that I’d added for each step.
Command-line Interface Script
The next step was to write a simple way to input g-code commands. We used pyserial for this.
import serial
import fileinput
def consumeLines():
while True:
l = ser.readline().strip()
print ">>> " + l
if l == ">": # wait for the device to be ready for the next command
break
ser = serial.Serial('/dev/cu.usbmodem1411', baudrate=9600, timeout=None) # block forever on reads
ser.isOpen()
consumeLines()
for cmd in fileinput.input():
print "sending cmd: " + cmd.strip()
ser.write(cmd)
consumeLines()
ser.close()
Now we can invoke it like this:
$ python serialsend.py square.gcode
And square.gcode can contain raw gcode commands like this:
G00 X10000 G00 Y10000 G00 X0 G00 Y0
Notes / Learnings / Todos
-
The arc drawing command did not work as expected, it would draw a (mostly) straight line… need to debug this.
-
The motors were rotating slowly initially, until the feed-rate was changed from 10,000 to max (1,000,000), which resulted in a sleep of 1µs.
-
The driver was setup to run in microstep mode, with 16 microsteps/step. Removing the jumpers to turn off micro-stepping did not seem to have any effect… need to investigate this further.
-
grbl is a full-featured, production-grade g-code interpreter.
-
Measure backlash, explore linear encoders.