3D Print
DIY 打印机
Marlin 2.X固件_Configuration
Marlin_MKS-TinyBee(ESP32)
Marlin-独立配置文件(快速配置)
Marlin-LCD增加机器调试
常见问题
ESP3D_2.1X
OctoPrint for Android
Klipper 3D打印机
Klipper 摄像头
Klipper-中文Gcode
Klipper-打印预览
Kiauh_部署加速
配置文件_功能注释
Klipper-添加Shell支持
Klipper-常用命令
Klipper-机器校准
Klipper-打完自动关机
Klipper-Android版
Klipper-层暂停
Mainsail-交互宏
TMC2209/2208
步数计算
最大速度/最大加速度
常见问题
3D打印机打印时风扇不转
SD卡更新固件失败
支撑平面塌陷
其他黑科技
切片软件
耗材变脆
Fusion360
参数化设计__Fusion 360
Fusion 360-齿轮齿条绘制
不含设计历史导出
STL模型可编辑
申请教育账户
本文档使用 MrDoc 发布
-
+
首页
Klipper-层暂停
目前有两种方案: 1. Klipper设置(可实时更改,但每次打印都需手动设置) 2. 切片软件中设置(无法实时更改,切片后重复打印无需设置) ## Klipper 层暂停 Klipper使用层暂停是需要实时知道确切的总层数和当前层数(SET_PRINT_STATS_INFO) 使用的 Cura 切片软件默认是没有该参数的,需要手动添加该参数,这里为了方便使用大佬提供的插件来实现 [klipper-preprocessor](https://github.com/pedrolamas/klipper-preprocessor) 是一个 Cura 后处理脚本,用于改进 Klipper 使用的输出 G 代码 1. 打开 Cura 2. 打开 `Help(帮助)` 菜单并单击 `Show Configuration Folder(显示配置文件夹)` 3. 在文件列表中,打开 `scripts` 文件夹 4. 将 [KlipperPreprocessor.py](https://github.com/pedrolamas/klipper-preprocessor/blob/master/KlipperPreprocessor.py?raw=true) 下载到 `scripts` 文件夹 5. 重启 Cura 6. 打开 `Extensions(扩展)——Post Processing(后处理)——Modify G-Code(修改 G-Code)` 7. 点击 `Add a script(添加一个脚本)` 选择 `Klipper Preprocessor(刚添加的脚本)` 8. 保持默认 `Add SET_PRINT_STATS_INFO、Add TIMELAPSE_TAKE_FRAME` 勾选即可 正常切片后就可以使用 Klipper 的层暂停功能 ### KlipperPreprocessor.py ```python # Klipper Preprocessor script for Cura # Version: 1.4.1 # # Copyright (c) 2022 Lasse Dalegaard # Copyright (c) 2023 Pedro Lamas # MIT licensed from ..Script import Script from UM.Logger import Logger from UM.Message import Message from tempfile import TemporaryDirectory from typing import List, Tuple import sys import subprocess import shutil import os class KlipperPreprocessor(Script): """ Prepare resulting gcode for Klipper. """ def getSettingDataString(self) -> str: return """{ "name": "Klipper Preprocessor v1.4.1", "key": "KlipperPreprocessor", "metadata": {}, "version": 2, "settings": { "add_set_print_stats_info": { "label": "Add SET_PRINT_STATS_INFO", "description": "Enable this to allow the slicer to add Klipper's SET_PRINT_STATS_INFO macro to the resulting G-Code. This allows Klipper to know what is the exact total layer count, and the current layer number in real-time.", "type": "bool", "default_value": true }, "add_timelapse_take_frame": { "label": "Add TIMELAPSE_TAKE_FRAME", "description": "Enable this to allow the slicer to add moonraker-timelapse's TIMELAPSE_TAKE_FRAME macro to the resulting G-Code. This allows Klipper to take snapshots on each layer change to make timelapse videos.", "type": "bool", "default_value": true }, "preprocess_cancellation_enabled": { "label": "Use preprocess_cancellation", "description": "Enable this to will allow the slicer to add object cancellation data to the resulting G-Code, enabling Klipper to cancel any specific single object while printing.", "type": "bool", "default_value": false }, "preprocess_cancellation_path": { "label": "Path to preprocess_cancellation", "description": "The path to the preprocess_cancellation binary (including file name).", "type": "str", "default_value": "", "enabled": "preprocess_cancellation_enabled" }, "preprocess_cancellation_timeout": { "label": "preprocess_cancellation timeout", "description": "The maximum time to allow preprocess_cancellation to run.", "unit": "s", "type": "int", "default_value": 600, "minimum_value": "1", "minimum_value_warning": "10", "maximum_value_warning": "900", "enabled": "preprocess_cancellation_enabled" }, "klipper_estimator_enabled": { "label": "Use klipper_estimator", "description": "Enable this to allow the slicer to add a more accurate time estimation to the resulting G-Code.", "type": "bool", "default_value": false }, "klipper_estimator_path": { "label": "Path to klipper_estimator", "description": "The path to the klipper_estimator binary (including file name).", "type": "str", "default_value": "", "enabled": "klipper_estimator_enabled" }, "klipper_estimator_timeout": { "label": "klipper_estimator timeout", "description": "The maximum time to allow klipper_estimator to run.", "unit": "s", "type": "int", "default_value": 600, "minimum_value": "1", "minimum_value_warning": "10", "maximum_value_warning": "900", "enabled": "klipper_estimator_enabled" }, "klipper_estimator_config_type": { "label": "Config source type", "description": "", "type": "enum", "options": { "file": "Config File", "moonraker_url": "Moonraker URL" }, "default_value": "file", "enabled": "klipper_estimator_enabled" }, "klipper_estimator_moonraker_url": { "label": "Moonraker URL", "description": "URL to Moonraker base.", "type": "str", "default_value": "", "enabled": "klipper_estimator_enabled and klipper_estimator_config_type == 'moonraker_url'" }, "klipper_estimator_moonraker_api_key": { "label": "Moonraker API Key", "description": "Optional Moonraker API Key.", "type": "str", "default_value": "", "enabled": "klipper_estimator_enabled and klipper_estimator_config_type == 'moonraker_url'" }, "klipper_estimator_config_cache": { "label": "Cache config from Moonraker", "description": "Enable this to cache a copy of the config from Moonraker.", "type": "bool", "default_value": false, "enabled": "klipper_estimator_enabled and klipper_estimator_config_type == 'moonraker_url'" }, "klipper_estimator_config_file_path": { "label": "Path to config file", "description": "The full path for the klipper_estimator config file.", "type": "str", "default_value": "", "enabled": "klipper_estimator_enabled and (klipper_estimator_config_type != 'moonraker_url' or klipper_estimator_config_cache)" } } }""" def execute(self, data: List[str]) -> List[str]: try: with TemporaryDirectory() as work_dir: filename, total_layers = self.prepare_temp_file(data, work_dir) self.execute_preprocess_cancellation(filename) self.execute_klipper_estimator(filename, work_dir) return self.return_processed_data(filename, total_layers) except Exception as e: self.showWarningMessage("Unhandled exception:\n%s" % (str(e),)) return data def prepare_temp_file(self, data: List[str], work_dir: str) -> Tuple[str, int]: Logger.log("d", "Initial run...") add_timelapse_take_frame: bool = self.getSettingValueByKey("add_timelapse_take_frame") filename = os.path.join(work_dir, "work.gcode") total_layers = 0 with open(filename, 'w') as work_file: for index, layer in enumerate(data): if index: work_file.write(";CURA_DATA_SPLIT_HERE\n") lines = layer.split("\n") for line in lines: if line.startswith(';LAYER:'): total_layers += 1 if add_timelapse_take_frame: work_file.write("TIMELAPSE_TAKE_FRAME\n") work_file.write(line + "\n") Logger.log("d", "Total layers found: %d", total_layers) return filename, total_layers def return_processed_data(self, filename: str, total_layers: int) -> List[str]: Logger.log("d", "Return output...") add_set_print_stats_info: bool = self.getSettingValueByKey("add_set_print_stats_info") data: List[str] = [] current_layer = 0 with open(filename) as work_file: layer: List[str] = [] for line in work_file: line = line.strip() if line == ";CURA_DATA_SPLIT_HERE": data.append("\n".join(layer)) layer = [] else: layer.append(line) if add_set_print_stats_info: if line.startswith(';LAYER:'): current_layer += 1 layer.append("SET_PRINT_STATS_INFO CURRENT_LAYER=%s" % (current_layer,)) elif line.startswith(';LAYER_COUNT:'): layer.append("SET_PRINT_STATS_INFO TOTAL_LAYER=%s" % (total_layers,)) data.append("\n".join(layer)) return data def execute_preprocess_cancellation(self, filename: str) -> None: preprocess_cancellation_enabled: bool = self.getSettingValueByKey("preprocess_cancellation_enabled") if preprocess_cancellation_enabled: Logger.log("d", "Running preprocess_cancellation...") preprocess_cancellation_path: str = self.getSettingValueByKey("preprocess_cancellation_path") args = [ preprocess_cancellation_path, filename, ] timeout: int = self.getSettingValueByKey("preprocess_cancellation_timeout") try: ret = subprocess.run(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, startupinfo = self.getSubprocessStartupinfo(), timeout = timeout) if ret.returncode != 0: raise Exception(ret.stdout.decode().strip()) except subprocess.TimeoutExpired: self.showWarningMessage("Timeout while running preprocess_cancellation") except Exception as e: self.showWarningMessage("Failed to run preprocess_cancellation\n%s" % (str(e),)) def execute_klipper_estimator(self, filename: str, work_dir: str) -> None: klipper_estimator_enabled: bool = self.getSettingValueByKey("klipper_estimator_enabled") if klipper_estimator_enabled: klipper_estimator_config_type: str = self.getSettingValueByKey("klipper_estimator_config_type") klipper_estimator_moonraker_url: str = self.getSettingValueByKey("klipper_estimator_moonraker_url") klipper_estimator_moonraker_api_key: str = self.getSettingValueByKey("klipper_estimator_moonraker_api_key") klipper_estimator_config_file_path: str = self.getSettingValueByKey("klipper_estimator_config_file_path") klipper_estimator_path: str = self.getSettingValueByKey("klipper_estimator_path") if klipper_estimator_config_type == 'moonraker_url' and self.getSettingValueByKey("klipper_estimator_config_cache"): Logger.log("d", "Running klipper_estimator to get config from Moonraker...") klipper_estimator_config_type = 'file' args = [ klipper_estimator_path, "--config_moonraker_url", klipper_estimator_moonraker_url, "--config_moonraker_api_key", klipper_estimator_moonraker_api_key, "dump-config" ] config_filename = os.path.join(work_dir, "config.json") with open(config_filename, 'w') as config_file: try: ret = subprocess.run(args, stdout = config_file, stderr = subprocess.PIPE, startupinfo = self.getSubprocessStartupinfo(), timeout = 5) if ret.returncode == 0: shutil.copy(config_filename, klipper_estimator_config_file_path) else: self.showWarningMessage("Failed to run klipper_estimator\n%s" % (ret.stderr.decode().strip(),)) except subprocess.TimeoutExpired: pass Logger.log("d", "Running klipper_estimator...") klipper_estimator_config_arg: str = klipper_estimator_moonraker_url if klipper_estimator_config_type == 'moonraker_url' else klipper_estimator_config_file_path args = [ klipper_estimator_path, "--config_" + klipper_estimator_config_type, klipper_estimator_config_arg, "--config_moonraker_api_key", klipper_estimator_moonraker_api_key, "post-process", filename, ] timeout: int = self.getSettingValueByKey("klipper_estimator_timeout") try: ret = subprocess.run(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, startupinfo = self.getSubprocessStartupinfo(), timeout = timeout) if ret.returncode != 0: raise Exception(ret.stdout.decode().strip()) except subprocess.TimeoutExpired: self.showWarningMessage("Timeout while running klipper_estimator") except Exception as e: self.showWarningMessage("Failed to run klipper_estimator\n%s" % (str(e),)) def getSubprocessStartupinfo(self): if sys.platform == "win32": startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.wShowWindow = subprocess.SW_HIDE return startupinfo return None def showWarningMessage(self, text) -> None: Logger.logException("w", text) message = Message(text, title = "Klipper Preprocessor", message_type = Message.MessageType.WARNING) message.show() ``` ## Cura 切片层暂停 Cura 默认调用的方法 Klipper 是不支持的(如 Marlin M0),需要手动添加 1. 打开 `Extensions(扩展)——Post Processing(后处理)——Modify G-Code(修改 G-Code)` 2. 点击 `Add a script(添加一个脚本)` 选择 `Pause at height(在高处暂停)` 3. 根据需求设定即可(支持指定层和指定高度)  ### Klipper 添加 M0 支持 添加`M0` G-Code 支持,这里切片就需要选择 Malrin(M0) 的方法进行暂停 `printer.cfg` ```bash [gcode_macro M0] # 解决使用 Marlin G-Code 的 M0 暂停无效 description: 暂停打印 gcode: PAUSE # 暂停打印(实际就是调用该脚本) ```
造物者W
2024年9月14日 13:02
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码