← robot.smithjack.net  /  how it works

TopicFS — How It Works

the same four tasks — bash vs Python — with and without ROS2 on the host
ROS2 normally requires compiled message typesupport libraries for every node that touches a topic. There is no schema on the wire — you can't just "read" a topic without a full ROS2 toolchain installed. TopicFS solves this by running one ROS2-aware node inside a container and projecting all topic data outward as ordinary POSIX files. The host needs no ROS2 installation whatsoever. A PHP script, a shell script, or any program that can open a file can read live robot data or send commands.
~12 lines — TopicFS bash
~60 lines — traditional Python
0 ROS2 pkgs on host
any language, no bindings
TopicFS approach bash — 12 lines
# 1. Read live arm position
cat /mnt/topicfs/swiftpro/position/latest
{"x":226.6,"y":-2.8,"z":19.9}

# 2. Send a move command
echo '{"j1":90,"j2":45,"j3":45,"speed":5000,"wait":false}' \
  > /mnt/topicfs/swiftpro/set_servo_angles/command

# 3. Read the response
cat /mnt/topicfs/swiftpro/set_servo_angles/response
{"success":true}

# 4. Reset the arm
echo '{}' > /mnt/topicfs/swiftpro/reset/command
cat /mnt/topicfs/swiftpro/reset/response
{"success":true}
what you need on the host
  • any language that can open a file
  • zero ROS2 packages installed
  • zero compiled message libraries
  • zero knowledge of DDS or topic types
  • one running container (TopicFS)
Traditional ROS2 approach Python — ~60 lines
# Requires: ROS2 installed, sourced,
# swiftpro_resources pkg built + installed

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Point
from std_msgs.msg import Bool
from swiftpro_resources.srv import SetServoAngles, Reset

class ArmController(Node):
  def __init__(self):
    super().__init__('arm_controller')
    # 1. Subscribe to position topic
    self.position = None
    self.create_subscription(
      Point, '/swiftpro/position',
      lambda msg: setattr(self, 'position', msg),
      10
    )
    # 2+3. Create service client for move
    self.move_cli = self.create_client(
      SetServoAngles, '/swiftpro/set_servo_angles'
    )
    while not self.move_cli.wait_for_service(
      timeout_sec=1.0):
      self.get_logger().info('waiting for move...')
    # 4. Create service client for reset
    self.reset_cli = self.create_client(
      Reset, '/swiftpro/reset'
    )
    while not self.reset_cli.wait_for_service(
      timeout_sec=1.0):
      self.get_logger().info('waiting for reset...')

  def get_position(self):
    # spin until first message received
    while self.position is None:
      rclpy.spin_once(self, timeout_sec=0.1)
    return self.position  # Point(x,y,z)

  def move(self, j1, j2, j3):
    req = SetServoAngles.Request()
    req.j1, req.j2, req.j3 = j1, j2, j3
    req.speed = 5000; req.wait = False
    future = self.move_cli.call_async(req)
    rclpy.spin_until_future_complete(self, future)
    return future.result().success  # bool

  def reset(self):
    req = Reset.Request()
    future = self.reset_cli.call_async(req)
    rclpy.spin_until_future_complete(self, future)
    return future.result().success  # bool

rclpy.init()
node = ArmController()
pos = node.get_position()   # 1. position
ok  = node.move(90,45,45)    # 2. move
print(ok)                     # 3. response
ok  = node.reset()           # 4. reset
print(ok)
what you need on the host
  • ROS2 Jazzy fully installed + sourced
  • swiftpro_resources package built
  • compiled message typesupport .so files
  • colcon build system
  • understanding of DDS, QoS, spin loops
  • Python rclpy bindings
  • service client lifecycle management
architecture
TopicFS stack on worker
UArm Swift Pro (USB)
swiftros2_runtime container
ROS2 topics + services
topicfs_runtime container
FUSE mount at /mnt/topicfs/
Apache + PHP
robot.smithjack.net
your browser
source code