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:
đ 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:
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:
Option 1: Full Client Project (Recommended)
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__.pycomm/grpc/robot_control_pb2.pycomm/grpc/robot_control_pb2_grpc.py
Add the directory containing comm/ to PYTHONPATH, or use sys.path.insert:
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:
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.
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
Steps:
stateâ confirm FSM and switchable statesmode ZEROâ enter homingmode STAND_WALKâ pollstateuntilfsm_statebecomesSTAND_WALKmode MULTI_AGENTâ enter upper-body motion modemotion play Sources/motion/Greeting.txtâ play motion (file must exist underSources/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.
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:
- Path setup: Adds the
rl_client/root (tools/parent) tosys.pathforcomm.grpcimports. - State refresh: Calls
GetRobotStatebefore commands; caches FSM and action lists. - State-aware UI: Filters
helpand Tab completion byavailable_actions;modeTab usesswitchable_states. - Motion confirmation: After
motion play/stop, polls untilmotion_playingandcurrent_motion_fileupdate.
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 |