Skip to content

Instantly share code, notes, and snippets.

@xyzaxyz
Created April 3, 2018 20:33
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xyzaxyz/a42bde12124317a33b2d5e02911eda50 to your computer and use it in GitHub Desktop.
Save xyzaxyz/a42bde12124317a33b2d5e02911eda50 to your computer and use it in GitHub Desktop.
MP Scara 3D Printed Robotic Arm Cartesian To Scara GCode Translator Python Script
"""
@author: Tyler Williams
For use with the MPSCARA
Tested in, developed for, and funded by the Solheim Additive
Manufacturing Laboratory at the University of Washington, Seattle.
REQUIRES
settings.txt
Contains:
Machine Name:XXXX
L1 (mm, measured on printed part):XXX.XX
L2 (mm, measured on printed part):XXX.XX
Inner Radius limit (mm, usually 90 for left handed):XX
Handed (R/L):L
Quality (Interpolation length, mm):2
For quality, lower number = straighter output lines = larger file size
targets.txt
Contains:
g_code_file_1.g
g_code_file_2.gcode
...
[etc, can process any number of files, list all names here]
"""
import math
import sys
try:
s = open("settings.txt","r")
except:
sys.exit("File 'settings.txt' is missing, please read included "+
"instructions and try again")
try:
t = open("targets.txt","r")
except:
sys.exit("File 'targets.txt' is missing, please read included "+
"instructions and try again")
settings = s.readlines()
i=0
for line in settings:
settings[i] = line[line.index(":")+1:-1]
i+=1
targets = t.readlines()
m_name = str(settings[0]).strip()
quality = float(settings[5])
# Converts the linear feedrate from mm/min to deg/min, and scales
# based on location. x1, y1 are the initial coordinates, x2, y2 are the end
# coordinates, F is the linear feedrate, settings contains the settings for
# the machine.
def find_feedrate(x1,x2,y1,y2,F,settings):
a = float(settings[1]) # Length of the upper arm/vector a
b = float(settings[2]) # Length of the forearm/vector b
handed = str(settings[4]).upper().strip()
if handed == "R":
h = -1
else:
h = 1
dist = math.sqrt((x2-x1)**2+(y2-y1)**2) # Distance between the two coords.
x_dot = (x2-x1)/dist*F # Linear velocity in the x direction
y_dot = (y2-y1)/dist*F # Linear velocity in the y direction
c = math.sqrt(x1**2+y1**2) # Length of vector C (From shoulder to coord 1)
c_dot = (x1*x_dot+y1*y_dot)/math.sqrt(x1**2+y1**2) # Rate of change of the
# length of vector c
omega_c = (x1*y_dot-y1*x_dot)/c**2 # Angular rate of change of vector c
omega_a = (omega_c - h*c_dot*(2*c**2*a-c**2-a**2+b**2)/(2*c**2*a**2)*
math.sqrt(1-(c**2+a**2-b**2)/(2*c*a))) # Angular rate of change
# of the upper arm
omega_b = (omega_c + h*c_dot*(2*c**2*b-c**2-b**2+a**2)/(2*c**2*b**2)*
math.sqrt(1-(c**2+b**2-a**2)/(2*c*b))) # Angular rate of change
# of the forearm
omega = math.sqrt(omega_a**2+omega_b**2) # Sum the squares of the angular
# feedrates.
return abs(round(math.degrees(omega),5))
# Determines the required angles of each motor in order to set the end
# at the proper position. x,y are the coordinates in the cartesian system,
# settings contains the settings of the machine, and name is the name of the
# gcode file being analyzed.
def find_angles(x,y,settings,file_name):
a = float(settings[1])
b = float(settings[2])
handed = str(settings[4]).upper().strip()
c = math.sqrt(x**2+y**2)
thetaC = math.atan2(y,x)
if handed == "R":
h = -1
else:
h = 1
try:
thetaA = thetaC + h * math.acos((c**2 + a**2 - b**2)/(2*c*a))
thetaB = thetaC - h * math.acos((c**2 + b**2 - a**2)/(2*c*b))
except:
sys.exit("File '%s.g' attempts to go outside of build area,"%file_name
+ " please resize or rearrange and try again.")
return[round(math.degrees(thetaA),4),round(math.degrees(thetaB),4)]
"""
This goes through every name posted in the targets file and creates a
new file with the _MPSCARA suffix. It then populates the file with the
angular g-code, cutting the line segments into pieces [QUALITY] long
and producing additional points as needed in order to approximate lines
from large numbers of small arcs. It also calls the find_feedrate
function to properly set the angular feedrates from the given linear
feedrates, and does this at the beginning of each segment. As expected
a lower [QUALITY] value will cut the line segments into more pieces,
producing larger numbers of smaller arcs that will better approximate
straight lines, but this can lead to joltier movements, and greatly
increases .g file sizes.
"""
for file_name in targets:
inner_rad = float(settings[3])
gn = str(file_name)
if gn.endswith("\n"):
# Trims new-line characters
gn = gn[:-1]
if gn.endswith(".gcode"):
# Trims the .gcode suffix to get the file name
gn = gn[:-6]
elif gn.endswith(".g"):
# Trims the .g suffix to get the file name
gn = gn[:-2]
old_g = open(gn+".g","r")
new_g = open(gn + "_MPSCARA.g","w+")
new_g.write("; Translated for use with the MPSCARA Machine %s\n"%m_name)
new_g.write("; File Quality: %d mm\n"%quality)
x_old = None # Initializing x position variable
y_old = None # Initializing y position variable
e_old = None # Initializing extruder position variable
f = None # Initializing feedrate variable
for line in old_g:
arguments = line.upper().split()
if line.upper().startswith("G92 "):
# Catches variable resets, usually only useful for E value.
for coord in arguments:
if coord.upper().startswith("X"):
x_old = 0
elif coord.upper().startswith("Y"):
y_old = 0
elif coord.upper().startswith("Z"):
z_old = 0
elif coord.upper().startswith("E"):
e_old = 0
if line.upper().startswith("G0 ") or line.upper().startswith("G1 "):
x_new = None
y_new = None
z_new = None
e_new = None
for coord in arguments:
# Goes through the gcode line and stores important values
if coord.upper().startswith("X"):
x_new = float(coord[1:].strip())
elif coord.upper().startswith("Y"):
y_new = float(coord[1:].strip())+inner_rad
# The y offset is added in here for clarity in future
# calculations.
elif coord.upper().startswith("Z"):
z_new = float(coord[1:].strip())
elif coord.upper().startswith("E"):
e_new = float(coord[1:].strip())
elif coord.upper().startswith("F"):
f = float(coord[1:].strip())
if x_new == None and y_new == None:
code_line = arguments[0] + " "
if z_new != None:
code_line += "Z%f "%z_new
if e_new != None:
code_line += "E%f "%e_new
if f != None:
code_line += "F%f "%f
elif x_old == None and y_old == None:
[thetaA,thetaB] = find_angles(x_new,y_new,settings,gn)
code_line = "G1 X%f Y%f "%(thetaA, thetaB)
if z_new != None:
code_line += "Z%f "%z_new
if e_new != None:
code_line += "E%f "%e_new
if f != None:
code_line += "F%f "%f
else:
if x_new == None:
x_new = x_old
if y_new == None:
y_new = y_old
x_mid = x_old
y_mid = y_old
e_mid = e_old
movement = math.sqrt((x_new-x_mid)**2+(y_new-y_mid)**2)
while movement > quality:
x_mid = x_mid + (x_new - x_mid)*quality/movement
y_mid = y_mid + (y_new - y_mid)*quality/movement
if e_new != None and e_mid != None:
e_mid = round(e_mid + (e_new - e_mid)
*quality/movement,6)
[thetaA,thetaB] = find_angles(x_mid,y_mid,settings,gn)
code_line = "G1 X%f Y%f "%(thetaA, thetaB)
if e_mid != None:
code_line += "E%f "%e_mid
if f != None:
omega = find_feedrate(x_mid,x_new,y_mid,
y_new,f,settings)
code_line += "F%f "%omega
new_g.write(code_line.strip() + "\n")
movement = math.sqrt((x_new-x_mid)**2+(y_new-y_mid)**2)
[thetaA,thetaB] = find_angles(x_new,y_new,settings,gn)
code_line = "G1 X%f Y%f "%(thetaA, thetaB)
if e_new != None:
code_line += "E%f "%e_new
if f != None:
omega = find_feedrate(x_mid,x_new,y_mid,y_new,f,settings)
code_line += "F%f "%omega
if x_new != None:
x_old = x_new
if y_new != None:
y_old = y_new
if e_new != None:
e_old = e_new
new_g.write(code_line.strip() + "\n")
else:
new_g.write(line.strip() + "\n")
new_g.close()
@nguyennhattam268
Copy link

