Skip to main content

Hex Flow Node for Robot Control (Archer Y6, Firefly Y6, Hello Y6)

Project description

HEX FLOW NODE ROBOT

      


📖 Overview

What is hex_flow_node_robot

hex_flow_node_robot provides hex-flow nodes for controlling HEXFELLOW robot arms (Archer Y6, Firefly Y6, Hello Y6). Each driver node bridges a hardware robot to the Zenoh pub/sub network via hex_flow_core, publishing state topics from hex_driver_robot callbacks and subscribing to control topics, using FlatBuffer messages from hex_util_msg. Test nodes provide terminal-based dashboards for live state visualization and command injection.

What problem it solves

  • Hardware abstraction: Wraps robot-specific network protocols (UDP-based control) into structured, topic-based state/control interfaces over Zenoh.
  • Bidirectional bridge: Publishes real-time robot state (joint positions, velocities, efforts, end-effector pose) and subscribes to control commands with multiple control modes (MIT, compensation, position, pose, trajectory planning).
  • Flexible deployment: Supports host/port configuration, topic remapping, and control parameter tuning via environment variables — no code changes needed to adapt to different robot setups.
  • Integrated testing: Ships with terminal-based test nodes for validating robot state and control without additional tools.

Target users

  • Engineers integrating HEXFELLOW robot arms (Archer Y6, Firefly Y6, Hello Y6) into automated systems.
  • Researchers running robot control experiments with the hex-flow framework.
  • Developers building on the hex-flow framework who need a working robot control layer.

Nodes

Node Description Publishes Subscribes
hex-flow-robot-archer-y6 Archer Y6 arm + gripper driver arm_state, grip_state arm_ctrl, grip_ctrl
hex-flow-robot-archer-y6-test Terminal viewer for Archer Y6 arm_ctrl, grip_ctrl arm_state, grip_state
hex-flow-robot-firefly-y6 Firefly Y6 arm + gripper driver arm_state, grip_state arm_ctrl, grip_ctrl
hex-flow-robot-firefly-y6-test Terminal viewer for Firefly Y6 arm_ctrl, grip_ctrl arm_state, grip_state
hex-flow-robot-hello-y6 Hello Y6 arm + grip joy + LED driver arm_state, grip_joy grip_led_ctrl
hex-flow-robot-hello-y6-test Terminal viewer for Hello Y6 grip_led_ctrl arm_state, grip_joy

📦 Installation

Requirements

  • Python >= 3.10
  • Core dependencies:
    • hex_flow_core >= 0.0.0, < 0.1.0
    • hex_driver_robot >= 0.1.0, < 0.2.0
    • hex_util_msg >= 0.0.0, < 0.1.0
    • hex_util_runtime >= 0.0.0, < 0.1.0

Install hex-flow-cli

For Ubuntu or any Debian-based system, install Zenoh and hex-flow CLI:

sudo apt update
sudo apt install -y curl gpg

curl -L https://download.eclipse.org/zenoh/debian-repo/zenoh-public-key | sudo gpg --dearmor --yes --output /etc/apt/keyrings/zenoh-public-key.gpg
echo "deb [signed-by=/etc/apt/keyrings/zenoh-public-key.gpg] https://download.eclipse.org/zenoh/debian-repo/ /" | sudo tee -a /etc/apt/sources.list > /dev/null
sudo apt update
sudo apt install zenoh curl

curl -fsSL https://raw.githubusercontent.com/hexfellow/hex-flow/main/install.sh | sh

For other systems, please install zenohd yourself, then run the install script.

Install hex-flow-node-robot from PyPI

pip install hex_flow_node_robot

Install hex-flow-node-robot from source

We provide a venv.sh script to create a virtual environment with all dependencies installed. However, you need to install uv first. For uv installation, please refer to uv official installation guide.

curl -LsSf https://astral.sh/uv/install.sh | sh

Then you can use our venv.sh to create a virtual environment with all dependencies installed:

git clone https://github.com/hexfellow/hex_flow_node_robot.git
cd hex_flow_node_robot
./venv.sh

📑 Python Config API

The package provides helper functions that return NodeConfig objects for easy integration into your LaunchConfig.

Archer Y6

from hex_flow_core import LaunchConfig
from hex_flow_node_robot import (
    default_robot_archer_y6_node,
    default_robot_archer_y6_test_node,
)

config = LaunchConfig(
    local_only=True,
    enable_tui=True,
    log_to_file=True,
    save_path="/tmp/robot_archer_y6.launch.yml",
)

