Part Finishing Library
LEonard Part Finishing Library
Life can be a grind. Why have your robot programming chores be one, too?
The LEonard Part Finishing application works in conjunction with the UR Robot Interface to provide a sanding and grinding component that works onb flat, cylindrical, or spherical surfaces using the UR's bnuilt-in force control mode.
Patterns of a variety of shapes and programmable sizes may be executed to sand, grind, or buff parts.
Line
Rectangle
Serpentine
Polygon
Circle
Spiral
LElib.UR.grind: The UR Grinding Package
The grinding commands use a set of common parameters described below:
dx_mm, dy_mm, diam_mm: dimensions of the patterns in mm
n_cycles: times to repeat the pattern (ignored if test grinding)
speed_mm/s: speed to grind at (ignored if test grinding
force_N: force in Newtons to apply
stay_in_contact: 0 to retract at end of grind, 1 to stay in contact
grind_line(dx_mm, dy_mm, n_cycles, speed_mm/s, force_N, stay_in_contact)
grind_line_deg(length_mm, angle_deg, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind in a straight line centered on the current position, defined either by endpoints or angle.
grind_rect(dx_mm, dy_mm, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind along a rectangle centered on the current position at the current RZ angle of the tool.
grind_serp(dx_mm, dy_mm, n_xsteps, n_ysteps, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind a serpentine pattern within a rectangle centered on the current position. N_xsteps and n_ysteps is the number of moves needed to span the rectangle. One or the other of these must be equal to 1.
grind_poly(circle_diam_mm, n_sides, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind along a polygon of n_sides inscribed in circle_diam_mm centered on the current position.
grind_circle(circle_diam_mm, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind along a circle centered on the current position.
grind_spiral(circle1_diam_mm, grind_circle2_diam_mm, n_spirals, n_cycles, speed_mm/s, force_N, stay_in_contact)
Grind along a variable diameter circle centered on the current position. The circle goes from the first diameter to the second in n_spirals full revolutions.
grind_retract()
Ensure not in contact with the part. Happens automatically if a non-grind command is sent, if stop or pause is selected, or if grind_max_wait timer expires.
grind_contact_enable(0=Touch OFF,Grind OFF|1=Touch ON,Grind OFF| 2=Touch ON,Grind ON)
Set the grinding mode programmatically as shown.
The commands below provide a programmatic way to set the grinding parameters.
grind_touch_retract(touch_retract_mm)
Set grind retract speed used after touchoff.
grind_touch_speed(touch_speed_mm/s)
Set speed used to go in for touchoff in Z.
grind_force_dwell(dwell_time_ms)
A dwell time performed when force mode is turned on to allow the robot to settle against the grind surface.
grind_max_wait(max_time_before_retract_ms)
If the tool is left in contact with the surface awaiting the next grind command, it will retract if this timeout is exceeded.
grind_max_blend_radius(grind_blend_radius_mm)
Sets the maximum blend radius that will be used in any pattern. This will be reduces for small geometries.
grind_trial_speed(trial_speed_mm/s)
Sets the speed used for “air grinding” when not in Touch + Grind mode.
grind_linear_accel(accel_mm/s^2)
Sets the linear acceleration used for grinding operations.
grind_point_frequency(point_frequency_hz)
Sets a point interpolation frequency used for complex figures. Obsolete.
grind_jog_speed(trial_speed_mm/s)
Sets the speed used when the grinding requires a robot move while not in contact with the part.
grind_jog_accel(accel_mm/s^2)
Sets the acceleration used for grinding m,oves not in contact with the part.
grind_force_mode_damping(damping: 0.0 – 1.0)
Sets the UR force_mode_damping parameter to assist in stabilizing force-mode performance.
grind_force_mode_gain_scaling(scaling: 0.0 – 2.0)
Sets the force_mode_gain_scaling parameter to assist in stabilizing force-mode performance.
Grind User Timers: Internal Use
Enabling these will time each grind operation and place it in a circular buffer of user_timers that can be returned to the variable list with return_user_timers(). Used primarily for internal testing.
enable_user_timers(integer 0=off, 1=on)
Turn the UR-internal user timers on or off.
zero_user_timers()
Zero all UR-internal user timers.
return_user_timers()
Return an array of timers. Each timer represents one grinding operation. Repeating the same grinding operations on different surface geometries can be used to validate Lecky Engineering’s internal speed calibration system.
LElib.UR.grind Grinding Examples
Here are a few sequences that show the kinds of things that can be done in a recipe. The Examples subdirectory in the Code folder has many more complicated examples that you can examine (and run!).
These examples are shown in LEScript and require slight edits in Java or Python sequences.
Remove Current Tool
Just remove the current tool from the robot. As long as the one actually mounted is selected, this goes to the tool home followed by the mount/demount position and prompts the operator when it is time to remove.
# Remove Current Tool
# Go through demount procedure
# Assumes you have selected whatever tool is actually mounted!
prompt(Please confirm: you wish to demount {robot_tool}?)
move_tool_home()
move_tool_mount()
prompt(Please demount tool {robot_tool})
select_tool(none)
Install A Tool
This goes through prompting to mount a specific tool.
# Install 2F85
# Example to install a tool when none is currently installed
# We just select the new tool, move to the mount position, prompt the operator, and move to tool_home
# Change to whatever tool you like
tool=2F85
# Operator confirmation
prompt(About to mount {tool})
# Mounting process
select_tool({tool}) # This only informs the robot what is mounted
# This does the physical swap
move_tool_mount()
prompt(Please mount tool {tool})
move_tool_home()
Integrated Example
Here we start with the 2F85 tool ready to grind and swap tools and continue from the same location mid-Sequence.
# Integrated Example
# Assumes we're where we want to grind initially but need to do a tool swap mid-way
tool1=2F85
tool2=vertest
# Program assumes we are starting with tool1- verify internally and with operator!
assert(robot_tool,{tool1})
prompt(Confirming tool {tool1} is currently mounted and you are grinding on {robot_geometry})
# This will always be our grind_start position
save_position(grind_start)
# Do some grinding with tool1
move_linear(grind_start)
grind_rect(30,30,3,10,10,1)
grind_rect(20,20,3,10,10,1)
prompt(Ready to swap {tool1} to {tool2}?)
# Remove {tool1}
move_tool_home()
move_tool_mount()
prompt(Please remove {tool1})
# Install {tool2}
select_tool({tool2})
move_tool_mount()
prompt(Please install {tool2})
move_tool_home()
# Do some grinding with tool2
move_linear(grind_start) # Returns us to the starting position
grind_rect(30,30,3,10,10,1)
grind_rect(20,20,3,10,10,1)
Computed Concentric Circles
Here’s a test recipe that grinds 3 concentric circles explicitly and in a loop, not lifting until the final one.
# 26 Concentric Circle Test
# Old school
grind_circle(30,2,0.9,10,1)
grind_circle(20,2,0.9,10,1)
grind_circle(10,2,0.9,10,0)
# Do it with a loop
size = 30
count = 2
speed = 0.9
force = 10
repeat:
grind_circle({size},{count},{speed},{force},1)
size -= 10
jump_gt_zero(size,repeat)
Lots of Grinds
By pre-teaching points and swapping geometries, a whole day’s work could be done (other than tool swaps!)
# Test all the patterns on all the geometries
size1=40
size2=10
count=3
speed=5
force=10
select_tool(2F85)
cycleCount=0
redo:
move_linear(demo_flat)
set_part_geometry(FLAT,0)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(CYLINDER,400.1)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(CYLINDER,600.1)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(CYLINDER,800.1)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(CYLINDER,1000.1)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(SPHERE,400.2)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(SPHERE,600.2)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(SPHERE,800.2)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
set_part_geometry(SPHERE,1000.2)
grind_line({size1},{size2},{count},{speed},{force},1)
grind_line(-{size2},{size1},{count},{speed},{force},1)
grind_rect({size1},{size2},{count},{speed},{force},1)
grind_rect({size2},{size1},{count},{speed},{force},1)
grind_serp({size1},{size1},1,3,{count},{speed},{force},1)
grind_serp({size1},{size1},3,1,{count},{speed},{force},1)
grind_circle({size1},{count},{speed},{force},1)
grind_circle({size2},{count},{speed},{force},1)
grind_spiral({size1},{size2},3,{count},{speed},{force},1)
cycleCount++
jump(redo)