Virtual Robot Node Template (Arduino-style)

This template mirrors the Arduino programming model—where setup() runs once and loop() runs every cycle—but implemented in Python for a virtual robot.

  • setup(): one-time initialization.
  • update(): called each iteration (the “loop”).

Timing: The simulation backend tops out around 50 Hz, so the loop sleep is set to 0.02 seconds (20 ms).

Core Class: VRobotNode

Implements the Arduino-like lifecycle using VRobotNodeBase.

from ubicoders_vrobots_ipc.vrobot_node import VRobotNodeBase, vrobot_client_runner

class VRobotNode(VRobotNodeBase):
    def __init__(self, sysId: int = 0):
        super().__init__(sysId)

    # ======================================================
    # Arduino-like structure: setup() and update() (loop)

    # Called once at the beginning
    def setup(self):
        pass

    # Called every iteration
    def update(self):
        if self.read_new_states():
            ts = self.state.timestamp  # Unix time in millis
            pos = self.state.linPos
            elapsed_sec = (ts - self.first_ts) / 1000.0  # seconds
            print(f"State t={elapsed_sec:.3f}s pos=({pos.x:.3f},{pos.y:.3f},{pos.z:.3f})")
    # ======================================================

if __name__ == "__main__":
    vrobot_client_runner([VRobotNode(sysId=0)])

Main Loop: vrobot_client_runner()

Accepts a list of nodes for multi-robot (fleet/swarm) control.

import time
from typing import Any, List
from ubicoders_vrobots_ipc.vrobot_client import VRobotClient  # adjust import if needed

def vrobot_client_runner(vrobot_nodes: List[Any]):
    vrclient = VRobotClient()
    for node in vrobot_nodes:
        vrclient.add_vrobot_node(node)
    try:
        while True:
            vrclient.update()
            time.sleep(0.02)  # ~50 Hz (0.02 s = 20 ms)
    finally:
        print("Shutting down...")
        vrclient.shutdown()

Why 0.02 s? The game engine behind the simulator typically won’t exceed 50 Hz in the best case. Keeping the sleep at 0.02 seconds keeps your node in sync and avoids wasted CPU.

Example: One-Time Setup

Use setup() to create reusable state and register subscriptions only once.

import cv2

class VRobotNode(VRobotNodeBase):
    def __init__(self, sysId: int = 0):
        super().__init__(sysId)

    def setup(self):
        # Example goal for a rover (x, y, z)
        self.position_setpoint = [1.0, 2.0, 3.0]

        # Configure a resizable OpenCV window for the left camera feed
        cv2.namedWindow("CamLeft", cv2.WINDOW_NORMAL)
        cv2.resizeWindow("CamLeft", 640, 360)

        # Subscribe once to the left camera stream published by this vrobot
        self.register_img_subscriber("left")

    def update(self):
        if self.read_new_states():
            # Control logic can reference self.position_setpoint here
            pass

Multi-Robot (Fleet) Example

Instantiate one node per robot with distinct sysIds. However, this does not instantiate the robot in the sim, to instantiate the robot, checkout service: ch1/hello_service.html Therefore, it is recommended to run the control script after defining your mission service.

if __name__ == "__main__":
    nodes = [
        VRobotNode(sysId=0),
        VRobotNode(sysId=1),
        VRobotNode(sysId=2),
    ]
    vrobot_client_runner(nodes)

Where do states and images come from?

The vrobot states data come from the topics published through Zenoh and Iceoryx2. Zenoh transport is used for robot states data. Iceoryx2 shared memory is used to publish the image data by the sim.

Listing topics...
  - [i] vr/0/cams/down/720p
  - [i] vr/0/cams/left/720p
  - [i] vr/0/cams/right/720p
  - [z] vr/0/states

Simply, the VRobotNode is subscribing the data specific to its sysId as the topics published by the sim follow the naming convention such as "vr/[sysId]/states"

Key Points

  • Lifecycle: setup() once → update() repeatedly.
  • Fleet-ready: Pass multiple nodes to vrobot_client_runner.
  • Timing: Use 0.02 s sleep (~50 Hz) for stable sim performance.
  • Setup scope: Put one-time work (targets, subscriptions, UI windows) in setup().