nodes = {
    "robot_archer_y6":
    default_robot_archer_y6_node(
        name="robot_archer_y6",
        host="172.18.23.197",
        port=8439,
        ctrl_rate=500,
        state_buffer_size=200,
        sens_ts=True,
        grip_type="gp80",
        pose_end_in_flange="0.187,0.0,0.0,1.0,0.0,0.0,0.0",
        required=True,
        hidden=True,
        remap_dict={
            "arm_state": "robot_archer_y6/arm_state",
            "grip_state": "robot_archer_y6/grip_state",
            "arm_ctrl": "robot_archer_y6/arm_ctrl",
            "grip_ctrl": "robot_archer_y6/grip_ctrl",
        },
    ),
    "test_archer_y6":
    default_robot_archer_y6_test_node(
        name="test_archer_y6",
        rate_hz=10,
        arm_ctrl_mode="pos",
        grip_ctrl_mode="pos",
        required=False,
        remap_dict={
            "arm_state": "robot_archer_y6/arm_state",
            "grip_state": "robot_archer_y6/grip_state",
            "arm_ctrl": "robot_archer_y6/arm_ctrl",
            "grip_ctrl": "robot_archer_y6/grip_ctrl",
        },
    ),
}

config.set_nodes(nodes)
print(config.export())

default_robot_archer_y6_node

