Skip to main content

Hex Flow Node for Teleoperation Input (Keyboard & Joystick)

Project description

HEX FLOW NODE TELEOP

      


📖 Overview

What is hex_flow_node_teleop

hex_flow_node_teleop provides hex-flow nodes for reading teleoperation inputs from keyboard and joystick/gamepad using evdev. Each node maintains an internal state table updated by background threads and publishes the full state at the configured rate (default 100 Hz) over Zenoh via hex_flow_core, using FlatBuffer messages from hex_util_msg.

What problem it solves

  • Hardware abstraction: Abstracts Linux input devices (/dev/input/*) into structured, rate-controlled teleoperation topics over Zenoh.
  • Rate-controlled publishing: Decouples variable-rate hardware events from fixed-rate publish output, ensuring consistent state updates regardless of actual input event frequency.
  • Flexible deployment: Supports device auto-detection, topic remapping, and environment variable configuration — no code changes needed to adapt to different hardware setups.
  • Integrated testing: Ships with terminal-based test nodes for validating keyboard and joystick state without additional tools.

Target users

  • Engineers integrating teleoperation input devices (keyboard, gamepad, joystick) into HEXFELLOW robot systems.
  • Researchers running remote teleoperation experiments with HEXFELLOW robots.
  • Developers building on the hex-flow framework who need a working teleop input layer.

Nodes

Node Description Publishes Subscribes
hex-flow-teleop-keyboard Keyboard letter-key state reader teleop_keyboard -
hex-flow-teleop-joystick Joystick/Gamepad button & axis state reader teleop_joy -
hex-flow-teleop-keyboard-test Terminal viewer for keyboard state - teleop_keyboard
hex-flow-teleop-joystick-test Terminal viewer for joystick state - teleop_joy

Prerequisites

Accessing /dev/input/* requires the current user to be in the input group:

sudo usermod -aG input $USER
# Log out and back in for the change to take effect

📦 Installation

Requirements

  • Python >= 3.10
  • OS: Ubuntu (or other Linux)
  • Core dependencies:
    • evdev >= 1.7.0
    • hex_flow_core >= 0.0.0, < 0.1.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-teleop from PyPI

pip install hex_flow_node_teleop

Install hex-flow-node-teleop 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_teleop.git
cd hex_flow_node_teleop
./venv.sh

📑 Python Config API

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

from hex_flow_core import LaunchConfig
from hex_flow_node_teleop import (
    default_teleop_keyboard_node,
    default_teleop_keyboard_test_node,
    default_teleop_joystick_node,
    default_teleop_joystick_test_node,
)

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

nodes = {
    "teleop_keyboard": default_teleop_keyboard_node(
        name="teleop_keyboard",
        device_path="",
        rate_hz=100.0,
        required=True,
        remap_dict={"teleop_keyboard": "teleop_keyboard/teleop_keyboard"},
    ),
    "test_keyboard": default_teleop_keyboard_test_node(
        name="test_keyboard",
        rate_hz=10.0,
        required=False,
        remap_dict={"teleop_keyboard": "teleop_keyboard/teleop_keyboard"},
    ),
    "teleop_joystick": default_teleop_joystick_node(
        name="teleop_joystick",
        device_path="",
        rate_hz=100.0,
        required=True,
        remap_dict={"teleop_joy": "teleop_joystick/teleop_joy"},
    ),
    "test_joystick": default_teleop_joystick_test_node(
        name="test_joystick",
        rate_hz=10.0,
        required=False,
        remap_dict={"teleop_joy": "teleop_joystick/teleop_joy"},
    ),
}

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

default_teleop_keyboard_node

Parameter Type Default Description
name str "teleop_keyboard" Node name and remap prefix
device_path str "" /dev/input/eventN or empty for auto-detect
rate_hz float 100.0 Publishing rate in Hz
required bool True Required for launch
remap_dict dict None Custom remap; defaults to {name}/teleop_keyboard

default_teleop_keyboard_test_node

Parameter Type Default Description
name str "test_keyboard" Node name
rate_hz float 10.0 Display refresh rate in Hz
required bool False Required for launch
remap_dict dict None Custom remap; defaults to {teleop_name}/teleop_keyboard
teleop_name str "teleop_keyboard" Keyboard teleop node to subscribe to

default_teleop_joystick_node

Parameter Type Default Description
name str "teleop_joystick" Node name and remap prefix
device_path str "" /dev/input/eventN or empty for auto-detect
rate_hz float 100.0 Publishing rate in Hz
required bool True Required for launch
remap_dict dict None Custom remap; defaults to {name}/teleop_joy

default_teleop_joystick_test_node

Parameter Type Default Description
name str "test_joystick" Node name
rate_hz float 10.0 Display refresh rate in Hz
required bool False Required for launch
remap_dict dict None Custom remap; defaults to {teleop_name}/teleop_joy
teleop_name str "teleop_joystick" Joystick teleop node to subscribe 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:

Keyboard

# Auto-detect device, 100 Hz publish, 10 Hz test display
hexflow run example/keyboard_test.launch.py

Joystick

# Auto-detect device, 100 Hz publish, 10 Hz test display
hexflow run example/joystick_test.launch.py

YAML Examples

Keyboard (100 Hz)

nodes:
  - name: teleop_keyboard
    build: pip install hex_flow_node_teleop
    run: hex-flow-teleop-keyboard
    required: true
    hidden: true
    remap:
      teleop_keyboard: teleop_keyboard/teleop_keyboard
    env:
      DEVICE_PATH: ""
      RATE_HZ: "100"

  - name: test_keyboard
    build: pip install hex_flow_node_teleop
    run: hex-flow-teleop-keyboard-test
    required: false
    hidden: true
    remap:
      teleop_keyboard: teleop_keyboard/teleop_keyboard
    env:
      RATE_HZ: "10"

Joystick / Gamepad (100 Hz)

nodes:
  - name: teleop_joystick
    build: pip install hex_flow_node_teleop
    run: hex-flow-teleop-joystick
    required: true
    hidden: true
    remap:
      teleop_joy: teleop_joystick/teleop_joy
    env:
      DEVICE_PATH: ""
      RATE_HZ: "100"

  - name: test_joystick
    build: pip install hex_flow_node_teleop
    run: hex-flow-teleop-joystick-test
    required: false
    hidden: true
    remap:
      teleop_joy: teleop_joystick/teleop_joy
    env:
      RATE_HZ: "10"

Message Types (FlatBuffer)

All topics use FlatBuffer messages from hex_util_msg.msg_teleop.

teleop_keyboardHexTeleopKeyboard

Field Type Description
ts_ns int64 Timestamp in nanoseconds
key_akey_z bool Per-letter key state (true = pressed)

Schema: msgs/msg_teleop/teleop_keyboard.fbs

teleop_joyHexTeleopJoy

Field Type Description
ts_ns int64 Timestamp in nanoseconds
btn_x, btn_y, btn_a, btn_b bool Face buttons
btn_lb, btn_rb bool Shoulder bumpers
btn_lt, btn_rt bool Trigger buttons
btn_lthumb, btn_rthumb bool Thumbstick clicks
axis_lx, axis_ly float64 Left stick axes (normalized to [-1, 1])
axis_rx, axis_ry float64 Right stick axes (normalized to [-1, 1])
axis_lt, axis_rt float64 Trigger analog values (normalized to [-1, 1])
hat_x, hat_y float64 D-pad / hat switch values

Schema: msgs/msg_teleop/teleop_joy.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

Teleop Nodes (keyboard / joystick)

Variable Type Default Description
DEVICE_PATH str "" (auto) Specific /dev/input/eventN path; empty = auto-detect
RATE_HZ float 100.0 Publishing rate in Hz

Test Nodes (test-keyboard / test-joystick)

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

Architecture

Both keyboard and joystick nodes follow the same pattern:

  1. Device discovery — enumerate /dev/input/* via evdev.list_devices() and filter by capabilities (letter keys for keyboard, analog axes for joystick).
  2. Background reader thread — a daemon thread continuously reads evdev events and updates an in-memory state table protected by a threading.Lock.
  3. FlatBuffer serialization — on each publish cycle, the node snapshots the current state, builds a FlatBuffer message (HexTeleopKeyboard or HexTeleopJoy) with a nanosecond timestamp, and publishes the binary payload via hex_flow_core.Node.pub().
  4. Rate-controlled publish loop — using HexRate from hex_util_runtime, the node publishes at the configured rate (default 100 Hz).

Test nodes subscribe via hex_flow_core.Node.create_sub() and poll with Node.get(topic, latest=True) at a lower rate (default 10 Hz), deserializing the FlatBuffer to refresh the terminal display.

This decouples the variable-rate hardware events from the fixed-rate publish output, ensuring consistent state updates regardless of actual input event frequency.

📄 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_teleop-0.0.1.tar.gz (17.7 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_teleop-0.0.1-py3-none-any.whl (17.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hex_flow_node_teleop-0.0.1.tar.gz
  • Upload date:
  • Size: 17.7 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_teleop-0.0.1.tar.gz
Algorithm Hash digest
SHA256 948a1c3fa50f7772dd59eba6387983f25cf17f73541345460c07aef50f2475fc
MD5 6c919264667bd69d27570817f5166295
BLAKE2b-256 830c3c63cb492a1236f8abbd323c979840ef6fa92d5f2122c9f8b782cefbf331

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for hex_flow_node_teleop-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 09aa678f74e45b56d1e10d4cafe9f2515120f72404165b7cfb72efdcc385aaf3
MD5 19df3c95d0d866341e135f84d751cbb8
BLAKE2b-256 edbe23812795640b4cb71340126370e36cefd1646e696e8ed10ab4bb4b8e43b0

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