Please help me when I run Gcode_Translator.py
Error
x_dot = (x2-x1)/dist*F # Linear velocity in the direction
ZeroDivisionError : float division by zero

@krucho
Copy link

krucho commented Sep 12, 2019

Hi there! I am having some problems with the coordinates translation, I can't make the robot draw something "similar" to the original Gcode. Can I contact you? Thanks!

@Minecraftian14
Copy link

From line 37 to 40 the programs only pops out errors.
I changed it a bit and hence worked fine:

i = 0
for line in settings:
    driver = line.split(':')
    settings[i] = driver[1]
    i += 1

@broadcom9821
Copy link

Hi there! I am having some problems with the coordinates translation, I can't make the robot draw something "similar" to the original Gcode. Can I contact you? Thanks!

Hello! I am Vladimir and I experienced same problem. Do you have any suggestions how to solve it? Maybe we can make it together)

@krucho
Copy link

krucho commented Apr 7, 2020

Hi there! I am having some problems with the coordinates translation, I can't make the robot draw something "similar" to the original Gcode. Can I contact you? Thanks!

Hello! I am Vladimir and I experienced same problem. Do you have any suggestions how to solve it? Maybe we can make it together)

Hi Vladimir! I ended up with another approach, I did't get this script works. It was quite a mess but I used this https://hackaday.io/project/164066/files In these files you will find a Linux executable, I ran it on the Ubuntu app for Windows without problems, the output is converted to SCARA and works fine, the origin of the coordinates is a mystery although was the only way I make the robot works. Hope it helps you.

@broadcom9821
Copy link

broadcom9821 commented Apr 7, 2020

Thank you very much and can I get your contacts please) (maybe email)
Because I think that I will have some more troubles with this software(

@broadcom9821
Copy link

Hi there! I am having some problems with the coordinates translation, I can't make the robot draw something "similar" to the original Gcode. Can I contact you? Thanks!

Hello! I am Vladimir and I experienced same problem. Do you have any suggestions how to solve it? Maybe we can make it together)

Hi Vladimir! I ended up with another approach, I did't get this script works. It was quite a mess but I used this https://hackaday.io/project/164066/files In these files you will find a Linux executable, I ran it on the Ubuntu app for Windows without problems, the output is converted to SCARA and works fine, the origin of the coordinates is a mystery although was the only way I make the robot works. Hope it helps you.

Hello krucho, can you please help me with the settings for this gcode2Scara converter that you sent me?
If you can, please share your settings with me, please

@krucho
Copy link

krucho commented Apr 8, 2020 via email

@broadcom9821
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment