From 2da5d34c9d35fae7d09d49d6aba6403507721b3a Mon Sep 17 00:00:00 2001 From: YOLO-DT <3199429837@qq.com> Date: Sun, 11 Oct 2026 13:56:19 +0800 Subject: [PATCH] docs: correct the enable() lag figures and the 10 ms advice The 7.0-7.8 ms figure for get_state().enabled to turn true after enable() does not reproduce. Re-measured on the same unit: return at 2.0-3.1 ms, flag true at 17.6-48.5 ms (n=11, cold start and after disable alike). The earlier run read 7.0-7.8 ms, so the lag is state-dependent -- but it is always several status frames, never one. That also makes the "wait a frame (or ~10 ms)" advice wrong: 10 ms is shorter than the longest observed lag, so following it still reads a stale False. Every site now says to poll until the flag flips. disable() does not share the problem: return at 2.9-3.1 ms against 3.0-3.1 ms for enabled to read False. A single immediate read can still return the frame from before the command, which is what made it look like the same defect in the first place. Docstring and documentation only, no behaviour change. --- README.md | 6 ++++-- README.zh-CN.md | 6 ++++-- docs/DEVELOPER_GUIDE.md | 4 ++-- docs/DEVELOPER_GUIDE.zh-CN.md | 4 ++-- src/litearm/arm.py | 8 +++++--- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e266cd1..2ae9e4d 100644 --- a/README.md +++ b/README.md @@ -132,8 +132,10 @@ arm.home() # go home ``` **Do not** read `get_state().enabled` on the line after `enable()`. It returns before the -state stream reflects the change — measured 2.0-2.7 ms to return against 7.0-7.8 ms for the -flag to turn true — so the check reads `False` and means nothing. Wait a frame before judging. +state stream reflects the change — **2.0-3.1 ms** to return, while the flag takes +**17.6-48.5 ms** to turn true (n=11; one earlier run on the same unit read 7.0-7.8 ms — +the lag is state-dependent, but always several frames, never one). So the check reads +`False` and means nothing. **Poll until it flips** — a fixed 10 ms wait is not enough. ### Cartesian motion diff --git a/README.zh-CN.md b/README.zh-CN.md index 72f224c..6e3c34e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -123,8 +123,10 @@ arm.home() # 回零 ``` ⚠ **别在 `enable()` 的下一行读 `get_state().enabled`。** 它在状态帧反映之前就返回—— -实测返回耗时 2.0~2.7 ms,而那个标志变真要到 7.0~7.8 ms——所以读到的是 `False`, -那个值没有任何意义。判定前先等一拍。 +实测它 **2.0~3.1 ms** 就返回了,而那个标志变真要到 **17.6~48.5 ms**(n=11;同一台机 +另有一轮量到 7.0~7.8 ms——滞后随臂/固件状态而变,但**始终是好几拍**,不是一拍)。 +所以读到的是 `False`,那个值没有任何意义。判据请**轮询到它变真**—— +⚠ 写"固定等一拍(约 10 ms)"不够,照那个做仍会误判。 ### 笛卡尔运动 diff --git a/docs/DEVELOPER_GUIDE.md b/docs/DEVELOPER_GUIDE.md index 5ee59c2..02e1e69 100644 --- a/docs/DEVELOPER_GUIDE.md +++ b/docs/DEVELOPER_GUIDE.md @@ -234,8 +234,8 @@ park() | Method | Caveat | | --- | --- | -| `enable(attempts=12)` | Enables all joints. Retries are whitelisted: **only `(0x10, 0x03)` is retried**. ⚠ **It returns before the state stream confirms it** — measured 2.0-2.7 ms to return against 7.0-7.8 ms for `get_state().enabled` to turn true, so `enable(); get_state().value.enabled` reads `False` and means nothing. Wait a frame (or ~10 ms) before judging | -| `disable()` | Cuts the position loop. The arm is no longer held | +| `enable(attempts=12)` | Enables all joints. Retries are whitelisted: **only `(0x10, 0x03)` is retried**. ⚠ **It returns before the state stream confirms it** — measured **2.0-3.1 ms** to return against **17.6-48.5 ms** for `get_state().enabled` to turn true (n=11; an earlier run on the same unit read 7.0-7.8 ms — state-dependent, but always several frames), so `enable(); get_state().value.enabled` reads `False` and means nothing. **Poll until it flips** — a fixed ~10 ms wait is not enough | +| `disable()` | Cuts the position loop. The arm is no longer held. Unlike `enable()` it has **no long lag** — measured 2.9-3.1 ms to return against 3.0-3.1 ms for `enabled` to read `False`. A single immediate read can still return the previous frame (≤1 status frame), so poll if the answer matters | | `emergency_stop()` | Single frame, one-way, does not read state — the only entry point with no preconditions | | `reset()` | A software state reset, **not an MCU reboot**; the same object stays usable | | `clear_faults()` | Clears RAM fault bits only, does not write flash | diff --git a/docs/DEVELOPER_GUIDE.zh-CN.md b/docs/DEVELOPER_GUIDE.zh-CN.md index ff6e3f6..5ac74c5 100644 --- a/docs/DEVELOPER_GUIDE.zh-CN.md +++ b/docs/DEVELOPER_GUIDE.zh-CN.md @@ -226,8 +226,8 @@ park() | 方法 | 注意 | | --- | --- | -| `enable(attempts=12)` | 使能全部关节。重试是白名单,**只有 `(0x10, 0x03)` 会重试**。⚠ **它在状态帧反映之前就返回**——实测返回耗时 2.0~2.7 ms,而 `get_state().enabled` 变真要到 7.0~7.8 ms,所以 `enable(); get_state().value.enabled` 读到的是 `False`,那个值没有任何意义。判定前先等一拍(约 10 ms) | -| `disable()` | 切断位置环。此后机械臂不再被托住 | +| `enable(attempts=12)` | 使能全部关节。重试是白名单,**只有 `(0x10, 0x03)` 会重试**。⚠ **它在状态帧反映之前就返回**——实测返回耗时 **2.0~3.1 ms**,而 `get_state().enabled` 变真要到 **17.6~48.5 ms**(n=11;同一台机另有一轮量到 7.0~7.8 ms——随状态而变,但始终是好几拍),所以 `enable(); get_state().value.enabled` 读到的是 `False`,那个值没有任何意义。判据请**轮询到它变真**——固定等 10 ms 不够 | +| `disable()` | 切断位置环。此后机械臂不再被托住。与 `enable()` 不同,它**没有长滞后**——实测返回 2.9~3.1 ms、`enabled` 读到 `False` 是 3.0~3.1 ms。但紧接着单读一次仍可能拿到上一帧(≤1 拍),要紧就轮询 | | `emergency_stop()` | 单帧、单向、不读状态,唯一没有前置条件的入口 | | `reset()` | 软件状态复位,**不是 MCU 重启**,同一对象仍可用 | | `clear_faults()` | 只清 RAM 故障位,不写 flash | diff --git a/src/litearm/arm.py b/src/litearm/arm.py index 458ba91..c3f4742 100644 --- a/src/litearm/arm.py +++ b/src/litearm/arm.py @@ -1474,10 +1474,12 @@ def enable(self, attempts: int = 12) -> None: """使能全关节 (`CMD_ENABLE 0x10`), 必要时重试。 ⚠⚠ **本方法在状态帧反映之前就返回。** 真机实测 (yd 机整臂, `Litearm1.10.0-7J`): - 返回耗时 **2.0~2.7 ms**, 而 `get_state().enabled` 首次为真要到 **7.0~7.8 ms**。 + 返回耗时 **2.0~3.1 ms**, 而 `get_state().enabled` 首次为真要到 **17.6~48.5 ms** + (**n=11**; 同一台机另有一轮量到 **7.0~7.8 ms** —— 该滞后随臂/固件状态而变, + 但**始终是好几拍**, 不是一拍)。 ⇒ **`enable(); get_state().value.enabled` 读到的是 `False`, 那个值没有任何意义** - (6 轮实测每次都这样, 不是偶发)。要判定使能是否成立就**隔一拍再读** - (约 10 ms, 或等一条新的状态帧)。 + (每次都这样, 不是偶发)。要判定使能是否成立只能**轮询到它变真**: + ⚠ **"等一拍(约 10 ms)再读"不够** —— 实测最长 48.5 ms, 照那个做仍会误判。 ⚠ **只有固件明说"可重试"的码才重试** —— 判据是白名单 :data:`litearm.errors.ENABLE_RETRYABLE_CODES` (= `{0x03}`), 不是