适配TRON1#2841
Conversation
Greptile SummaryThis PR adds full TRON1 humanoid robot support to DimOS, including a WebSocket-based high-level client, wire protocol codec, connection module, skill container, seven deployment blueprints (basic, agentic, RealSense, LiDAR/PointLIO, SLAM map visualization), and associated Nix build fixes. It also patches the dynamic CLI callback to correctly handle
Confidence Score: 3/5The core new files introduce real behavioral defects in the primary data path: quaternion ordering mismatch in protocol.py, walk-mode flag not reset on stop in connection.py, and silent telemetry death on WebSocket drop in high_level_client.py. Three independently confirmed defects in the most central files of the integration all affect real robot state — orientation, motion control, and telemetry. The Nix build also cannot complete while pkgs.lib.fakeHash remains. The blueprints and skill container are structurally sound, but the low-level transport layer needs fixes before this is production-safe. protocol.py (quaternion ordering), connection.py (_walk_mode_enabled reset), high_level_client.py (no reconnect on drop, late-response leak), and mapping/ray_tracing/rust/flake.nix (fakeHash blocks Nix builds) Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Agent as AI Agent
participant SC as TRON1SkillContainer
participant Conn as TRON1Connection
participant HLC as TRON1HighLevelClient
participant Robot as TRON1 Robot (WebSocket)
Note over Conn,Robot: start()
Conn->>HLC: start() → ws.connect()
HLC-->>Robot: WebSocket connect
Conn->>Robot: send request_enable_odom / request_enable_imu
Conn->>Robot: send request_stand_mode (if auto_stand)
Note over Agent,Robot: agentic movement
Agent->>SC: move(x, y, yaw, duration)
SC->>Conn: move(Twist, duration)
Conn->>Robot: send request_walk_mode (first call only)
Conn->>Robot: send request_twist
Conn-->>Conn: Timer → _send_zero_twist after duration
Note over Robot,Conn: telemetry callbacks
Robot-->>HLC: notify_odom / notify_imu
HLC->>Conn: _on_notify_odom / _on_notify_imu
Conn->>Conn: publish state_estimation + odom + imu
Note over Agent,Robot: agentic RPC
Agent->>SC: stand()
SC->>Conn: publish_request(request_stand_mode)
Conn->>HLC: call(request_stand_mode)
HLC->>Robot: "send + wait for response (timeout=10s)"
Robot-->>HLC: response
HLC-->>Conn: return result dict
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Agent as AI Agent
participant SC as TRON1SkillContainer
participant Conn as TRON1Connection
participant HLC as TRON1HighLevelClient
participant Robot as TRON1 Robot (WebSocket)
Note over Conn,Robot: start()
Conn->>HLC: start() → ws.connect()
HLC-->>Robot: WebSocket connect
Conn->>Robot: send request_enable_odom / request_enable_imu
Conn->>Robot: send request_stand_mode (if auto_stand)
Note over Agent,Robot: agentic movement
Agent->>SC: move(x, y, yaw, duration)
SC->>Conn: move(Twist, duration)
Conn->>Robot: send request_walk_mode (first call only)
Conn->>Robot: send request_twist
Conn-->>Conn: Timer → _send_zero_twist after duration
Note over Robot,Conn: telemetry callbacks
Robot-->>HLC: notify_odom / notify_imu
HLC->>Conn: _on_notify_odom / _on_notify_imu
Conn->>Conn: publish state_estimation + odom + imu
Note over Agent,Robot: agentic RPC
Agent->>SC: stand()
SC->>Conn: publish_request(request_stand_mode)
Conn->>HLC: call(request_stand_mode)
HLC->>Robot: "send + wait for response (timeout=10s)"
Robot-->>HLC: response
HLC-->>Conn: return result dict
Reviews (2): Last reviewed commit: "更新地图点云来源为导航栈后处理" | Re-trigger Greptile |
| cargoLock = { | ||
| lockFile = ./Cargo.lock; | ||
| outputHashes = { | ||
| "dimos-lcm-0.1.0" = "sha256-4DWFTf7Xqnx6pd2jXA/MVpRmZiFr6HqTSp9Qo9ZjToA="; |
There was a problem hiding this comment.
pkgs.lib.fakeHash left as placeholder — breaks Nix builds
pkgs.lib.fakeHash evaluates to sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=, which Nix deliberately rejects after computing the real hash and prints it to stderr. Anyone who runs nix build .#default --no-write-lock-file will see an immediate hash mismatch failure. This must be replaced with the real sha256-… string for lcm-msgs-0.1.0 before merging.
| if isinstance(v, (list, tuple)) and len(v) >= 4: | ||
| w = float(v[0]) | ||
| x = float(v[1]) | ||
| y = float(v[2]) | ||
| z = float(v[3]) | ||
| if w == 0.0 and x == 0.0 and y == 0.0 and z == 0.0: | ||
| return Quaternion(0.0, 0.0, 0.0, 1.0) | ||
| return Quaternion(x, y, z, w) |
There was a problem hiding this comment.
Inconsistent quaternion list ordering between IMU and odom parsers
parse_notify_imu treats a list as [w, x, y, z] (index 0 = w), while parse_notify_odom treats the same structure as [x, y, z, w] (index 3 = w). If the robot sends quaternions as arrays in both notifications, orientation data from one of the parsers will be wrong. The orderings should be made consistent; pick the convention that matches the actual wire format (typically ROS uses [x, y, z, w]).
| if isinstance(v, (list, tuple)) and len(v) >= 4: | |
| w = float(v[0]) | |
| x = float(v[1]) | |
| y = float(v[2]) | |
| z = float(v[3]) | |
| if w == 0.0 and x == 0.0 and y == 0.0 and z == 0.0: | |
| return Quaternion(0.0, 0.0, 0.0, 1.0) | |
| return Quaternion(x, y, z, w) | |
| if isinstance(v, (list, tuple)) and len(v) >= 4: | |
| x = float(v[0]) | |
| y = float(v[1]) | |
| z = float(v[2]) | |
| w = float(v[3]) | |
| if w == 0.0 and x == 0.0 and y == 0.0 and z == 0.0: | |
| return Quaternion(0.0, 0.0, 0.0, 1.0) | |
| return Quaternion(x, y, z, w) |
| @rpc | ||
| def stop(self) -> None: | ||
| if self._stop_timer: | ||
| try: | ||
| self._stop_timer.cancel() | ||
| except Exception: | ||
| pass | ||
| self._stop_timer = None | ||
| if self._client: | ||
| self._client.stop() | ||
| self._client = None | ||
| super().stop() |
There was a problem hiding this comment.
_walk_mode_enabled is not reset on stop(), breaking stop/start cycles
_walk_mode_enabled is set to True on the first move() call and never reset when the connection is stopped. On a subsequent start() + move(), the guard not self._walk_mode_enabled prevents request_walk_mode from being re-sent to the newly connected robot, which likely still needs walk mode enabled. The flag should be cleared in stop() alongside _client.
| @rpc | |
| def stop(self) -> None: | |
| if self._stop_timer: | |
| try: | |
| self._stop_timer.cancel() | |
| except Exception: | |
| pass | |
| self._stop_timer = None | |
| if self._client: | |
| self._client.stop() | |
| self._client = None | |
| super().stop() | |
| @rpc | |
| def stop(self) -> None: | |
| if self._stop_timer: | |
| try: | |
| self._stop_timer.cancel() | |
| except Exception: | |
| pass | |
| self._stop_timer = None | |
| if self._client: | |
| self._client.stop() | |
| self._client = None | |
| self._walk_mode_enabled = False | |
| super().stop() |
| def _recv_loop(self) -> None: | ||
| while not self._closed and self._ws: | ||
| try: | ||
| raw = self._ws.recv() | ||
| except websocket.WebSocketTimeoutException: | ||
| continue | ||
| except (websocket.WebSocketConnectionClosedException, OSError): | ||
| break | ||
|
|
||
| if isinstance(raw, bytes): | ||
| continue | ||
|
|
||
| try: | ||
| msg = json.loads(raw) | ||
| except json.JSONDecodeError: | ||
| continue | ||
|
|
||
| if not isinstance(msg, dict): | ||
| continue | ||
|
|
||
| if protocol.is_notify(msg): | ||
| self._dispatch_notify(msg) | ||
| continue | ||
|
|
||
| if protocol.is_response(msg): | ||
| req_id = str(msg.get("guid") or msg.get("id") or "") | ||
| if not req_id: | ||
| continue | ||
| with self._lock: | ||
| self._responses[req_id] = msg | ||
| pending = self._pending.get(req_id) | ||
| if pending: | ||
| pending.set() | ||
| continue |
There was a problem hiding this comment.
No reconnection on WebSocket drop — silently stops receiving data
When _recv_loop exits due to WebSocketConnectionClosedException or OSError, no reconnection attempt is made and no error is propagated — the module continues running but all notify_odom/notify_imu callbacks stop firing. If the robot's WebSocket server restarts (e.g., firmware update), the connection is silently dead until the whole module is restarted.
| @@ -0,0 +1,381 @@ | |||
| # TRON 1 接入 DimOS 迁移实施文档 | |||
|
|
|||
| 本文档描述如何将当前 DimOS 项目移植/适配到 LimX TRON 1 平台。目标是把 TRON 1 的 SDK(上层应用接口 + lowlevel 接口)映射到 DimOS 的 `Module / Blueprint / Skill / MCP` 体系,使你可以通过 `dimos run tron1-*` 运行同一套导航、感知、agentic 能力。 | |||
There was a problem hiding this comment.
Text should be in English.
There was a problem hiding this comment.
chinese + english ok maybe add translations
|
Cool integration @Pico-liujianhua - welcome to share any videos of the tron! We don't have a tron currently but you can bring it to our shenzhen office for further testing and integration if you'd like |
Problem
Closes DIM-XXX
Solution
How to Test
Contributor License Agreement