gogo-operator is an asyncio/Urwid operator for Motion Stack 2. It has no
rclpy dependency: robot discovery and messages are provided by either the
native Zenoh bridge or the ROS 2-compatible pyzeros bridge.
The operator discovers every */joint_read publisher, keeps the robot
namespace attached to its joints, monitors joint values and tip_pos IK
feedback, and routes commands back to the matching namespace. The shared
operator logic only sees Motion Stack types and BaseSub streams.
Install the workspace and start the default pyzeros backend:
pixi install
pixi run operatorUse native Zenoh instead:
pixi run operator-zenohUseful CLI options:
gogo-operator --help
gogo-operator --transport zenoh --timeout 8
gogo-operator --transport pyzeros --node-name my_operator
gogo-operator --no-keyboardThe terminal window is used for menus and selection. Held motion keys are read
from the separate Gorilla keyboard window so key-up events reliably stop
motion. Space stops every active command and Esc returns to the main menu.
Controls:
- Main menu:
Mmonitor,Rrobots,Jjoints,Wwheels,UIK. - Joint mode: hold
W/S;Zsends selected joints to zero. - Wheel mode: hold
W/Sfor forward/backward andA/Dfor turning. - IK mode: arrows move X/Y,
I/Kmove Z, andQ/E,W/S,A/Drotate around X, Y, and Z. Use the translation and rotation sliders by clicking or dragging with the mouse, or useLeft/Rightfor fine speed adjustments;Page Up/Page Downmakes larger adjustments, and the mouse wheel nudges the focused slider.
Python warnings and log messages generated while the interface is running are shown in the log box instead of being written over the terminal display.
The built-in app accepts an existing transport session, which lets another async application own the session lifecycle:
import asyncio_for_robotics as afor
from gogo_operator import build_app
@afor.scoped
async def main(zenoh_session):
app = build_app("zenoh", session=zenoh_session)
await app.run()For a new transport, subclass DynamicJointBridge and implement discovery plus
the four existing Motion Stack bridge hooks. OperatorController,
RobotRegistry, and OperatorApp can then be reused unchanged.
- Native Zenoh discovers
**/joint_readkeys and removes a robot when no samples arrive within--timeout. - pyzeros follows ROS 2 Zenoh liveliness tokens, including DELETE events, so an idle but still-live robot is not removed just because its joint values stop changing.
- Joint commands are filtered per namespace. If two selected namespaces expose the same joint name with conflicting directions, that joint is skipped and a warning is shown instead of sending an ambiguous command.