2.2.21. Batch processing with PrusaSlicer

Todo

Batch processing with PrusaSlicer scripts.

Important

I am doing a massive update based on the recent name change from Slic3rPE to PrusaSlicer. Please bear with me while I update everything.

Note

The latest Alpha releases of PrusaSlicer do not support command-line processing as of this writing. Use the latest release version.

Note

These notes are based on my experiences with the Prusa i3 Mk3 and Artillery/Evnovo Sidewinder X1 printers. If you are using a different printer, please verify the hardware details are similar.

2.2.21.1. .ini scripts

Listing 2.19 PrusaSlicer printer definition .ini file
 1bed_shape = 0x0,250x0,250x210,0x210
 2bridge_acceleration = 1000
 3end_gcode = ; Last updated 20181007\nG4 ; wait\nG92 E0 ; prepare to retract\nG1 E-0.8 F2100; retract to avoid ooze\nM221 S100 ; reset extruder factor to 100%\nM900 K0 ; reset linear acceleration\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+60, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200 ; present bed\nM84 ; disable motors\nM300 S100 P10 ; chirp
 4gcode_flavor = marlin
 5extruder_clearance_height = 20
 6extruder_clearance_radius = 20
 7extruder_colour = #FFFF00
 8extruder_offset = 0x0
 9extrusion_axis = E
10machine_max_acceleration_e = 5000,5000
11machine_max_acceleration_extruding = 1250,1250
12machine_max_acceleration_retracting = 1250,1250
13machine_max_acceleration_x = 1500,960
14machine_max_acceleration_y = 1500,960
15machine_max_acceleration_z = 1000,1000
16machine_max_feedrate_e = 120,120
17machine_max_feedrate_x = 200,100
18machine_max_feedrate_y = 200,100
19machine_max_feedrate_z = 12,12
20machine_max_jerk_e = 1.5,1.5
21machine_max_jerk_x = 8,8
22machine_max_jerk_y = 8,8
23machine_max_jerk_z = 0.4,0.4
24machine_min_extruding_rate = 0,0
25machine_min_travel_rate = 0,0
26max_volumetric_speed = 11.5
27max_print_height = 210
28output_filename_format = [input_filename_base].gcode
29printer_model = MK3
30;print_center = 125,105
31remaining_times = 1
32silent_mode = 1
33start_gcode = ; Last updated 20181018\nM115 U3.3.1 ; tell printer latest fw version\n; Set initial warmup temps\nM104 S140 ; set extruder no-ooze temp\nM140 S{max(first_layer_bed_temperature[0],60)}  ; set bed PINDA warmup temp\n; Nozzle warmup routine\nM300 S40 P10 ; chirp\nG28 W ; home all without mesh bed level\nG0 X0 Y200 Z100.0 F10200 ; raise nozzle and present bed for cleaning\nM109 ; wait for extruder no-ooze warmup temp before mesh bed leveling, cool hot PINDA\n; PINDA warmup routine\nM300 S40 P10 ; chirp\nG0 X125 Y105 Z0.10 F10200; PINDA warming position\nM860 S35 ; wait for PINDA temp to stabilize\nM300 S40 P10 ; chirp\nG28 W ; home all without mesh bed level\nM140 S[first_layer_bed_temperature] ; set bed final temp\nG80 ; mesh bed leveling\n; Final warmup routine\nM104 S[first_layer_temperature] ; set extruder final temp\nM109 S[first_layer_temperature] ; wait for extruder final temp\nM190 S[first_layer_bed_temperature] ; wait for bed final temp\n; Prime line routine\nM300 S25 P10 ; chirp\nM83  ; extruder relative mode\nM900 K0; Disable LA for prime line\nG92 E0.0 ; reset extrusion distance\nG1 Y-3.0 F1000.0 ; go outside print area\nG1 E2 F1000 ; de-retract and push ooze\nG1 X20.0 E6  F1000.0 ; fat 20mm intro line @ 0.30\nG1 X60.0 E3.2  F1000.0 ; thin +40mm intro line @ 0.08\nG1 X100.0 E6  F1000.0 ; fat +40mm intro line @ 0.15\nG1 E-0.8 F2100; retract to avoid stringing\nG1 X99.0 E0 F1000.0 ; -1mm intro line @ 0.00\nG1 X110.0 E0 F1000.0 ; +10mm intro line @ 0.00\nG1 E0.5 F1500; de-retract\nG92 E0.0 ; reset extrusion distance\n; Final print adjustments\nM92 E282.54 ; adjust extrusion ratio\n;M221 S{if layer_height==0.05}100{else}95{endif}
34threads = 4
35use_relative_e_distances = 1
36use_volumetric_e = 0
37variable_layer_height = 1
Listing 2.20 PrusaSlicer nozzle definition .ini file
 1brim_width = 0
 2external_perimeter_extrusion_width = 0
 3extrusion_width = 0
 4first_layer_extrusion_width = 0
 5infill_extrusion_width = 0
 6max_layer_height = 0.32
 7min_layer_height = 0.1
 8nozzle_diameter = 0.4
 9perimeter_extrusion_width = 0
