cristhian fdabf7e241 refactor node into services over the dora 1.0 contract
Replaces the command/status JSON pair with dora's own service pattern:
one request, one response, correlated by the request_id dora puts in the
message metadata. Motions take a wait flag, and their lifecycle is
published as the arm's state on motion_state rather than as a per-goal
stream — the arm is one resource, so a new motion preempts the one in
play and a state describes that exactly.

motion_state is keyed by request_id and publishes accepted before the
control thread reaches the motion. Both exist to close one race: a caller
polling a bare moving/idle flag right after its request would read "not
moving" and conclude the motion had already finished.

Three threads, because the SDK blocks. The control thread carries out one
job at a time and may block; the reporter thread only ever reads the
report cache, so telemetry keeps flowing through a ten-second move; the
stopper thread exists because a stop fits nowhere else — it must not
queue behind the motion it aborts, and emergency_stop re-commands the
controller for up to three seconds, which is far too long to stop routing
a dataflow.

The event loop never calls anything that can block, get_state included:
that one is allowed there only because it reads the cache. The node is
built before the arm threads start, so INIT_MODE cannot move a robot
before the process has joined the dataflow, and a restart leaves the arm
where it is rather than driving it home unasked. Shutdown halts the arm
first — a motion lives in the controller, and dropping the connection
does not abort it.
2026-09-12 11:46:59 -03:00

Dora ULite6 Node

Dora node for controlling a UFactory Lite6 robot via REST API, web UI, and publishing joint/TCP state.

Installation

uv pip install -e .

Dataflow

nodes:
  - id: ulite6
    git: ssh://git@gitea.liria.cl:5001/liria/dora-ulite6.git
    git_checkout: v0.1.0
    build: uv pip install -e .
    path: dora_ulite6/main.py
    inputs:
      tick: dora/timer/millis/10
    outputs: [status, joint_pose, tcp_pose]
    inputs: [command]
    env:
      ROBOT_IP: "192.168.1.192"
      DEFAULT_SPEED: "30"
      DEFAULT_UNITS: "mm"
      API_HOST: "0.0.0.0"
      API_PORT: "8080"
      VACUUM_ENABLED: "false"

Configuration

Env Variable Default Description
ROBOT_IP 192.168.1.192 Robot IP address
DEFAULT_SPEED 30 Movement speed (mm/s)
DEFAULT_UNITS mm Position units (mm or m)
API_HOST 0.0.0.0 API server host
API_PORT 8080 API server port
VACUUM_ENABLED false Enable vacuum gripper controls
TOOL_TYPE none Mounted tool: none, vacuum, gripper
TOOL_OFFSET_MM 0.0 Additional Z-offset in mm added on top of the tool preset
INIT_MODE none Startup positioning: home, pose, or none
INIT_X/Y/Z 300/0/250 Initial pose coordinates (mm)
INIT_ROLL/PITCH/YAW 180/0/0 Initial pose orientation (deg)
INIT_SPEED 50 Startup movement speed (mm/s)

Web UI

Access at http://localhost:8080/

  • Live status, TCP pose, and joint angles
  • Home and Reset buttons
  • Move to position form
  • Vacuum gripper controls (when enabled)

REST API

Interactive docs at http://localhost:8080/docs

Method Endpoint Description
GET / Web control interface
GET /api/status Robot status (connected, errors)
GET /api/pose Current TCP pose
GET /api/joints Current joint angles
GET /api/config API configuration
POST /api/home Go to home position
POST /api/reset Clear errors and reset state
POST /api/move_to Move to position
POST /api/move_to_pose Move to full pose
POST /api/disconnect Disconnect from robot

Vacuum Gripper (when VACUUM_ENABLED=true)

Method Endpoint Description
GET /api/vacuum Get vacuum status
POST /api/vacuum/on Turn vacuum on
POST /api/vacuum/off Turn vacuum off

CLI Examples

# Get pose
curl http://localhost:8080/api/pose

# Go home
curl -X POST http://localhost:8080/api/home

# Move to position
curl -X POST http://localhost:8080/api/move_to \
  -H "Content-Type: application/json" \
  -d '{"x": 200, "y": 0, "z": 300}'

# Move with orientation
curl -X POST http://localhost:8080/api/move_to_pose \
  -H "Content-Type: application/json" \
  -d '{"x": 200, "y": 0, "z": 300, "roll": 180, "pitch": 0, "yaw": 0}'

Dora Outputs

  • status - JSON: {ok, action, message, timestamp_ns}
  • joint_pose - 6 joint angles in degrees
  • tcp_pose - [x, y, z, roll, pitch, yaw] in mm/deg — reflects tool tip when TOOL_TYPE is set

Dora Inputs

  • command - JSON string: {id, action, payload}
    • id: unique command id (required)
    • action: home, move_to, or move_to_pose
    • payload: fields for move (x, y, z, roll, pitch, yaw, speed)

status will include command_id for command responses.

S
Description
No description provided
Readme
372 KiB
2026-09-16 17:29:45 +00:00
Languages
Python 100%