Skip to main content

Hex Flow Node for Teleoperation Input (Keyboard & Joystick)

Project description

HEX FLOW NODE TELEOP


Overview

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.

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:

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

Install from PyPI

pip install hex_flow_node_teleop

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_config.py

Joystick

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

YAML Examples

Keyboard (100 Hz)

nodes:
  - name: teleop_keyboard
    build: pip install hex_flow_node_teleop
    run: hex-flow-teleop-keyboard
    required: 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
    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
    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
    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.

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.1a5.tar.gz (15.9 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.1a5-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hex_flow_node_teleop-0.0.1a5.tar.gz
  • Upload date:
  • Size: 15.9 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.1a5.tar.gz
Algorithm Hash digest
SHA256 37bd501fd80dce517a67583bb7984c9c837071690ae88bef87cd8f65c828b048
MD5 c6658aeed387e6ac877e30581020f067
BLAKE2b-256 bf93604c5279ebca6904ca2263b7f8fcda4afc5fac62da7f11b401f45659832b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for hex_flow_node_teleop-0.0.1a5-py3-none-any.whl
Algorithm Hash digest
SHA256 d249f13d0083e08108ffc5852a5537ae36136af5b240d731fa887884b07c2864
MD5 499d208e0e4f6672c5cee0b3ce99bf04
BLAKE2b-256 0729c5abfed11789d189089df7ab9da01176eb3c69dc18bf5fa595b97d2cee8b

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