10perimeters = 3
11solid_infill_extrusion_width = 0
12support_material_extrusion_width = 0
13top_infill_extrusion_width = 0
Listing 2.21 PrusaSlicer filament definition .ini file
 1; filament characteristics
 2filament_type = PLA
 3filament_diameter = 1.715
 4extrusion_multiplier = 0.98
 5filament_max_volumetric_speed = 11.5
 6z_offset = 0
 7start_filament_gcode = "M900 K30"
 8
 9; temperatures
10bed_temperature = 55
11temperature = 200
12first_layer_bed_temperature = 60
13first_layer_temperature = 205
14
15; cooling
16max_fan_speed = 100
17min_fan_speed = 100
18disable_fan_first_layers = 1
19fan_always_on = 1
20fan_below_layer_time = 100
21bridge_fan_speed = 100
22cooling = 1
23
24; retraction
25retract_before_travel = 1
26retract_layer_change = 1
27retract_length = 0.8
28retract_lift = 0.6
29retract_lift_above = 0
30retract_lift_below = 209
31retract_restart_extra = 0
32retract_speed = 35
33deretract_speed = 25
34
35; bridges
36dont_support_bridges = 1
37bridge_flow_ratio = 1
38bridge_speed = 30
Listing 2.22 PrusaSlicer print job definition .ini file
 1notes = Normal
 2;quality settings
 3perimeters = 3
 4external_perimeters_first = 0
 5avoid_crossing_perimeters = 1
 6external_fill_pattern = rectilinear
 7extra_perimeters = 1
 8infill_every_layers = 2
 9only_retract_when_crossing_perimeters = 1
10ooze_prevention = 0
11overhangs = 1
12seam_position = aligned
13spiral_vase = 0
14variable_layer_height = 1
15xy_size_compensation = 0
16gcode_comments = 1
17
18; layer height
19layer_height = 0.2
20first_layer_height = 0.2
21bottom_solid_layers = 5
22top_solid_layers = 5
23
24; speeds
25perimeter_speed = 45
26external_perimeter_speed = 35
27small_perimeter_speed = 25
28first_layer_speed = 20
29gap_fill_speed = 40
30infill_speed = 200
31solid_infill_speed = 200
32min_print_speed = 15
33top_solid_infill_speed = 50
34travel_speed = 180
35
36; acceleration
37default_acceleration = 1000
38first_layer_acceleration = 1000
39infill_acceleration = 1250
40perimeter_acceleration = 800
41
42; print characteristics
43fill_angle = 45
44fill_density = 20%
45fill_pattern = grid
46infill_first = 0
47infill_only_where_needed = 0
48infill_overlap = 25%
49solid_infill_below_area = 0
50solid_infill_every_layers = 0
51solid_infill_extruder = 1
52solid_infill_extrusion_width = 0
53
54interface_shells = 0
55
56; skirt, brim & raft
57raft_layers = 0
58skirt_distance = 2
59skirt_height = 1
60skirts = 1
61min_skirt_length = 4
62
63; support
64support_material = 0
65support_material_angle = 0
66support_material_auto = 1
67support_material_buildplate_only = 0
68support_material_contact_distance = 0.25
69support_material_enforce_layers = 0
70support_material_extruder = 0
71support_material_extrusion_width = 0
72support_material_interface_contact_loops = 1
73support_material_interface_extruder = 0
74support_material_interface_layers = 1
75support_material_interface_spacing = 0.2
76support_material_interface_speed = 100%
77support_material_pattern = rectilinear
78support_material_spacing = 2
79support_material_speed = 50
80support_material_synchronize_layers = 0
81support_material_threshold = 45
82support_material_with_sheath = 0
83support_material_xy_spacing = 60%
84

2.2.21.2. Build scripts

