3d打印远程上位机系统介绍
此上位机是基于3d打印固件Klipper,使用pyqt+tcp协议,Klipper 是一款开源的 3D 打印机固件,其中Klipper提供了可以与外部通信的API,通过借助这些API,可以开发一个远程的3d打印系统,该系统可以控制x,y,z三轴任意距离的移动,风扇调速,轴速控制,摄像头画面获取和部分G代码命令。
klipper有提供技术文档概述 - Klipper 文档
githup开源地址https://github.com/Klipper3d/klipper
如果本文对你有所帮助,那能否支持一下老弟呢,嘻嘻🥰
✨✨个人主页 点击✨✨
klipper通信框架分析
🤠Klipper的网页端主要有以下几种
- Mainsail:一个开源项目,主要使用 Vue.js 和 TypeScript 编程语言,旨在为使用 Klipper 固件的 3D 打印机提供流行的网页界面。具有响应式网页界面,可优化用于桌面、平板和移动设备;支持管理多台 3D 打印机;支持 12 种不同语言等众多功能。
- Fluidd:也是一款免费、开源的 Klipper 网页界面,同样使用 Vue.js 等技术开发,用于管理 3D 打印机。提供响应式的用户界面,支持桌面、平板和移动设备,内置多种颜色主题,可从一个 Fluidd 安装中管理多个打印机,还能让用户根据喜好移动任何面板,实现个性化的界面布局。
- Octoprint:它原本不完全算是专门为 Klipper 打造的网页界面,但也能与 Klipper 配合使用。有大量插件资源,适合已经习惯使用 Octoprint 且有较多插件依赖,或是追求功能全面、稳定可靠界面的用户。
🕵🏼就Fluidd而言,通信流程是Fluidd通过HTTP/Websocket协议连接运行在Klipper主机的web服务器Moonraker连接,moonraker再通过本地UNIX套接字与Klipper主机通信。
上述是浏览器通信,也就是HTTP协议的。
Klipper通信源码分析
Moonraker是一个用于外界客户端与Klipper主机通信的web服务器接口
Moonraker开源地址GitHub - Arksine/moonraker: Web API Server for Klipper
在klipper的源码路径为
/klipper-master/scripts/whconsole.py
whconsole.py中实现了如何通过本地UNIX套接字与Klipper通信
#!/usr/bin/env python2
# Test console for webhooks interface
#
# Copyright (C) 2020 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import sys, os, optparse, socket, fcntl, select, json, errno, time
# Set a file-descriptor as non-blocking
def set_nonblock(fd):
fcntl.fcntl(fd, fcntl.F_SETFL
, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
def webhook_socket_create(uds_filename):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.setblocking(0)
sys.stderr.write("Waiting for connect to %s\n" % (uds_filename,))
while 1:
try:
sock.connect(uds_filename)
except socket.error as e:
if e.errno == errno.ECONNREFUSED:
time.sleep(0.1)
continue
sys.stderr.write("Unable to connect socket %s [%d,%s]\n"
% (uds_filename, e.errno,
errno.errorcode[e.errno]))
sys.exit(-1)
break
sys.stderr.write("Connection.\n")
return sock
class KeyboardReader:
def __init__(self, uds_filename):
self.kbd_fd = sys.stdin.fileno()
set_nonblock(self.kbd_fd)
self.webhook_socket = webhook_socket_create(uds_filename)
self.poll = select.poll()
self.poll.register(sys.stdin, select.POLLIN | select.POLLHUP)
self.poll.register(self.webhook_socket, select.POLLIN | select.POLLHUP)
self.kbd_data = self.socket_data = b""
def process_socket(self):
data = self.webhook_socket.recv(4096)
if not data:
sys.stderr.write("Socket closed\n")
sys.exit(0)
parts = data.split(b'\x03')
parts[0] = self.socket_data + parts[0]
self.socket_data = parts.pop()
for line in parts:
sys.stdout.write("GOT: %s\n" % (line,))
def process_kbd(self):
data = os.read(self.kbd_fd, 4096)
parts = data.split(b'\n')
parts[0] = self.kbd_data + parts[0]
self.kbd_data = parts.pop()
for line in parts:
line = line.strip()
if not line or line.startswith(b'#'):
continue
try:
m = json.loads(line)
except:
sys.stderr.write("ERROR: Unable to parse line\n")
continue
cm = json.dumps(m, separators=(',', ':'))
sys.stdout.write("SEND: %s\n" % (cm,))
self.webhook_socket.send(cm.encode() + b"\x03")
def run(self):
while 1:
res = self.poll.poll(1000.)
for fd, event in res:
if fd == self.kbd_fd:
self.process_kbd()
else:
self.process_socket()
def main():
usage = "%prog [options] <socket filename>"
opts = optparse.OptionParser(usage)
options, args = opts.parse_args()
if len(args) != 1:
opts.error("Incorrect number of arguments")
ml = KeyboardReader(args[0])
ml.run()
if __name__ == '__main__':
main()
🧐函数解析:
- main()函数:
前半段是用optparse库来进行接收命令行参数,该参数是用于UNIX套接字通信的文件,以.sock结尾,它是一种特殊类型的文件,用于在同一台计算机上的不同进程之间进行通信
ml = KeyboardReader(args[0]) ml.run()
后半段是KeyboardReader类初始化并运行run()函数。
- KeyboardReader类初始化:
在__init__函数中有这样一行代码
self.webhook_socket = webhook_socket_create(uds_filename)
这是用于连接本地UNIX套接字的函数,其中uds_filename是传过来的UNIX套接字文件
- webhook_socket_create()函数:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
这行代码用于创建一个 UNIX 域流式套接字对象
下面的while循环指的是与套接字来接,成功后返回一个用于通信的套接字对象sock。
到此KeyboardReader初始化完成。
- run()函数:
其中self.process_kbd()是用于通信和解析JSON数据的函数
Klipper通信API协议分析
🌴klipper通信的协议是以“{JSON格式}”的数据,在Python中称为“字典”,且请求字典中必须包含一个”method”字段,其值应包含一个可用的Klipper端点”endpoint”名称字符串。
可用的endpoint有(部分)详细可看官方文档
-
info:
“info” 用于从Klipper获取系统和版本信息。同时也被用来向Klipper提供客户端的版本信息。比如说
{"id": 123, "method": "info", "params": { "client_info": { "version": "v1"}}}
如果存在,“client_info”参数必须是一个字典,但该字典可能具有任意内容。鼓励客户端在首次连接到Klipper API服务器时提供客户端名称及其软件版本。
-
gcode/script:
这个endpoint允许运行一系列的G代码命令。比如说:
{"id": 123, "method": "gcode/script", "params": {"script": "G90"}}
-
emergency_stop:
"emergency_stop"端点用于指示 Klipper 过渡到 "shutdown"状态。它的行为类似于 G 代码 "M112 "命令。例如:
{"id": 123, "method": "emergency_stop"}
-
objects/list
该端点查询可用打印机“对象”的列表,可以查询(通过“对象/查询”端点)。例如:
{“id”:123,“method”:“objects/list”}
可能返回:
{"id": 123, "result": {"objects": ["webhooks", "configfile", "heaters", "gcode_move", "query_endstops", "idle_timeout", "toolhead", "extruder"]}}
-
objects/query
这个endpoint允许从打印机对象中查询信息。比如说:
{"id": 123, "method": "objects/query", "params": {"objects": {"toolhead": ["position"], "webhooks": null}}}
可能返回
{"id": 123, "result": {"status": {"webhooks": {"state": "ready", "state_message": "Printer is ready"}, "toolhead": {"position": [0.0, 0.0, 0.0, 0.0]}}, "eventtime": 3051555.377933684}}
使用TCP协议搭建应用程序/服务器与Klipper通信
🔥通过上述的解释之后,我们知道了klipper的通信流程,我们可以使用tcp协议的客户端应用程序与moonraker通信,当然如果你不想使用moonraker服务器,你同样可以自己写一个服务器,如果只是功能实现的话,理论上来讲是比较容易的,因为前面说的已经很清楚了,本质就是UNIX+TCP通信。
下面就是一个简单的UNIX与TCP的服务器程序
import socket
import errno
import time
import sys
import json
from optparse import OptionParser
class server:
#函数初始化
def __init__(self):
self.uds_filename = "/home/biqu/printer_data/comms/klippy.sock"
parser = OptionParser()
parser.add_option("-i", "--ip", dest="ip",
help="setting ip address for server",default="192.168.1.235")
parser.add_option("-p", "--port", dest="port",
help="setting ip address for server", default=8899)
(options, args) = parser.parse_args()
if options.ip:
self.ip = options.ip
if options.port:
self.port = options.port
# print(self.ip,self.port)
self.unixsocket = self.connect_unixserver(self.uds_filename)
self.connect_tcpserver()
#连接tcp服务器
def connect_tcpserver(self):
self.tcpser = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# ip = input("请输入ip地址")
self.tcpser.bind((self.ip,self.port))
self.tcpser.listen(1)
print("tcp server listening......")
self.tcpsocket,addr = self.tcpser.accept()
print("tcp server connect client successly",addr)
while 1:
bytedata = self.tcpsocket.recv(4096)
if bytedata:
self.srtdata = bytedata.decode(encoding="utf-8")
print(self.srtdata)
self.process_kbd(self.srtdata)
if not bytedata:
print("server disconnect")
break
self.tcpser.close()
self.unixsocket.close()
#连接UNIX套接字
def connect_unixserver(self,uds_filename):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
print("unix server is connecting ......")
sock.setblocking(0)
while 1:
try:
sock.connect(uds_filename)
except socket.error as e:
if e.errno == errno.ECONNREFUSED:
time.sleep(0.1)
continue
print("donot connect unix server %s [%d,%s]\n"
% (uds_filename, e.errno,
errno.errorcode[e.errno]))
sys.exit(-1)
break
print("unix server connect client successly.")
return sock
#将tcp份客户端的数据通过UNIX套接字传回klipper中
def process_kbd(self,gcode):
parts = gcode.split('\n')
for line in parts:
line = line.strip()
if not line or line.startswith('#'):
continue
try:
m = json.loads(line)
except:
sys.stderr.write("ERROR: Unable to parse line\n")
continue
cm = json.dumps(m, separators=(',', ':'))
# self.unixsocket.send("%s\x03" % (cm))
self.unixsocket.send(cm.encode()+b"\x03")
if __name__ == '__main__':
ser = server()
其中UNIX套接字文件选择你klipper主机的printer_data目录下的.sock文件
self.uds_filename = "/home/xxx/printer_data/comms/klippy.sock"
IP和端口选择你运行klipper主机的ip地址和端口
运行
python2 server.py -i 192.168.242.34 -p 8899
注意最好用python2运行,python3运行会有问题。
⚡而客户端只要按照服务器的IP和端口进行连接就可以了
如下
def connect_server(self):
self.client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.client_socket.connect(192.168.242.34,8899)
self.app.serveredit.setText("tcp服务器连接成功......")
接下来只要按照klipper官方的通信协议发送就可以远程控制打印机了
G代码解析
🕵🏼G-code(G 指令)是一种用于控制计算机数控(CNC)机床、3D 打印机等设备的编程语言
也就是在3d模型经过切片软件之后得到的一个.gcode文件,这个文件是打印机可以识别的
常用命令
1. G0/G1 - 线性移动
-
G0: 快速移动(非打印移动)
-
G1: 线性移动(打印移动)
-
示例:
G1 X10 Y20 Z0.2 F1500
解释:将打印头移动到X=10, Y=20, Z=0.2的位置,速度为1500 mm/min。
2. G28 - 归零
-
G28: 返回原点(Home)
-
示例:
G28 X Y
解释:将X轴和Y轴归零。
3. G90/G91 - 坐标模式
-
G90: 绝对坐标模式
-
G91: 相对坐标模式
-
示例:
G90
解释:切换到绝对坐标模式。
4. G92 - 设置当前位置
-
G92: 设置当前位置为指定值
-
示例:
G92 X0 Y0 Z0
解释:将当前位置设置为X=0, Y=0, Z=0。
5. M104 - 设置挤出机温度
-
M104: 设置挤出机温度
-
示例:
M104 S200
解释:将挤出机温度设置为200°C。
6. M109 - 等待挤出机温度
-
M109: 等待挤出机达到设定温度
-
示例:
M109 S200
解释:等待挤出机温度达到200°C。
7. M140/M190 - 设置/等待热床温度
-
M140: 设置热床温度
-
M190: 等待热床达到设定温度
-
示例:
M140 S60
解释:将热床温度设置为60°C。
8. M106/M107 - 控制风扇
-
M106: 开启风扇
-
M107: 关闭风扇
-
示例:
M106 S255
解释:以最大速度(255)开启风扇。
9. M82/M83 - 挤出机模式
-
M82: 绝对挤出模式
-
M83: 相对挤出模式
-
示例:
M83
解释:切换到相对挤出模式。
10. G29 - 自动调平
-
G29: 执行自动床面调平
-
示例:
G29
解释:执行自动床面调平程序。
11. M84 - 禁用步进电机
-
M84: 禁用步进电机
-
示例:
M84
解释:禁用所有步进电机。
12. M117 - 显示消息
-
M117: 在LCD屏幕上显示消息
-
示例:
M117 Printing...
解释:在LCD屏幕上显示“Printing...”。
13. M300 - 播放声音
-
M300: 播放蜂鸣声
-
示例:
M300 S440 P1000
解释:播放440Hz的声音,持续1000毫秒。
14. G2/G3 - 圆弧移动
-
G2: 顺时针圆弧移动
-
G3: 逆时针圆弧移动
-
示例:
G2 X10 Y10 I5 J0 F1500
解释:顺时针移动到X=10, Y=10,圆心相对起点偏移I=5, J=0。
15. M600 - 换料
-
M600: 暂停打印并提示换料
-
示例:
M600
解释:暂停打印并提示用户换料。
16. M501 - 加载设置
-
M501: 加载EEPROM中的设置
-
示例:
M501
解释:加载EEPROM中的设置。
17. M500 - 保存设置
-
M500: 保存当前设置到EEPROM
-
示例:
M500
解释:保存当前设置到EEPROM。
18. M201/M203 - 设置加速度和速度
-
M201: 设置加速度
-
M203: 设置最大速度
-
示例:
M201 X1000 Y1000
解释:设置X轴和Y轴的加速度为1000 mm/s²。
19. M204 - 设置打印加速度
-
M204: 设置打印加速度
-
示例:
M204 P1000 T1500
解释:设置打印加速度为1000 mm/s²,移动加速度为1500 mm/s²。
启庞Kp3s V1打印参数设置
上图是启庞的Kp3s V1高速打印机,因为自己买不起拓竹,所以就买了这个极具性价比的打印机
右图是用这台打印机打印的一条龙,打印的真不错😁
打印出一个好的零件,离不开好的参数配置。
切片软件是将模型转换为打印机可识别的文件的关键,我用的是
Orcaslicer官网下载OrcaSlicer - Powerful 3D Printing Slicer
接下来主要从两方面来介绍打印参数的设置
- 如何打好支撑,并且容易拆?
- 如何解决模型翘边问题?
问题一:打开Orcaslicer,左侧边栏选择“支撑”
- 类型:有四种类型可选,默认是“树状自动”,若遇到仅需在特定位置添加支撑的可以选“树状”手动
- 阈值角度:对悬垂角度低于阈值的生成支撑,默认35度
- 首层扩展:支撑首层与热床的接触,数值越大,与热床接触越多,可以适当调大
- 顶部z距离:支撑顶部和模型的间距,决定支撑是否好拆,可以上下0.05调整
- 底部z距离:支撑底部在模型表面时的z距离,可选择默认
- 支撑/模型xy间距:支撑和模型之间的xy距离。
问题二::打开Orcaslicer,左侧边栏选择“其他”
找到brim
- brim类型:选择仅外侧,
- brim宽度:可大,越大与热床接触越多,越不容易翘边,
- brim与模型之间的间隙:越大越容易拆掉,但与模型接触太少会起不到解决翘边的作用,越小约不容易拆掉
参考文章klipper源码分析之whconsole.py-优快云博客