Проверка настроек слайсера по срезанному gcode

Интерфейс слайсера показывает то, что вы ввели. Принтер получает то, что осталось после наследования профилей, дефолтов самого слайсера и подстановок в стартовый G-code. Между этими двумя вещами регулярно оказывается пропасть, и единственный способ её увидеть — прочитать срез.

Три вещи, которые видно только там: эффективное значение ключа после всей цепочки наследования; дефолты слайсера для ключей, которых нет ни в одном профиле цепочки; и результат подстановок вида [chamber_temperature] в стартовый G-code.

Уровень 1: конфиг-блок в конце файла

Слайсер дописывает в хвост G-code все эффективные настройки строками ; ключ = значение. Это самый дешёвый и самый надёжный источник.

grep -E '^; (chamber_temperature|enable_pressure_advance|filament_max_volumetric_speed|gap_fill_target|wall_loops|sparse_infill_density) ' plate_1.gcode

Так я поймал у себя ; gap_fill_target = nowhere — ключа не было ни в одном профиле цепочки, значение пришло из дефолта слайсера, и в интерфейсе оно выглядело безобидно.

Полезные строки итогов там же:

; total filament used [g] = 87.90
; estimated printing time (normal mode) = 3h 20m 47s
; total layers count = 300

Именно по ним удобно сравнивать варианты настроек: разница в весе и времени считается без единой напечатанной детали.

Уровень 2: стартовая последовательность

Подстановки в машинном стартовом G-code — отдельный класс ошибок, потому что значение может быть корректным в профиле и потеряться при подстановке. Смотреть первые строки исполняемого блока:

grep -nE '^PRINT_START|^M14[01] |^M19[01] |^M104 |^M140 |^M106' plate_1.gcode | head -12

У меня это выглядело так до правки профиля и после:

PRINT_START BED=105 HOTEND=260 CHAMBER=0  EXTRUDER=0     # камера не грелась
PRINT_START BED=105 HOTEND=275 CHAMBER=60 EXTRUDER=0     # после смены базы профиля

Уровень 3: фактические скорости и расход

Заявленные в профиле скорости могут не достигаться — их режет лимит объёмного расхода. Метод разбора и таблица результатов — в Ограничение печати объёмным расходом.

Пакетная проверка вариантов из командной строки

Самое ценное применение — прогнать десяток вариантов настройки и получить вес и время каждого, не трогая интерфейс. У OrcaSlicer для этого есть CLI прямо в бандле приложения (на macOS — исполняемый файл внутри .app/Contents/MacOS/).

Базовый вызов:

OrcaSlicer --datadir "<каталог данных слайсера>" \
  --load-settings "machine.json;process.json" \
  --load-filaments "filament.json" \
  --slice 0 --outputdir . model.stl

Результат — plate_1.gcode в каталоге вывода. Дальше его читают grep-ом, как выше.

Грабли, на которые я потратил час

  • Ошибки не печатаются в stdout. На любой проблеме CLI пишет ровно run found error, exit. Настоящая причина — только в файле из --logfile (плюс --debug 4). Без этого отладка вслепую.
  • Пользовательские профили не имеют поля type — CLI падает с unknown config type of file ... in load-settings. Надо добавить его в копию: machine, process, filament. Осторожно: тип профиля печати называется process, а не print — на print та же ошибка, только с именем типа.
  • Копия профиля вне --datadir не подтягивает через inherits машинный G-code. Это самая коварная: слайсинг проходит, но со стартовым G-code по умолчанию. Симптом — в срезе нет PRINT_START, зато есть M190 S35, то есть стол греется до непонятных 35 °C. Лечится схлопыванием цепочки наследования в плоский конфиг перед подачей в CLI.
  • Но ключ inherits у машинного профиля надо оставить, даже когда все значения уже явные: проверка совместимости профиля печати с принтером сверяется именно с ним, иначе run 2652: process not compatible with printer. Плюс в compatible_printers профиля печати должны быть перечислены имена принтера — и своё пользовательское, и вендорское.
  • Промежуточная ошибка валидации Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add "G92 E0" to layer_gcode означает ровно то же самое: layer_change_gcode не унаследовался в копию.
  • Тип стола обязателен. Без curr_bed_type слайсер берёт температуру не того типа поверхности: у меня вместо BED=105 (High Temp Plate) уезжало BED=80 (значение для холодной плиты). Настройка живёт в профиле печати.
  • Имена полей итогов не те, что кажутся. Не total filament weight [g], а ; total filament used [g] =; не total estimated time, а ; estimated printing time (normal mode) =. Регулярка по неверному имени тихо вернёт «нет данных», и это легко принять за неудачный срез.

Автоматизация сравнения вариантов

Обвязка на питоне: взять плоский конфиг, поменять один ключ, срезать, вытащить вес и время. Так я за несколько минут получил кривую «плотность заполнения против веса и времени» и таблицу «лимит расхода против времени» — обе решили спор о настройках без единой печати.

import json, os, re, subprocess
 
def slice_variant(base_cfg, overrides, model):
    cfg = dict(base_cfg); cfg.update(overrides)
    json.dump(cfg, open("var.json", "w", encoding="utf-8"), ensure_ascii=False)
    if os.path.exists("plate_1.gcode"):
        os.remove("plate_1.gcode")
    subprocess.run([BIN, "--datadir", DATADIR, "--debug", "1", "--logfile", "/dev/null",
                    "--load-settings", "machine.json;var.json",
                    "--load-filaments", "filament.json",
                    "--slice", "0", "--outputdir", ".", model],
                   capture_output=True, timeout=900)
    if not os.path.exists("plate_1.gcode"):
        return None                      # срез не удался — смотреть логфайл
    t = open("plate_1.gcode", encoding="utf-8", errors="replace").read()
    g  = re.search(r'(?m)^; total filament used \[g\] = (.*)$', t)
    tm = re.search(r'(?m)^; estimated printing time \(normal mode\) = (.*)$', t)
    return (float(g.group(1)) if g else None, tm.group(1) if tm else None)

Проверку «файл появился» пропускать нельзя: при неудачном срезе в каталоге остаётся plate_1.gcode от предыдущего прогона, и обвязка бодро отрапортует чужими числами. Отсюда os.remove перед запуском.

Тестовую модель проще сгенерировать, чем искать: куб из двенадцати треугольников пишется в бинарный STL в десяток строк. Размер модели подбирать под вопрос — на кубе 20 мм при пяти стенках и крышках по 3 мм от заполнения почти ничего не остаётся, и разница плотностей не проявится; для таких сравнений нужен куб от 60 мм.

Когда это применять

  • Перед тем как поверить, что правка профиля вступила в силу — особенно если правился унаследованный ключ.
  • При споре «какая настройка лучше» — вес и время считаются точно, а не на глаз.
  • При разборе чужого профиля: конфиг-блок среза показывает его целиком и без домыслов.
  • После обновления слайсера: дефолты, которых нет в профилях, могут поменяться между версиями, а профиль при этом не менялся.

Источники