Listing 2.23 PrusaSlicer build script
 1#!/bin/sh
 2# Normal mode prints
 3DIR_BASENAME="./output"
 4find "./models/normal" -iname "*.stl" | while read MODEL
 5do 
 6    echo "${MODEL}"
 7    for NOZZLE in nozzles/*.ini; do 
 8        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
 9        for FILAMENT in filaments/XT.ini; do 
10            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
11            for SETTINGS in settings/settings_020_layers_normal.ini ; do 
12                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
13                mkdir -p "${DIR_PATH}"
14                run_prusaslicer \
15                --load printers/printer_mk3.ini \
16                --load $NOZZLE \
17                --load $FILAMENT \
18                --load $SETTINGS \
19                --post-process scripts/prusaslicer_massage.py \
20                --output "${DIR_PATH}" "${MODEL}"
21
22            done
23        done
24    done
25done
26
27# Vase mode prints
28find "./models/vase" -iname "*.stl" | while read MODEL
29do 
30    echo "${MODEL}"
31    for NOZZLE in nozzles/*.ini; do 
32        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
33        for FILAMENT in filaments/XT.ini; do 
34            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
35            for SETTINGS in settings/settings_020_layers_vase.ini ; do 
36                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
37                echo "Output file is $DIR_BASENAME/${DIR_FILAMENT}/${DIR_NOZZLE}/${DIR_MODEL}"
38                mkdir -p "${DIR_PATH}"
39                run_prusaslicer \
40                --load printers/printer_mk3.ini \
41                --load $NOZZLE \
42                --load $FILAMENT \
43                --load $SETTINGS \
44                --post-process scripts/prusaslicer_massage.py \
45                --output "${DIR_PATH}" "$MODEL"
46
47            done
48        done
49    done
50done
51
52# Concentric fill prints
53find "./models/concentric" -iname "*.stl" | while read MODEL
54do 
55    echo "${MODEL}"
56    for NOZZLE in nozzles/*.ini; do 
57        DIR_NOZZLE="$(basename -s .ini $NOZZLE)"
58        for FILAMENT in filaments/XT.ini; do 
59            DIR_FILAMENT="$(basename -s .ini $FILAMENT)"
60            for SETTINGS in settings/settings_020_layers_normal.ini ; do 
61                DIR_PATH="${DIR_BASENAME}/${DIR_FILAMENT}/${DIR_NOZZLE}"
62                echo "Output file is $DIR_BASENAME/${DIR_FILAMENT}/${DIR_NOZZLE}/${DIR_MODEL}"
63                mkdir -p "${DIR_PATH}"
64                run_prusaslicer \
65                --load printers/printer_mk3.ini \
66                --load $NOZZLE \
67                --load $FILAMENT \
68                --load $SETTINGS \
69                --post-process scripts/prusaslicer_massage.py \
70                --output "${DIR_PATH}" "$MODEL" \
71                --external-fill-pattern concentric
72
73            done
74        done
75    done
76done
Listing 2.24 PrusaSlicer run script
1#!/bin/sh
2/Applications/PrusaSlicer.app/Contents/MacOS/PrusaSlicer --no-gui --print-center 125,105 $*
3
Listing 2.25 PrusaSlicer postprocessing script
  1#!/usr/bin/python
  2import sys
  3import re
  4import os
  5
  6parmEstimatedHours = ''
  7parmEstimatedMinutes = ''
  8parmEstimatedSeconds = ''
  9parmNotes = ''
 10
 11sourceFile=sys.argv[1]
 12
 13# Read the ENTIRE g-code file into memory
 14with open(sourceFile, "r") as f:
 15    lines = f.readlines()
 16
 17    coordMinX = 9999.9
 18    coordMaxX =0.0
 19    coordMinY = 9999.9
 20    coordMaxY = 0.0
 21    currLine = 1
 22    parsing = False
 23
 24for line in lines:
 25    currLine += 1
 26    parts = line.split(';', 1)
 27    if len(parts) > 0:
 28        # Parse command
 29        command = parts[0].strip()
 30        if len(parts) > 1:
 31            # Parse comments
 32            comment = parts[1].strip()
 33
 34        # Track extruder movement ranges
 35        if not re.search(";", comment):
 36            # Limit track to print movements, avoid start and end gcode blocks
 37            if re.search("move to first skirt point", comment):
 38                #if not parsing:
 39                #    print("Start extruder movement parsing at line %d with %s" % (currLine, comment))
 40                parsing = True
 41            if re.search("PURGING FINISHED", comment):
 42                #print("Stop extruder movement parsing at line %d with %s" % (currLine, comment))
 43                parsing = False
 44        if parsing:
 45            stringMatch = re.search ('^G[01].*X([0-9.]+)', command)
 46            if stringMatch:
 47                val = float(stringMatch.group(1))
 48                # print("%s: X is %d" % (currLine, val))
 49                if(val < coordMinX):
 50                    coordMinX = val
 51                if(val > coordMaxX):
 52                    coordMaxX = val
 53            stringMatch = re.search ('^G[01].*Y([0-9.]+)', command)
 54            if stringMatch:
 55                val = float(stringMatch.group(1))
 56                if(val < coordMinY):
 57                    coordMinY = val
 58                if(val > coordMaxY):
 59                    coordMaxY = val
 60
 61        # Include files
 62        includeFile = re.search('#INCLUDE\s+(.*)', comment)
 63        #if includeFile:
 64        #    outputFile.write(' -- Would include '+includeFile.group(1)+' stuff here')
 65
 66        # Parse estimated print time
 67        stringMatch = re.search ('^notes = (.*)', comment)
 68        if stringMatch:
 69            parmNotes = stringMatch.group(1).strip()
 70        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)h.*', comment)
 71        if stringMatch:
 72            parmEstimatedHours = stringMatch.group(1)+'h'
 73        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)m.*', comment)
 74        if stringMatch:
 75            parmEstimatedMinutes = stringMatch.group(1)+'m'
 76        stringMatch = re.search('estimated printing time \(normal mode\) =.* ([0-9]+)s.*', comment)
 77        if stringMatch:
 78            parmEstimatedSeconds = stringMatch.group(1)+'s'
 79        # Parse print job parameters
 80        stringMatch = re.search('filament_type = (.*)', comment)
 81        if stringMatch:
 82            parmFilamentType = stringMatch.group(1)
 83        stringMatch = re.search('nozzle_diameter = (.*)', comment)
 84        if stringMatch:
 85            parmNozzleDiameter = stringMatch.group(1)
 86        stringMatch = re.search('layer_height = (.*)', comment)
 87        if stringMatch:
 88            parmLayerHeight = stringMatch.group(1)
 89        # Parse temperatures
 90        stringMatch = re.search('bed_temperature = (.*)', comment)
 91        if stringMatch:
 92            parmBedTemperature = stringMatch.group(1)
 93        stringMatch = re.search('first_layer_bed_temperature = (.*)', comment)
 94        if stringMatch:
 95            parm1stLayerBedTemperature = stringMatch.group(1)
 96        stringMatch = re.search('temperature = (.*)', comment)
 97        if stringMatch:
 98            parmTemperature = stringMatch.group(1)
 99        stringMatch = re.search('first_layer_temperature = (.*)', comment)
100        if stringMatch:
101            parm1stLayerTemperature = stringMatch.group(1)
102
103destFile = re.sub('\.gcode$','',sourceFile)
104if parmNotes:
105    destFile = destFile + ' ('+parmNotes+')'
106#destFile = destFile + ' S3r'
107if parmFilamentType:
108    destFile = destFile + ' '+parmFilamentType
109if parmLayerHeight:
110    destFile = destFile + ' '+parmLayerHeight
111if parmNozzleDiameter:
112    destFile = destFile + 'X'+parmNozzleDiameter
113if parm1stLayerTemperature:
114    destFile = destFile + ' '+parm1stLayerTemperature
115if parmTemperature:
116    destFile = destFile + '-'+parmTemperature
117if parm1stLayerBedTemperature:
118    destFile = destFile + ' '+parm1stLayerBedTemperature
119if parmBedTemperature:
120    destFile = destFile + '-'+parmBedTemperature
121if parmEstimatedHours or parmEstimatedMinutes or parmEstimatedSeconds:
122    destFile = destFile + ' '
123    if parmEstimatedHours:
124        destFile = destFile + parmEstimatedHours
125    if parmEstimatedMinutes:
126        destFile = destFile + parmEstimatedMinutes
127    if parmEstimatedSeconds:
128        destFile = destFile + parmEstimatedSeconds
129destFile = destFile + '.gcode'
130#print('Writing to %s' % re.sub('.*/','',destFile))
131
132with open(destFile, "w") as of:
133    of.write('; Minimum X = '+ str(coordMinX)+'\n')
134    of.write('; Maximum X = '+ str(coordMaxX)+'\n')
135    of.write('; Minimum Y = '+ str(coordMinY)+'\n')
136    of.write('; Maximum Y = '+ str(coordMaxY)+'\n')
137    for lIndex in xrange(len(lines)):
138        oline = lines[lIndex]
139        #if ("G0" in line or "G1" in line) and ("E" in line) :
140        #    line = re.sub("E","B",line)
141        of.write(oline)
142
143of.close()
144f.close()
145
146os.remove(sourceFile)
Batch build directory structure

Fig. 2.25 Batch build directory structure

Batch output directory structure

Fig. 2.26 Batch output directory structure

Contact and feedback

You can find me on the Prusa support forums or Reddit where I lurk in many of the 3D printing-related subreddits. I occasionally drop into the Official Prusa 3D discord server where I can be reached as bobstro (bobstro#9830). You can email me directly at projects@ttlexceeded.com.

Last updated 20190831