教程 · 中级 · 30 分钟
用限位开关回零:机器坐标与工件坐标
开环机器上电时不知道自己在哪,回零就是它找出位置的过程——三段式寻零、软限位,以及为什么机器零点不是工件零点。
归零解决的是什么问题
一台开环运行的步进轴,对它被要求做过的每一件事都有完美的记忆,而对自己在哪儿一无所知。上电时它内部的位置计数器读作零——不是因为滑车在零处,而是因为整数初始化就是这个值。
更糟的是,机器运行中计数器也可能是错的。丢步是无声的:驱动器继续数脉冲,固件继续相信这些脉冲,于是滑车永久地偏了某个整步的四倍。控制环永远不会察觉,因为根本没有环。
归零对这两个问题都是答案。它是一台机器运行中唯一一个位置来自物理世界测量、而不是从计数累积而来的时刻——而下游的一切都依赖它。
三段式寻找
朝着开关驱动,在它动作时停下,把那儿叫作零点。这能行,而且重复精度在半个毫米左右——毫无用处。每一台真实的机器都分三遍做:
- **快速寻找。**朝着开关驱动,速度是按“不耐烦”而不是按精度选的。这一遍只为靠近而存在,它可能走完轴的全长。
- **退回。**退开几毫米直到开关释放。这是第三遍能进行的原因,它同时也证明了那确实是个开关,而不是一个卡住的输入。
- **缓慢重新寻找。**用第一遍速度的一个小分数再次接近,记下动作点。这才是测量。
慢的那一遍之所以要紧,是机械原因。杠杆微动开关在柱塞移动了特定距离时才动作,但滑车在固件反应、轴减速的这段时间里还在继续走。在 50 mm/s 配 2 ms 检测延迟的情况下,滑车会在任何人知道之前越过动作点 0.1 mm。在 2 mm/s 下,它越过 0.004 mm。缓慢接近把开关自身的重复性——一只合格微动开关上几微米——转换成机器的重复性,而不是让减速距离去主宰它。
典型设置大致是这样:
| 参数 | 典型值 | 原因 |
|---|---|---|
| 快速寻找进给 | 30–50 mm/s | 快到不至于让人烦 |
| 退回距离 | 3–5 mm | 足以让开关完全释放 |
| 缓慢寻找进给 | 1–3 mm/s | 减速距离变得可忽略 |
| 归零后拉开 | 1–5 mm | 让开关保持释放,这样它还能兼作限位 |
给限位开关接线
开关本身和机器人当保险杠用的是同一只杠杆微动开关,接线规则也一样:
const int X_MIN_PIN = 9;
void setup() {
pinMode(X_MIN_PIN, INPUT_PULLUP); // NO to ground, pin pulled up
}
bool endstopTriggered() {
return digitalRead(X_MIN_PIN) == LOW;
}
COM 接地,NO 接引脚,并打开内部上拉。这样断线会被读作“没有触发”,而对归零开关来说那是安全的失效方式:机器找不到零点并停下,而不是认定自己已经在那里。
这和急停的规则正相反,急停要的是常闭,这样线一断机器就停。归零开关和急停的安全状态是相反的,把两者接成一样是一个真实的错误,而不是风格问题。
有两个细节造成了大部分限位故障:
- 给它消抖。一次按压会在 4.7 ms 内产生大约九次跳变。归零时你是在一个紧凑的循环里轮询,所以第一个边沿才是你要的,之后几毫秒内的一切都该被忽略。永远不要挂一个计数边沿的中断。
- **把限位线布离电机线。**一根长而无屏蔽的线与斩波步进线平行走,拾到的恰恰就是那种看起来像开关闭合的噪声。如果关掉电机时归零正常、一开电机就失败,原因就在这里。
机器坐标与工件坐标
归零之后,机器精确知道一件事:限位在哪儿。那个点加上一个固定偏移,定义了机器零点——在 G 代码里就是 G53 坐标系。它是坐标系的属性。它不会动,而且每次归零之后都相同。
而那几乎永远不是你想据以测量工件的地方。所以机器会保留第二个可移动的原点:工件坐标系,G54 到 G59,定义为相对机器零点的偏移。
| 机器坐标(G53) | 工件坐标(G54…) | |
|---|---|---|
| 原点 | 由限位和机架固定 | 你把它设在哪就是哪 |
| 由谁设定 | 归零 | 你,每个活 |
| 重启后是否还在 | 归零之后在 | 在,存于控制器 |
| 用在哪里 | 软限位、换刀、停放位 | 实际加工的每一行 |
| 重新夹紧后会不会变 | 不会 | 会——这正是重点 |
这份分离在你把同一个活跑第二遍时就开始回本。G 代码说“从 X0 Y0 起画一个 50 mm 的正方形”。把纸贴在一个方便的地方,点动到它的角上,在那儿把工件坐标清零,然后跑。把纸挪走,重新清零,跑同一个文件。程序一个字都没改;改的是偏移。
机器零点机架知道,工件零点活知道。把它们分开,就是你不必为了挪一个零件去改文件的原因。
软限位
有了已知的机器原点,固件就能在滑车真的到那里之前,拒绝那些会跑出包络的运动。这就是软限位,而它严格优于硬件限位,因为它让机器在一次计划好的减速处停下,而不是靠碰撞。
; GRBL
$20=1 ; soft limits on
$130=200.0 ; X max travel, mm
$131=200.0 ; Y
$132=60.0 ; Z
开了软限位却没有归零流程的机器,等于有了无法执行的软限位,因为包络是从一个它并不拥有的原点量出来的。所以大多数固件在归零之前拒绝运行,而由此产生的错误信息不是麻烦——那是联锁在起作用。
每根轴远端的硬件限位开关是一道更粗糙的后备手段,只在软限位算错时才用得上。许多小型机器跳过它们,只靠软限位,这在机器上没有任何东西能伤到你时是个合理的取舍。
状态机
归零天生就是一台小型有限状态机,把它写成一个,能让超时处理保持诚实:
enum HomeState { IDLE, SEEK_FAST, BACK_OFF, SEEK_SLOW, HOMED, FAILED };
HomeState state = IDLE;
long stateStartedAt = 0;
void homingStep() {
switch (state) {
case SEEK_FAST:
if (endstopTriggered()) { stopNow(); enter(BACK_OFF); }
// an axis that never finds its switch must give up, not grind
else if (travelled() > AXIS_LENGTH_MM + 10) enter(FAILED);
break;
case BACK_OFF:
if (!endstopTriggered() && travelled() >= BACKOFF_MM) enter(SEEK_SLOW);
break;
case SEEK_SLOW:
if (endstopTriggered()) {
stopNow();
machinePosition = HOME_OFFSET_MM; // the one real measurement
enter(HOMED);
} else if (travelled() > BACKOFF_MM * 2) enter(FAILED);
break;
}
}
FAILED 那些分支是值得照抄的部分。一根一直找却永远不动作的轴,可能是开关断了、滑车卡住了,或者它在把给它的每一步都丢掉——这三种情况下的正确答案都是停下来把话说清楚。一个没有超时的归零程序,会高高兴兴地把滑车往机架里磨,随你磨多久。
出问题时
| 现象 | 原因 | 解决 |
|---|---|---|
| 每次归零都稍微偏一点 | 没有缓慢的第二遍,或者它太快 | 加上重新寻找;降到 1–3 mm/s |
| 往错的方向驱动然后撞上 | 归零方向或电机方向反了 | 翻方向设置,或者把一对线圈对调 |
| 还没动就报“限位触发” | 开关接成常闭,或者卡住了 | 查常开还是常闭;确认释放时引脚读高 |
| 冷机好用,电机一跑就跪 | 限位线拾到了驱动器噪声 | 重新布线远离电机线;加一只 100 nF 到地 |
| 归零正常,但之后每次移动都偏移 | HOME_OFFSET 或拉开量不对 |
从开关量到你想放零点的那个位置 |
| 上电后机器拒绝移动 | 软限位开着,还没归零 | 先归零——那是联锁,不是故障 |
| 重复性很好,但零件位置不对 | 是工件偏移,不是归零 | 在活真正所在的位置重新清零 G54 |
| Z 轴归零时钻进平台 | Z 方向反了,或者探针偏移没设 | 在你信任任何偏移之前先查方向 |
它处在什么位置
归零是一台开环机器恢复它结构上无法测量的那一样东西的方法。值得说清它买到什么、买不到什么:它让机器的位置在一个活开始时是对的,仅此而已。如果轴在活干到一半丢步,原因是限流或加速度曲线不对,那么在两次运行之间归零只是每次把误差重置为零,永远修不好它。
判别方法很简单。一台归零很准、却把活干完在错误位置的机器,那是运动问题,不是归零问题。
探索关联图谱
本教程所属的搭建
包含本教程的项目和学习路线。
延伸阅读