Parameter Type Default Description
name str "robot_archer_y6" Node name and remap prefix
host str "192.168.1.100" Robot IP address
port int 8439 Robot UDP port
ctrl_rate float 500.0 Control loop rate in Hz
state_buffer_size int 200 Internal state buffer size
sens_ts bool True Use sensor timestamps in published messages
grip_type str "gp80" Gripper model type
pose_end_in_flange str "0.187,0.0,0.0,1.0,0.0,0.0,0.0" End-effector pose in flange frame (7-DOF vector)
required bool True Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {name}/*

default_robot_archer_y6_test_node

Parameter Type Default Description
name str "test_archer_y6" Node name
rate_hz float 10.0 Display refresh rate in Hz
arm_ctrl_mode str "pos" Arm control mode (mit, pos, pose)
grip_ctrl_mode str "pos" Gripper control mode (mit, pos)
required bool False Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {robot_name}/*
robot_name str "robot_archer_y6" Robot node to subscribe/publish to

Firefly Y6

from hex_flow_core import LaunchConfig
from hex_flow_node_robot import (
    default_robot_firefly_y6_node,
    default_robot_firefly_y6_test_node,
)

config = LaunchConfig(
    local_only=True,
    enable_tui=True,
    log_to_file=True,
    save_path="/tmp/robot_firefly_y6.launch.yml",
)

nodes = {
    "robot_firefly_y6":
    default_robot_firefly_y6_node(
        name="robot_firefly_y6",
        host="172.18.23.197",
        port=8439,
        ctrl_rate=500,
        state_buffer_size=200,
        sens_ts=True,
        grip_type="gp80",
        pose_end_in_flange="0.187,0.0,0.0,1.0,0.0,0.0,0.0",
        required=True,
        hidden=True,
        remap_dict={
            "arm_state": "robot_firefly_y6/arm_state",
            "grip_state": "robot_firefly_y6/grip_state",
            "arm_ctrl": "robot_firefly_y6/arm_ctrl",
            "grip_ctrl": "robot_firefly_y6/grip_ctrl",
        },
    ),
    "test_firefly_y6":
    default_robot_firefly_y6_test_node(
        name="test_firefly_y6",
        rate_hz=10,
        arm_ctrl_mode="pos",
        grip_ctrl_mode="pos",
        required=False,
        remap_dict={
            "arm_state": "robot_firefly_y6/arm_state",
            "grip_state": "robot_firefly_y6/grip_state",
            "arm_ctrl": "robot_firefly_y6/arm_ctrl",
            "grip_ctrl": "robot_firefly_y6/grip_ctrl",
        },
    ),
}

config.set_nodes(nodes)
print(config.export())

default_robot_firefly_y6_node

Parameter Type Default Description
name str "robot_firefly_y6" Node name and remap prefix
host str "192.168.1.100" Robot IP address
port int 8439 Robot UDP port
ctrl_rate float 500.0 Control loop rate in Hz
state_buffer_size int 200 Internal state buffer size
sens_ts bool True Use sensor timestamps in published messages
grip_type str "gp80" Gripper model type
pose_end_in_flange str "0.187,0.0,0.0,1.0,0.0,0.0,0.0" End-effector pose in flange frame (7-DOF vector)
required bool True Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {name}/*

default_robot_firefly_y6_test_node

Parameter Type Default Description
name str "test_firefly_y6" Node name
rate_hz float 10.0 Display refresh rate in Hz
arm_ctrl_mode str "pos" Arm control mode (mit, pos, pose)
grip_ctrl_mode str "pos" Gripper control mode (mit, pos)
required bool False Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {robot_name}/*
robot_name str "robot_firefly_y6" Robot node to subscribe/publish to

Hello Y6

from hex_flow_core import LaunchConfig
from hex_flow_node_robot import (
    default_robot_hello_y6_node,
    default_robot_hello_y6_test_node,
)

config = LaunchConfig(
    local_only=True,
    enable_tui=True,
    log_to_file=True,
    save_path="/tmp/robot_hello_y6.launch.yml",
)

nodes = {
    "robot_hello_y6":
    default_robot_hello_y6_node(
        name="robot_hello_y6",
        host="172.18.8.96",
        port=9439,
        ctrl_rate=500,
        state_buffer_size=200,
        sens_ts=True,
        led_buffer_size=10,
        required=True,
        hidden=True,
        remap_dict={
            "arm_state": "robot_hello_y6/arm_state",
            "grip_joy": "robot_hello_y6/grip_joy",
            "grip_led_ctrl": "robot_hello_y6/grip_led_ctrl",
        },
    ),
    "test_hello_y6":
    default_robot_hello_y6_test_node(
        name="test_hello_y6",
        rate_hz=10,
        required=False,
        remap_dict={
            "arm_state": "robot_hello_y6/arm_state",
            "grip_joy": "robot_hello_y6/grip_joy",
            "grip_led_ctrl": "robot_hello_y6/grip_led_ctrl",
        },
    ),
}

config.set_nodes(nodes)
print(config.export())

default_robot_hello_y6_node

Parameter Type Default Description
name str "robot_hello_y6" Node name and remap prefix
host str "192.168.1.100" Robot IP address
port int 8439 Robot UDP port
ctrl_rate float 500.0 Control loop rate in Hz
state_buffer_size int 200 Internal state buffer size
sens_ts bool True Use sensor timestamps in published messages
led_buffer_size int 10 LED command buffer size
required bool True Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {name}/*

default_robot_hello_y6_test_node

Parameter Type Default Description
name str "test_hello_y6" Node name
rate_hz float 10.0 Display refresh rate in Hz
required bool False Required for launch
hidden bool False Hidden node
remap_dict dict None Custom remap; defaults to {robot_name}/*
robot_name str "robot_hello_y6" Robot node to subscribe/publish to

💡 Examples

Ready-to-run config scripts are provided in the example/ directory. Each script prints a launch YAML to stdout, intended for use with hexflow run:

Archer Y6

# 500 Hz control loop, 10 Hz test display
hexflow run example/archer_y6_test.launch.py

Firefly Y6

# 500 Hz control loop, 10 Hz test display
hexflow run example/firefly_y6_test.launch.py

Hello Y6

# 500 Hz control loop, 10 Hz test display
hexflow run example/hello_y6_test.launch.py

YAML Examples

Archer Y6 (500 Hz)

nodes:
  - name: robot_archer_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-archer-y6
    required: true
    hidden: true
    remap:
      arm_state: robot_archer_y6/arm_state
      grip_state: robot_archer_y6/grip_state
      arm_ctrl: robot_archer_y6/arm_ctrl
      grip_ctrl: robot_archer_y6/grip_ctrl
    env:
      HOST: "172.18.23.197"
      PORT: "8439"
      CTRL_RATE: "500"
      STATE_BUFFER_SIZE: "200"
      SEN_TS: "True"
      GRIP_TYPE: "gp80"
      POSE_END_IN_FLANGE: "0.187,0.0,0.0,1.0,0.0,0.0,0.0"

  - name: test_archer_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-archer-y6-test
    required: false
    remap:
      arm_state: robot_archer_y6/arm_state
      grip_state: robot_archer_y6/grip_state
      arm_ctrl: robot_archer_y6/arm_ctrl
      grip_ctrl: robot_archer_y6/grip_ctrl
    env:
      RATE_HZ: "10"
      ARM_CTRL_MODE: "pos"
      GRIP_CTRL_MODE: "pos"

Firefly Y6 (500 Hz)

nodes:
  - name: robot_firefly_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-firefly-y6
    required: true
    hidden: true
    remap:
      arm_state: robot_firefly_y6/arm_state
      grip_state: robot_firefly_y6/grip_state
      arm_ctrl: robot_firefly_y6/arm_ctrl
      grip_ctrl: robot_firefly_y6/grip_ctrl
    env:
      HOST: "172.18.23.197"
      PORT: "8439"
      CTRL_RATE: "500"
      STATE_BUFFER_SIZE: "200"
      SEN_TS: "True"
      GRIP_TYPE: "gp80"
      POSE_END_IN_FLANGE: "0.187,0.0,0.0,1.0,0.0,0.0,0.0"

  - name: test_firefly_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-firefly-y6-test
    required: false
    remap:
      arm_state: robot_firefly_y6/arm_state
      grip_state: robot_firefly_y6/grip_state
      arm_ctrl: robot_firefly_y6/arm_ctrl
      grip_ctrl: robot_firefly_y6/grip_ctrl
    env:
      RATE_HZ: "10"
      ARM_CTRL_MODE: "pos"
      GRIP_CTRL_MODE: "pos"

Hello Y6 (500 Hz)

nodes:
  - name: robot_hello_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-hello-y6
    required: true
    hidden: true
    remap:
      arm_state: robot_hello_y6/arm_state
      grip_joy: robot_hello_y6/grip_joy
      grip_led_ctrl: robot_hello_y6/grip_led_ctrl
    env:
      HOST: "172.18.8.96"
      PORT: "9439"
      CTRL_RATE: "500"
      STATE_BUFFER_SIZE: "200"
      SEN_TS: "True"
      LED_BUFFER_SIZE: "10"

  - name: test_hello_y6
    build: pip install hex_flow_node_robot
    run: hex-flow-robot-hello-y6-test
    required: false
    remap:
      arm_state: robot_hello_y6/arm_state
      grip_joy: robot_hello_y6/grip_joy
      grip_led_ctrl: robot_hello_y6/grip_led_ctrl
    env:
      RATE_HZ: "10"

Message Types (FlatBuffer)

All topics use FlatBuffer messages from hex_util_msg.msg_robot.

arm_stateHexArmState

Field Type Description
ts_ns int64 Timestamp in nanoseconds
jnt_pos float64 Joint positions (rad)
jnt_vel float64 Joint velocities (rad/s)
jnt_eff float64 Joint efforts (Nm) — Archer Y6 / Firefly Y6
pose_pos float64 End-effector position (m) — Archer Y6 / Firefly Y6
pose_quat float64 End-effector orientation (quaternion) — Archer Y6 / Firefly Y6

Note: For Hello Y6, the published arm_state contains only jnt_pos and jnt_vel; jnt_eff, pose_pos, and pose_quat are omitted.

Schema: msgs/msg_robot/arm_state.fbs

grip_stateHexGripState

Field Type Description
ts_ns int64 Timestamp in nanoseconds
jnt_pos float64 Gripper joint position
jnt_vel float64 Gripper joint velocity
jnt_eff float64 Gripper joint effort

Schema: msgs/msg_robot/grip_state.fbs

arm_ctrlHexArmCtrl

Field Type Description
ts_ns int64 Timestamp in nanoseconds
ctrl_mode uint8 Control mode enum (mit, comp, pos, pose, pos_plan, pose_plan)
jnt_pos float64 Target joint positions
jnt_vel float64 Target joint velocities
pose_pos float64 Target end-effector position
pose_quat float64 Target end-effector orientation
mit_tau float64 Feed-forward torque (MIT mode)
mit_kp float64 Stiffness gains
mit_kd float64 Damping gains
lim_err float64 Position error limit for safety

Schema: msgs/msg_robot/arm_ctrl.fbs

grip_ctrlHexGripCtrl

Field Type Description
ts_ns int64 Timestamp in nanoseconds
ctrl_mode uint8 Control mode enum (mit, comp, pos, force)
jnt_pos float64 Target gripper position
jnt_vel float64 Target gripper velocity
mit_tau float64 Feed-forward torque (MIT mode)
mit_kp float64 Stiffness gains
mit_kd float64 Damping gains
lim_err float64 Position error limit for safety

Schema: msgs/msg_robot/grip_ctrl.fbs

grip_led_ctrlHexRgbCtrl

Field Type Description
ts_ns int64 Timestamp in nanoseconds
r uint8 Red channel LED values
g uint8 Green channel LED values
b uint8 Blue channel LED values

Schema: msgs/msg_robot/rgb_ctrl.fbs

grip_joyHexTeleopHello

Field Type Description
ts_ns int64 Timestamp in nanoseconds
trigger float64 Trigger analog value
axis_x float64 Joystick X-axis
axis_y float64 Joystick Y-axis
btn_w bool Button W state
btn_x bool Button X state
btn_y bool Button Y state
btn_z bool Button Z state

Schema: msgs/msg_teleop/teleop_hello.fbs

Environment Variables

All Nodes

Variable Type Default Description
HEX_FLOW_NODE_NAME str constructor arg Overrides node name (handled by hex_flow_core)
HEX_FLOW_REMAP str {} JSON dict for topic remapping (handled by hex_flow_core)
RUST_LOG str info Log level for envlog

Robot Driver Nodes (archer-y6 / firefly-y6 / hello-y6)

Variable Type Default Description
HOST str "192.168.1.100" Robot IP address
PORT int 8439 Robot UDP port
CTRL_RATE float 500.0 Control loop rate in Hz
STATE_BUFFER_SIZE int 200 Internal state buffer size
SEN_TS bool True Use sensor timestamps in published messages

Archer Y6 / Firefly Y6 Only

Variable Type Default Description
GRIP_TYPE str "gp80" Gripper model type
POSE_END_IN_FLANGE str "0.187,0.0,0.0,1.0,0.0,0.0,0.0" End-effector pose in flange frame

Hello Y6 Only

Variable Type Default Description
LED_BUFFER_SIZE int 10 LED command buffer size

Test Nodes (archer-y6-test / firefly-y6-test / hello-y6-test)

Variable Type Default Description
RATE_HZ float 10.0 Display refresh rate in Hz

Archer Y6 / Firefly Y6 Test Only

Variable Type Default Description
ARM_CTRL_MODE str "pos" Arm control mode (mit, pos, pose)
GRIP_CTRL_MODE str "pos" Gripper control mode (mit, pos)

Architecture

Both Archer Y6 and Firefly Y6 driver nodes follow the same pattern; Hello Y6 is a simplified variant:

  1. Parameter construction — reads environment variables and builds a HexRobot*Params object specific to the robot model.
  2. Callback registration — creates a NodeCallback node and registers two robot state callbacks: robot_arm_state_cb (invoked on arm state update) and robot_grip_state_cb / robot_grip_joy_cb (invoked on gripper/joy state update). Each callback builds the corresponding FlatBuffer message and publishes it via node.pub().
  3. Control subscription — subscribes to arm_ctrl and grip_ctrl (or grip_led_ctrl for Hello Y6) topics. Received samples are parsed from FlatBuffer and dispatched by control mode to the appropriate driver command method.
  4. Rate-controlled background loop — the hardware driver (HexRobot*Callback) runs its own control loop at the configured rate. The node's main loop simply sleeps and checks for shutdown.

Test nodes use the polling-style Node API (via node.get(topic, latest=True)) instead of callbacks, subscribing to state topics and publishing control commands at a lower refresh rate (default 10 Hz).

This architecture decouples the robot hardware interface from application logic, allowing control nodes to run on separate machines connected via Zenoh.

📄 License

Apache License 2.0. See LICENSE.

🌟 Star History

Star History Chart

👥 Contributors

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hex_flow_node_robot-0.0.1.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hex_flow_node_robot-0.0.1-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

Details for the file hex_flow_node_robot-0.0.1.tar.gz.

File metadata

  • Download URL: hex_flow_node_robot-0.0.1.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for hex_flow_node_robot-0.0.1.tar.gz
Algorithm Hash digest
SHA256 a04a50353a7589550e1c698ffc6267e224520c27327cf803c065e432c54efb9e
MD5 36aa97b053dbb5925f6e8fee8e1dcbd6
BLAKE2b-256 b8642492ef5c7e337502f4ec3bb61cf76984c14ffa53618da66983f3deb79733

See more details on using hashes here.

File details

Details for the file hex_flow_node_robot-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for hex_flow_node_robot-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c30fe3926720dffe223795a3ccee0f8764312815e5bed8bada520cadcd0d8d11
MD5 fd0fb93c86a2bf146ec08f65c45400e7
BLAKE2b-256 bdc0261a1a3ae3b723a216de12550b9648b2ed1b255d006a2b9224bd09f9035f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page