Skip to content

适配TRON1#2841

Open
Pico-liujianhua wants to merge 2 commits into
dimensionalOS:mainfrom
Pico-liujianhua:feat/tron1-joint-state
Open

适配TRON1#2841
Pico-liujianhua wants to merge 2 commits into
dimensionalOS:mainfrom
Pico-liujianhua:feat/tron1-joint-state

Conversation

@Pico-liujianhua

Copy link
Copy Markdown

Problem

Closes DIM-XXX

Solution

How to Test

Contributor License Agreement

  • I have read and approved the CLA.

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This 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 Literal and Union type annotations from GlobalConfig.

  • TRON1 integration (connection.py, high_level_client.py, protocol.py, skill_container.py): implements a thread-safe WebSocket client with request/response tracking, IMU/odometry parsing, and velocity-control plus posture-management skills exposed to the agent layer.
  • Nix build fixes (ray_tracing/rust/flake.nix, pointlio/cpp/flake.nix): pins nixpkgs to stable channels, switches ray-tracing to rust-overlay with cargoLock; the lcm-msgs-0.1.0 output hash is still pkgs.lib.fakeHash (tracked in a prior thread), blocking Nix builds.
  • CLI fixes (dimos/robot/cli/dimos.py): monkey-patches typer.main.lenient_issubclass to swallow TypeError for Literal annotations, adds origin is Literal → str coercion, and fixes Union detection to cover both typing.Union and types.UnionType.

Confidence Score: 3/5

The 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

Filename Overview
dimos/robot/limx/tron1/high_level_client.py New WebSocket client for TRON1 with request/response tracking; late-response entries after timeout are never evicted from _responses
dimos/robot/limx/tron1/protocol.py New TRON1 wire protocol codec; IMU list-quaternion parser uses [w,x,y,z] ordering while odom uses [x,y,z,w], producing wrong orientation data when the robot sends list-format quaternions (flagged in prior review thread)
dimos/robot/limx/tron1/connection.py New TRON1 connection module; _walk_mode_enabled flag is not reset on stop() so walk-mode request is silently skipped on re-connect (flagged in prior review thread)
dimos/robot/limx/tron1/skill_container.py New skill container exposing stand, sitdown, recover, emergency_stop, and velocity-control skills; straightforward delegation to connection spec
dimos/core/global_config.py Adds TRON1-specific config fields (ws_url, accid, max velocities, feature flags) sourced from environment variables; tron1_robot_type is declared but not referenced anywhere
dimos/robot/cli/dimos.py Adds Typer monkey-patch for Literal annotations and fixes Union/Optional detection in dynamic CLI callback; annotation changed from T
dimos/mapping/ray_tracing/rust/flake.nix Switches to rust-overlay for toolchain, uses cargoLock instead of cargoHash; lcm-msgs-0.1.0 output hash is left as pkgs.lib.fakeHash which intentionally breaks Nix builds (flagged in prior review thread)
dimos/robot/limx/tron1/blueprints/basic/tron1_basic.py Minimal blueprint wrapping TRON1Connection; correct
dimos/robot/limx/tron1/blueprints/agentic/tron1_agentic.py Composes basic stack with agentic skills (MCP server/client, speak, TRON1 skills); straightforward
dimos/robot/limx/tron1/slam_map_relay.py Simple relay that republishes an external SLAM PointCloud2 map as global_map

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
Loading
%%{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
Loading

Reviews (2): Last reviewed commit: "更新地图点云来源为导航栈后处理" | Re-trigger Greptile

Comment on lines +43 to +46
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"dimos-lcm-0.1.0" = "sha256-4DWFTf7Xqnx6pd2jXA/MVpRmZiFr6HqTSp9Qo9ZjToA=";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment on lines +169 to +176
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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]).

Suggested change
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)

Comment on lines +88 to +99
@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()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 _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.

Suggested change
@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()

Comment on lines +119 to +152
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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 能力。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text should be in English.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nahhh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chinese + english ok maybe add translations

@spomichter

spomichter commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants