Skip to content

gRPC Example (RL)

This guide targets reinforcement-learning (RL) mode. It explains how to obtain the gRPC client project, set up the environment, and control the robot via the interactive CLI or programmatic integration. The RL client lives in the repository's rl_client/ directory; the service port is fixed at 50051 and the package name is pnd.robot.


Obtaining the Client

The full client project (Proto definitions, generated bindings, and sample scripts) will be published on GitHub:

git clone https://github.com/pndbotics/pnd_grpc.git
cd pnd_grpc

🔗 Repository: pnd_grpc

Project Layout

The repository ships both the traditional-control (MPC) and reinforcement-learning (RL) clients, located in mpc_client/ and rl_client/ respectively:

pnd_grpc/
├── mpc_client/             # Traditional-control (MPC) client
│   ├── proto/              # gRPC protocol definition (adam_control.proto, source of truth)
│   ├── include/            # protoc-generated C++ headers and sources
│   ├── src/                # C++ API wrapper + interactive CLI client
│   ├── python/             # Python interactive client + generated stubs
│   ├── ip_config.json      # Robot server IP / port configuration
│   ├── build.sh / run.sh / clean.sh
│   └── README.md           # Detailed MPC mode usage
│
├── rl_client/              # Reinforcement-learning (RL) mode client ← this guide
│   ├── comm/
│   │   ├── proto/          # gRPC protocol definition (robot_control.proto)
│   │   └── grpc/           # protoc-generated Python stubs
│   ├── tools/grpc_client.py# Interactive Python CLI sample
│   └── README.md           # Detailed RL mode usage
│
└── README.md               # Project overview

This guide covers only rl_client/. Its stubs in comm/grpc/ import messages via from comm.grpc import robot_control_pb2, so the rl_client/comm/grpc/ directory structure must be preserved. For the traditional-control (MPC) client, see MPC gRPC Interface.


Environment Setup

System Requirements

We recommend Ubuntu 22.04 x86_64 for client development. The client can run on any machine that can reach the robot over the network, separately from the control program.

Install Dependencies

Minimum client dependency:

pip install --user grpcio

Network

Connect your computer and the robot on the same subnet. See Quick Development (Real) for network configuration.


Deployment Options

Two ways to organize client files depending on your use case:

Clone pnd_grpc, enter rl_client/, and keep the default layout. tools/grpc_client.py automatically adds the rl_client/ root to sys.path — no import changes needed.

Best for: debugging, demos, and extending the sample scripts.

Option 2: Standalone API Files

To integrate gRPC calls into your own project, copy the following files from rl_client/:

  • comm/__init__.py
  • comm/grpc/robot_control_pb2.py
  • comm/grpc/robot_control_pb2_grpc.py

Add the directory containing comm/ to PYTHONPATH, or use sys.path.insert:

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

Best for: automation scripts, application integration, minimal deployments.


Starting the Client

Ensure the robot control program is running before connecting. Use --addr to specify the robot address:

Scenario Address
Local localhost:50051
Remote <robot IP>:50051
# Local (run from the repository root)
python3 rl_client/tools/grpc_client.py

# Remote (replace IP with actual address)
python3 rl_client/tools/grpc_client.py --addr 192.168.1.100:50051

On connection failure:

[ERROR] RPC failed: UNAVAILABLE - failed to connect to all addresses

Verify the control program is running and the network is reachable. The service port is fixed at 50051; do not change it.


Interactive CLI Example

rl_client/tools/grpc_client.py provides an interactive CLI with Tab completion, command history, and state-aware command filtering.

python3 rl_client/tools/grpc_client.py --addr 192.168.1.100:50051
  Connected: 192.168.1.100:50051  |  FSM: STOP

╔══════════════════════════════════════════╗
║   PND Robot Control Client v1.0          ║
║   Type 'help' for commands, Tab to complete ║
╚══════════════════════════════════════════╝

robot> help
  Current FSM: STOP

  Global commands:
    state              Query current robot state
    mode [STATE]       Switch FSM mode (Tab for options)
    controlmode <0|1>  Set control paradigm (0=Traditional, 1=RL)
    controlstate       Query control paradigm from DDS state
    shutdown           Shutdown controller
    clear              Clear screen
    quit / exit        Exit client

robot> state
  FSM State:         STOP
  Velocity:          vx=0.000  vy=0.000  vyaw=0.000
  Height:            0.000
  Motion File:       (none)
  Motion Playing:    False
  Tracking Motion:   (none)
  Tracking Playing:  False
  Switchable:        ZERO
  Available Actions: (none)

robot> mode ZERO
  [OK] ok  (current=STOP)

robot> mode STAND_WALK
  [OK] ok  (current=ZERO)
# Command accepted; the FSM only switches to STAND_WALK after homing completes

robot> mode MULTI_AGENT
  [OK] ok  (current=STAND_WALK)

robot> motion play Sources/motion/Greeting.txt
  [OK] ok  (file=Sources/motion/Greeting.txt, playing=True)
  Observed: motion_file=Sources/motion/Greeting.txt, playing=True

robot> motion stop
  [OK] ok
  Observed: motion_file=(none), playing=False

robot> controlmode 0
  [OK] ok, Traditional (0) queued for control_mode_cmd

robot> controlstate
  [OK] ok, current control mode Traditional  domain_id=0

robot> quit
  Bye.

CLI Command Reference

Command RPC Description
state GetRobotState Query and display current state
mode <STATE> SetMode Switch FSM; Tab completes from switchable_states
motion play <path> SetMotion(PLAY) Play upper-body motion file
motion stop SetMotion(STOP) Stop motion playback
tracking <path> SetTrackingMotion Switch trajectory file
controlmode <0\|1> SetControlMode Switch control paradigm
controlstate GetControlState Query control paradigm
shutdown Shutdown Shutdown controller

help and Tab completion filter commands based on available_actions.


Typical Control Flow

STOP → ZERO → STAND_WALK → MULTI_AGENT → motion play → motion stop

Steps:

  1. state — confirm FSM and switchable states
  2. mode ZERO — enter homing
  3. mode STAND_WALK — poll state until fsm_state becomes STAND_WALK
  4. mode MULTI_AGENT — enter upper-body motion mode
  5. motion play Sources/motion/Greeting.txt — play motion (file must exist under Sources/motion/ on the robot)

Motion file paths

Preset motions are under Sources/motion/, relative to the control program root /etc/pndbotics/pnd_adam_dds/. See Arm Motion Service Interface for the full list.

  1. motion stop — stop playback

Safety

Ensure the robot is safely suspended or in a clear area before mode switches and motion playback. Follow the Operations Guide.


Programmatic Integration

Wrapper Class

import grpc
from comm.grpc import robot_control_pb2 as pb2
from comm.grpc import robot_control_pb2_grpc as pb2_grpc


class PndRobotClient:
    def __init__(self, addr: str = "localhost:50051"):
        self._stub = pb2_grpc.RobotControlStub(grpc.insecure_channel(addr))

    def get_state(self):
        return self._stub.GetRobotState(pb2.GetRobotStateRequest())

    def set_mode(self, target_state: str):
        r = self._stub.SetMode(pb2.SetModeRequest(target_state=target_state))
        return r.success, r.message

    def play_motion(self, file_path: str):
        r = self._stub.SetMotion(pb2.SetMotionRequest(
            command=pb2.SetMotionRequest.PLAY, motion_file=file_path))
        return r.success, r.message

    def stop_motion(self):
        r = self._stub.SetMotion(pb2.SetMotionRequest(
            command=pb2.SetMotionRequest.STOP))
        return r.success, r.message

    def set_tracking_motion(self, file_path: str):
        r = self._stub.SetTrackingMotion(pb2.SetTrackingMotionRequest(motion_file=file_path))
        return r.success, r.message

    def set_control_mode(self, domain_id: int):
        r = self._stub.SetControlMode(pb2.SetControlModeRequest(domain_id=domain_id))
        return r.success, r.message

One-Shot Script

Execute a single command via command-line arguments, convenient for automation:

import argparse

def main():
    parser = argparse.ArgumentParser(description="PND Robot gRPC one-shot client")
    parser.add_argument("--addr", default="localhost:50051", help="robot address host:port")
    parser.add_argument("--state", action="store_true", help="query robot state")
    parser.add_argument("--mode", help="switch FSM state, e.g. ZERO / STAND_WALK")
    parser.add_argument("--motion-play", metavar="PATH", help="play upper-body motion file")
    parser.add_argument("--motion-stop", action="store_true", help="stop motion playback")
    parser.add_argument("--tracking", metavar="PATH", help="switch trajectory file")
    parser.add_argument("--control-mode", type=int, choices=[0, 1], help="0=Traditional, 1=RL")
    args = parser.parse_args()

    client = PndRobotClient(args.addr)

    if args.state:
        s = client.get_state()
        print(f"fsm_state={s.fsm_state}")
        print(f"switchable_states={list(s.switchable_states)}")
        print(f"available_actions={list(s.available_actions)}")
    if args.mode:
        print("set_mode:", client.set_mode(args.mode))
    if args.motion_play:
        print("play_motion:", client.play_motion(args.motion_play))
    if args.motion_stop:
        print("stop_motion:", client.stop_motion())
    if args.tracking:
        print("set_tracking_motion:", client.set_tracking_motion(args.tracking))
    if args.control_mode is not None:
        print("set_control_mode:", client.set_control_mode(args.control_mode))


if __name__ == "__main__":
    main()

Call Examples

# Query state
python3 my_client.py --addr 192.168.1.100:50051 --state

# Switch to ZERO (homing calibration)
python3 my_client.py --addr 192.168.1.100:50051 --mode ZERO

# Play a motion in the MULTI_AGENT state
python3 my_client.py --addr 192.168.1.100:50051 --motion-play Sources/motion/Greeting.txt
Argument Description Example
--addr Robot address host:port 192.168.1.100:50051
--state Query robot state (flag)
--mode Switch FSM state ZERO, STAND_WALK
--motion-play Play upper-body motion file Sources/motion/Greeting.txt
--motion-stop Stop motion playback (flag)
--tracking Switch trajectory file <trajectory_file_path>
--control-mode Switch control paradigm 0 or 1

Code Walkthrough

Core logic in rl_client/tools/grpc_client.py:

  1. Path setup: Adds the rl_client/ root (tools/ parent) to sys.path for comm.grpc imports.
  2. State refresh: Calls GetRobotState before commands; caches FSM and action lists.
  3. State-aware UI: Filters help and Tab completion by available_actions; mode Tab uses switchable_states.
  4. Motion confirmation: After motion play/stop, polls until motion_playing and current_motion_file update.

FAQ

Symptom Likely cause Suggestion
UNAVAILABLE - failed to connect Control program not running or network issue Verify control program and IP/subnet
SetMode fails Target not in switchable_states Run state first
SetMotion fails Not in MULTI_AGENT or file missing on robot Check FSM, available_actions, and file path
FSM stays ZERO after mode STAND_WALK Homing not finished Poll state until fsm_state changes