调平与探针
调平不是一个动作,是三层:先让龙门/机架自身水平(多 Z 找平),再测床面形状(网格), 最后定义 Z 零点(探针偏移)。顺序不能反 —— 上一层变了,下一层的测量全部作废。
大幅面机(1 m 以上 / 大喷嘴 / 混凝土)和桌面机是两套完全不同的方案,混用必踩坑。
一、先分层,再选方案
| 层 | 解决什么 | 手段 | 失效后果 |
|---|---|---|---|
| L1 机架找平 | 龙门/横梁自身歪斜、四立柱不等高 | Z_TILT_ADJUST(2 个 Z)/ QUAD_GANTRY_LEVEL(4 个 Z) |
首层一边厚一边薄,网格怎么测都补不平 |
| L2 床面形状 | 床板本身的凹凸、热变形 | BED_MESH_CALIBRATE(网格补偿) |
首层局部过高刮床 / 过低不粘 |
| L3 Z 零点 | 喷嘴到床的实际间隙 | PROBE_CALIBRATE → z_offset |
整体首层偏高或偏低,靠手调救 |
关键关系:L2 的网格是「相对高度表」,它的基准面就是 L3 定义的 Z 零点。
所以 z_offset 一变,之前测的网格整体平移、直接作废。官方文档写得很直白:
Prior to Mesh Calibration you will need to be sure that your Probe's Z-Offset is calibrated.
G28 → PROBE_CALIBRATE / TESTZ 逼近 → ACCEPT → SAVE_CONFIG
G28 → BED_MESH_CALIBRATE → BED_MESH_PROFILE SAVE=default
L1 又在 L2 之上:龙门没找平就去测网格,等于用一个歪的尺子量床面, 补偿量会被机架误差污染,越补越乱。
二、大幅面机的方案选型
1 m 以上的床,热变形量级是零点几毫米(实测:同一点冷床 +0.55 mm, 加热到 80 °C 变成 +0.94 mm,热变形约 0.4 mm)。这个尺度上「手动调平」和「单 Z 电机」都不成立。
| 方案 | 硬件成本量级 | 精度 | 扫描 1 m 床耗时 | 适用 |
|---|---|---|---|---|
| 多 Z(2–4)+ 电涡流探头 ⭐ | 探头 300–500 元 + 每多一根 Z 电机约 200 元 | ±20 μm | ~30 s | 大幅面首选;非接触、无磨损 |
| 多 Z + BLTouch 克隆 | 探头 ~40 元 + 电机 ~80 元 | ±50 μm 级 | 3–5 min | 预算卡死、不常换床 |
| 纯电感接近开关(PL-08N) | 几十元 | 温漂 0.1 mm+ | — | 不推荐:冷热态偏差吃掉全部精度 |
| 单 Z + 手动 | 0 | — | — | 不成立:龙门必然一头高一头低 |
混凝土 / 颗粒料打印的特殊约束
大喷嘴会粘料,常规接触式探针没法戳(戳一下带一坨料走)。三条路:
- 激光测距(如 Panasonic HG-C1030 一类,约 800 元)—— 非接触,粘料无所谓
- 首层视觉补偿 —— 摄像头拍首层,偏差反馈回 G-code
- 龙门找平必须做 —— 用机械限位块 + 编码器绝对定位
三、四 Z 调平的四个硬规则(quad_gantry_level)
这四条都是「不知道就一定会把机器拧坏」的级别。
规则 1:gantry_corners 的顺序 = 电机顺序
[quad_gantry_level]
gantry_corners: # 顺序 ↔ 电机,绝不能错
1100,800 # → stepper_z (DRIVER0)
1100,100 # → stepper_z1 (DRIVER1)
-100,100 # → stepper_z2 (DRIVER2)
-100,800 # → stepper_z3 (DRIVER3)
points: 950,750 / 950,150 / 50,150 / 50,750
speed: 100
horizontal_move_z: 20
角位与 stepper_z / z1 / z2 / z3 的对应关系必须现场逐根核对。
反了的后果不是报错,是四根立柱朝互相相反的方向扭转,把龙门拧变形。
同理,给 gear_ratio / rotation_distance 时四个 Z 必须一起改,
漏一个 ⇒ 同一段 G28 Z 里四电机按不同比例走。
规则 2:points 必须落在四角围成的矩形内
Klipper 要求探测点在 gantry_corners 围成的矩形内。出界不报错,而是外推,
误差被放大。报错信息是 Probe points outside gantry corners。
立柱常装在行程之外(比如 X −100 / 1100 而行程是 0–1000),所以取点要往里收:
| 项 | 立柱位置 | 取点 |
|---|---|---|
| X | −100 / 1100 | 50 / 950(各收 50 避开软限位) |
| Y | 100 / 800 | 150 / 750 |
[bed_mesh] 的 mesh_min/mesh_max 应与 QGL 的 points 对齐,
否则网床测量范围出界,边缘照样外推。
⚠️ 结构性限制:立柱 Y 只跨 100–800 而机床 Y 行程 1000 ⇒ Y < 100 / Y > 800 的台面 本身调平覆盖不到,这是机械决定的,靠改配置解决不了。
规则 3:只有 1 个 Z 端停时,「端停当探针」要三处联动
四 Z 机常常只装一个 Z 限位开关。要把它当床探针用,必须同时改三处:
[stepper_z]
endstop_pin: probe:z_virtual_endstop ; 原 ^PD11 —— 引脚交给 probe 管
# position_endstop: -13 ; 注释掉,改由 probe 的 z_offset 决定
[probe]
pin: ^PD11 ; 就是原来 Z 端停那个脚
z_offset: -13 ; 与旧 position_endstop 对齐 ⇒ 归零后坐标不变
- 只把
[probe] pin写成PD11而不改[stepper_z] endstop_pin⇒ 启动报pin PD11 used multiple times z_offset取旧position_endstop的值,是为了让G28 Z之后坐标仍显示 0(不改变已有坐标系)- 常闭探针保留
^!,常开去掉!;x_offset / y_offset按探针相对喷嘴的实际位置填
规则 4:Mainsail 里看不到按钮 = 配置没加载,不是配置错
症状:printer.cfg 里已经写了 [quad_gantry_level],Mainsail 左侧 MACHINE → Tools
里却没有按钮。
原因:Klipper 没重启。Mainsail 的按钮是从 Klipper 内存里的对象列表生成的。
核对方法:printer.cfg 的 mtime 晚于 Klipper 进程启动时间 ⇒ 就是没加载。
处理:FIRMWARE_RESTART。重启后起不来,优先看两类错误:
pin ... used multiple times(规则 3)Probe points outside gantry corners(规则 2)
四、最隐蔽的失效:探针下探余量 = 0
这是「探针没坏、点位也测得到,但第二点就报 No trigger on probe after full movement」
的真根因,非常容易被误判成硬件故障。
机理
下探目标高度 = [stepper_z] position_min (probe.py: descend_until_trigger → lookup_minimum_z)
探针归零后触发瞬间的喷嘴坐标 = [probe] z_offset
⇒ 可探条件:bed_z ≥ position_min − z_offset
两个值重合(余量 0)时,只有比归零中心高的点能探到,低 0.05 mm 就报 No trigger。
现场特征(可用来识别这个坑)
probe: at 50.000,50.000 bed will contact at z=0.940625 ← 第1点(中心)正常触发
probe: at 50.000,50.000 bed will contact at z=0.931250 ← samples=2 也正常
!! No trigger on probe after full movement ← 第2点(远端)挂
// Klipper state: Shutdown
判据:中心点每次成功、远端点每次失败,且远端床面比中心低 → 就是余量问题,不是探针坏。
另一个旁证:z_offset = 0 期间同样的 Z_TILT_ADJUST 连续 8 次全部成功;
改成负值之后一次都没成功过。
修法
[stepper_z]
position_min: -15 # 原 -5;与 z_offset=-5 配合得到 10 mm 余量
更干净的路子是重标 z_offset:正常应为正值;出现负值本身说明喷嘴零位关系不对
(首层高度会整体偏),重标后设 position_min = z_offset − 8。
余量留多少?按热变形量级算:冷床 +0.55 → 热床 80 °C +0.94,约 0.4 mm 变形, 再给装配误差和探测重复性留量 —— 5 mm 是下限不是目标。
五、涡流位移传感:为什么能在冷端测喷嘴
消费级标杆(Bambu Lab A1 系列)被普遍误称为「load cell / 应变片」, 实际是涡流位移传感器 + 弹簧浮动热端:
喷嘴触床 → 热端被床面顶起(轴向浮动)→ 散热鳍与涡流线圈间隙变小
→ 线圈阻抗/谐振频率变化 → 触发「已接触」→ 记录 Z 零点
- 热端(喷嘴 + 加热块 + 散热鳍 + 热断一体)由卡扣 + 防转盘固定, 允许轴向滑动但不旋转
- 涡流线圈装在挤出机与热端之间、热端正上方(冷区),测散热鳍顶面的位移
- 一感多用:Z 归零、床面网格(最高 21×21)、熔体背压 → 自动压力提前(PA)
三代产品的技术路线对照
| 机型 | 调平传感器 | 探测方式 | 首层/流量检测 |
|---|---|---|---|
| A1 / A1 mini | 工具头涡流线圈(测散热鳍位移) | 喷嘴触床 + 浮动热端 | 同传感器测熔体背压 → 自动 PA |
| X1 / X1C | 热床下 3 个压电陶瓷力传感器 + 预紧弹簧 | 喷嘴按压热床 | 力传感器网格 + 微 LiDAR 复核 |
| P1 / P1S | 同 X1 床下力传感器 | 同 X1 | 无 LiDAR |
四条核心优势
- 零探针偏置 —— 测的就是喷嘴触床,无
z_offset标定,换板不用重调 - 传感器在冷端 —— 测散热鳍顶面(风扇强制冷却),避开 250 °C 高温区
- 无机械探针 —— 没有伸缩针,可靠、快(所以敢做 21×21 密集网格)
- 一感多用 —— 归零 + 网格 + 熔体压力自动 PA + 打印中实时监测
代价:热端必须轴向浮动(降低刚性,装配纪律要求高);传感器靠近热源有温漂。
温度补偿的四条做法
- 归零时喷嘴须 ≤ 140 °C —— 保证热膨胀状态一致
- 传感器冷区化 —— 测散热鳍而非加热块
- 判定用信号突变(微分判据)而非绝对值 —— 抗基线漂移
- 每次归零/调平重新建立基线(tare)
移植到 1.5 mm 颗粒热端:力程放大 10 倍
这是不能直接抄的关键差异:
| 喷嘴 | 熔体背压 |
|---|---|
| 0.4 mm | 2.5–3.8 N |
| 1.5 mm | 35–53 N(F = P × A,约 10 倍) |
- 探测小力(几 N)+ 挤出大力(几十 N)方向相反,量程跨度大,弹簧刚度要重新设计
- 浮动系统对重热端(马达 + 减速 + 料筒)固有频率低,要加阻尼
- 压力—位移传递函数必须随整机标定 —— 不能抄原厂 K 值
最值得抄的是架构:浮动热端 + 冷端位移传感 + 微分触发。开源可参照 Prusa MK4/XL 的 loadcell、Voron TAP(磁力耦合位移)、BDPressure(350 Ω 应变片 + 惠斯通电桥)。
面包板原型的低成本验证路线
| 方案 | 元件 | 成本 |
|---|---|---|
| LDC1612/LDC1614 模块(推荐) | TI 电感数字转换器,免自搭振荡电路;自绕 Φ10 mm 空心线圈(0.2 mm 漆包线 15–20 圈),ESP32/Arduino SPI 读 | ~20 元 |
| 74HC14 施密特触发器 | 线圈 L + 100 pF 电容并联成 LC 谐振,74HC14 维持自激;金属靠近 → 涡流损耗 → 等效电感减小 → 频率升高;单片机定时器输入捕获测频 | ~2 元 |
测试顺序(顺序很重要):频率—距离标定曲线(3/2/1.5/1/0.5/0.2/0 mm 逐档,确认单调) → 温漂测试 → 触发阈值与滞回标定。
⚠️ 涡流方案对非金属床无效(玻璃台面等),上方案前先确认床材质。
六、压力称重调平:Creality 路线的两个坑
桌面机另一条主流路线是床下 4 路 HX711 称重,喷嘴触床产生力信号触发 Z。 好处是无需探针、探测点就是喷嘴;坏处是软件闭源、易出「保存了不生效」的问题。
坑 1:模块压根不读 z_offset
实测取证链(这是典型形态,值得记住怎么查):
| 步骤 | 结果 |
|---|---|
| ① 屏上点「保存 Z offset」 | ✅ |
② 发出 Z_OFFSET_APPLY_PROBE |
✅ 日志有 |
③ probe.py 写进 [prtouch_v2] z_offset |
✅ save_config: set ... z_offset = -1.100 |
④ SAVE_CONFIG 写进 printer.cfg #*# 段 |
✅ |
⑤ 下次 G28,模块读这个值 |
❌ 它压根不读 |
模块内硬编码 def get_position_endstop(self): return 0;它真正读的参数列表里没有 z_offset。
而「保存」之所以看起来成功,是因为该机的 probe 对象挂在这个 section 名下,
Klipper 的通用保存机制认得这个名字 —— 名字对了,对面没人接。
旁证:历史值 -0.350 → -0.800 → -1.700 → -1.100 乱跳 = 每次都在重新猜,
没有任何反馈闭环。
真正决定 Z 基准的杠杆是模块自己的补偿参数(如喷嘴热膨胀补偿 noz_ex_com)+ 网床。
三条出路:
| 方案 | 做法 | 评价 |
|---|---|---|
| A 通用 Z 偏移持久化 | [save_variables] + 自定义 ZCOMP_SAVE/ZCOMP_APPLY,PRINT_START 里 G28 后套用 |
最稳、不碰第三方模块;换机要重配 |
| B 改模块自己认的参数 | 改补偿系数 = 整体平移 Z 基准 | 要手算差值,SAVE_CONFIG 才生效 |
| C 给模块打补丁 | 让它认 z_offset |
最「正确」,但要改第三方模块归零逻辑 |
坑 2:打补丁时的四个陷阱
- 必须完整重启 Klipper 进程 —— Python 模块缓存,
RESTART不够。 验证:看.pyc的重新编译时间。 - 补丁生效前先清垃圾值 —— 把
#*#段里累积的旧 offset 归零。 否则补丁一生效,那个「不生效时期」攒下的 -1.100 会真的生效,首层直接悬空 1.1 mm。 - 符号约定 —— 负值 = 喷嘴抬高(与标准
[probe]语义一致)。 - 旧暂存会覆盖现场改动 —— 下载过的
/tmp/printer.cfg里还带旧值, 直接套用会把归零又写回旧数。
七、BLTouch:引脚复用与混用限位
引脚复用规则(源码级)
Klipper 的 pins.py:同一引脚只有「用法类型 + 极性都相同」才允许复用。
| 场景 | 结果 |
|---|---|
[stepper_x]/[stepper_y] 共用 enable_pin: !PC15 |
✅ |
[stepper_z]/[stepper_z1] 共用 endstop_pin: !PB13 |
✅ |
BLTouch 的 control_pin 撞上 enable_pin: !PB6 |
❌ pin PB6 used multiple times |
断料检测的 switch_pin 撞上风扇 pin: PA1 |
❌ |
另一个机制:重复 section 不报错,后面的覆盖前面的
(RawConfigParser(strict=False),注释写 overrides apply linearly)。
⇒ 不要靠「重复段」兜底,stepper_z 应在原地改,而不是新增第二个 [stepper_z]。
★ Can't add signal that is already active 的真根因
这个报错看着像信号/中断问题,实际是一轴上混用了两种限位:
- 出处
src/trsync.c:同一路 trigger-sync 只能挂一个信号 - 全固件只有
stepper_stop_on_trigger会挂信号 —— 挂的是步进电机 ⇒ 问题在「电机 ↔ 限位/归零」的装配,与引脚、PWM 无关 - 官方明确禁止一轴混用(
homing.py):Can't home to mix of probe:z_virtual_endstop rails - 典型触发:把
[stepper_z]改成probe:z_virtual_endstop后,[stepper_z1]还留着机械endstop_pin: !PB13⇒ 轴上变成「探针 + 机械」两个不同限位
改之前不报错,是因为两个 Z 电机共用同一个引脚,Klipper 按引脚名把它合并成一个限位。
修法(一处):[stepper_z1] 里注释掉 endstop_pin —— 双 Z 的第二个电机不参与归零。
改完执行 FIRMWARE_RESTART(不是 RESTART)。
BLTouch 分步验证流程
顺序:先控制脚、再信号脚、再精度、最后标定。
FIRMWARE_RESTART # 先从 shutdown 恢复
BLTOUCH_DEBUG COMMAND=pin_down # 针伸出 + 红灯灭
BLTOUCH_DEBUG COMMAND=pin_up # 针收回 + 红灯亮
BLTOUCH_DEBUG COMMAND=self_test # 完整自检
# 信号脚:pin_down → 指甲轻顶上顶 → QUERY_PROBE 应 TRIGGERED
PROBE_ACCURACY SAMPLES=20 PROBE_SPEED=2 # 标准差 < 0.01 mm 算好;range > 0.05 mm 说明干扰大
G28 # 探针下降时用指甲顶住,机器应立刻停
PROBE_CALIBRATE → TESTZ Z=-0.1 → … → ACCEPT → SAVE_CONFIG
判据:两次 QUERY_PROBE 都 open 或都 TRIGGERED ⇒ 信号线 / 上拉 / 配置问题,
不是机械问题。
校准宏(最小可用版)
[gcode_macro Z_OFFSET_CALIBRATE]
description: Z偏移校准:归零 → 移到床中心 → 进入 PROBE_CALIBRATE
variable_center_x: None # None = 自动取行程中心
variable_center_y: None
variable_travel_z: 20 # 安全高度,跑之前确认平台上没有超过 20 mm 的夹具
gcode:
{% set amin = printer.toolhead.axis_minimum %}
{% set amax = printer.toolhead.axis_maximum %}
{% set cx = center_x if center_x is not none else (amin.x|float + amax.x|float) / 2.0 %}
{% set cy = center_y if center_y is not none else (amin.y|float + amax.y|float) / 2.0 %}
M117 Z校准:归零中...
G28
G1 Z{travel_z} F600
G1 X{cx} Y{cy} F6000
M117 Z校准:TESTZ 微调
PROBE_CALIBRATE
八、什么时候两者同时失效
这些操作会让 z_offset 和 bed mesh 同时作废,做完必须重跑全流程:
- 拆装 / 挪动探针
- 换喷嘴
- 动床螺丝
- 跑
Z_TILT_ADJUST/QUAD_GANTRY_LEVEL/DELTA_CALIBRATE(探针有 X/Y 偏移时)
还有一个最容易被忽略的:温度必须一致。探针触发高度随温度漂移, 要么都在室温下做、要么都在打印温度下且等热稳定 —— 混着做等于白做。