Skip to content

gRPC Example (MPC)

This guide targets traditional-control (MPC) mode. It explains how to obtain the gRPC client project, set up the environment, and control the robot via the interactive CLI (Python / C++) or programmatic integration. The MPC client lives in the repository's mpc_client/ directory; the service port is fixed at 6666 and the package name is adam_control.


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 ← this guide
│   ├── 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
│   ├── 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 mpc_client/. Its proto/adam_control.proto is the single source of truth for the interface; after any protocol change, run ./build.sh to regenerate the C++ (include/) and Python (python/) stubs without breaking compatibility. For the reinforcement-learning (RL) client, see gRPC Example (RL).


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

Only grpcio is required at runtime; grpcio-tools is used by build.sh to regenerate stubs:

pip install -r mpc_client/python/requirements.txt
# requirements.txt: grpcio>=1.46.0  grpcio-tools>=1.46.0
sudo apt-get install -y protobuf-compiler libprotobuf-dev
sudo apt-get install -y nlohmann-json3-dev
# gRPC (grpc++) usually needs to be built from source (v1.46.3 recommended), or use a system/third-party prebuilt package

Network

Connect your computer and the robot on the same subnet. See Quick Development (Real) for network configuration. The service gRPC port is fixed at 6666.


Configuring the Server IP

Unlike the RL client (which takes --addr), the MPC client reads mpc_client/ip_config.json at startup:

{
  "server": {
    "ip": "192.168.x.x",
    "port": 6666,
    "comment": "ip = robot NIC IP; port defaults to 6666 and rarely needs changing"
  }
}

Configuration notes

  • After changing the IP, the C++ client does not need to be recompiled (the config is read at runtime).
  • The service port is fixed at 6666 and should match the gRPC client connect address printed in the robot's PndControl startup log.
  • Placeholder IPs (xx.xx.xx.xx, 0.0.0.0, empty) are rejected at startup with a prompt to configure.

Pre-Connection Checklist

  1. The robot's PndControl is running and was built with gRPC enabled (-Dgrpc_on=true in buildrobot.sh).
  2. install.sh has been run and systemctl restart pnd_adam_dds.service applied so the new binary takes effect.
  3. The IP in ip_config.json matches the gRPC address printed in the console.
  4. The client machine can reach port 6666 on the robot (not blocked by a firewall).

Build and Run

cd mpc_client
chmod +x build.sh run.sh clean.sh

./build.sh        # Generate C++/Python stubs + cmake build → bin/adam_command_client
./run.sh          # Start the Python client by default
./run.sh python   # Same as above
./run.sh bin      # Start the C++ client (build first)
./clean.sh        # Remove build/, bin/, lib/; keep stubs in include/ and python/

Or run directly:

cd mpc_client/python && python3 adam_command_client.py
cd mpc_client/bin && ./adam_command_client

build.sh generates stubs from proto/adam_control.proto and builds with cmake under build/. If your local protoc differs from the bundled generated files, rerun ./build.sh to overwrite the local stubs.


Interactive CLI Example

Both the Python and C++ clients provide a REPL: type help for commands and exit to go back / quit. The client enables commands dynamically based on the robot's current FSM state; the Python client additionally supports Tab completion and is case-insensitive.

$ ./run.sh python
Running Python client: .../mpc_client/python/adam_command_client.py
厞čŋžæŽĨ到 gRPC æœåŠĄ: 192.168.31.76:6666
Adam Command Client v1.1.0
Type 'help' for usage information.

> help
Available commands:
  SetMode
  SetStandMotion
  SetStandCarryBox
  SetStandAction
  SetStandDynamic
  SetSpeed
  AutoUnigaitCOM
  SetErrorClear
  GetStandList
  GetRobotState
  SetControlMode  <0=Traditional|1=RL>  -- switch control paradigm
  GetControlState                       -- query current control mode from DDS rt/control_mode_state
  clear
  exit

> GetRobotState
Current Mode: Start
Current Motion:
Enable Mode List: ['Zero']
Enable Motion List: []
Enable Action List: []
Enable Carry Box List: []
Stand Pitch: 0.0
Stand Roll: 0.0
Stand Yaw: 0.0
Stand Height: 0.0
X Velocity: 0.0
Y Velocity: 0.0
Yaw Velocity: 0.0
Balance Control State: False
Control Mode: 0 (Traditional)

> SetMode
Available Modes:  Zero
Enter parameter for Setmode: Zero
Success: Mode set successfully

> SetMode
Available Modes:  Stand
Enter parameter for Setmode: Stand
Success: Mode set successfully

> SetStandMotion
Available Motions:  Greeting, Chest Expansion, Stretching, Gentleman's Salute
Enter parameter for Setstandmotion: Greeting
Success: Motion executed successfully

> SetStandAction
Current action values (stand_pitch stand_roll stand_yaw base_height) (0.0, 0.0, 0.0, 0.0)
Please enter values within the following ranges:
  - Pitch: [-0.1, 0.1]
  - Roll: [-0.06, 0.06]
  - Yaw: [-0.25, 0.25]
  - Base Height: [-0.2, 0.0]
Action> Enter action values (stand_pitch stand_roll stand_yaw stand_height): 0.0 0.0 0.0 -0.1
Success: Action executed successfully

> GetControlState
Control State: domain_id=0 (Traditional)

> SetControlMode
Current control mode: 0 (Traditional)
Enter control mode (0/1): 1
  Waiting for hardware to switch to RL (timeout=30s, poll every 0.5s)...
  [  0.0s] Current mode: 0 (Traditional)
  [  2.5s] Current mode: 1 (RL)
Success: Switched to RL successfully.

> exit
Exiting Adam Command Client.

CLI Command Reference

Command RPC Mode Description
SetMode SetMode per mode_enable_list Switch Start/Zero/Stand/Walk/Run/Stop, etc.
SetStandMotion SetStandMotion Stand Predefined motion (e.g. Greeting)
SetStandCarryBox SetStandCarryBox Stand Box-carrying action (1 squat / 2 stand / 0 place)
SetStandAction SetStandAction Stand Posture pitch/roll/yaw + squat height
SetStandDynamic SetStandDynamic Stand Toggle dynamic balance
SetSpeed SetSpeed Walk / Run Set x/y/yaw speed (mind safety)
AutoUnigaitCOM AutoUnigaitCOM Walk / Run COM X-direction offset balance
SetErrorClear SetErrorClear Stop Clear driver errors without power cycling
GetStandList GetStandList any Get fixed motion/mode lists
GetRobotState GetRobotState any Get current state and the *_enable_lists
SetControlMode SetControlMode any Switch paradigm 0=Traditional / 1=RL
GetControlState GetControlState any Query the current control paradigm

Query state before commanding

Run GetRobotState before issuing mode/action commands; a target mode/action can only be executed when it appears in the corresponding *_enable_list. Commands outside the list are rejected by the client or server.


Typical Control Flow

Start → Zero → Stand â”Ŧ─ SetStandMotion / SetStandAction / SetStandCarryBox / SetStandDynamic
                     └─ Walk / Run → SetSpeed / AutoUnigaitCOM
(on error) Stop → SetErrorClear

Steps:

  1. GetRobotState — confirm the current FSM and mode_enable_list
  2. SetMode → Zero — homing
  3. SetMode → Stand — enter standing
  4. In Stand, execute actions, e.g. SetStandMotion (Greeting) or SetStandAction (posture / squat height)
  5. To walk: SetMode → Walk / Run, then set speed with SetSpeed
  6. If a driver error occurs: SetMode → Stop, then SetErrorClear

Safety

  • SetSpeed carries safety risk; in production prefer gamepad teleoperation — the sample client is for interface demonstration only.
  • SetStandAction pitch/roll/yaw and squat height must be within the allowed ranges (below); out-of-range values are rejected.
  • Ensure the robot is safely suspended or in a clear area before operating, and follow the Operations Guide.

SetStandAction Posture Ranges

Parameter Range
Pitch [-0.1, 0.1]
Roll [-0.06, 0.06]
Yaw [-0.25, 0.25]
Base Height [-0.2, 0.0]

Programmatic Integration

The interactive CLI is for manual debugging. To integrate into your own program, use the wrappers below.

Python

Use the stub directly:

import grpc
import adam_control_pb2 as pb2
import adam_control_pb2_grpc as pb2_grpc

channel = grpc.insecure_channel("192.168.1.100:6666")
stub = pb2_grpc.RobotControlStub(channel)

# Switch mode
r = stub.SetMode(pb2.SetModeRequest(mode="Stand"))
print(r.success, r.message)

# Query state and enable lists
s = stub.GetRobotState(pb2.GetRobotStateRequest(get_state_flag=True))
print(s.fsm_name, list(s.mode_enable_list))

# Execute actions in Stand
stub.SetStandMotion(pb2.SetStandMotionRequest(motion="Greeting"))
stub.SetStandAction(pb2.SetActionRequest(
    stand_pitch=0.0, stand_roll=0.0, stand_yaw=0.0, stand_height=-0.1))

# Switch control paradigm and read back
stub.SetControlMode(pb2.SetControlModeRequest(domain_id=0))
ctrl = stub.GetControlState(pb2.GetControlStateRequest())
print(ctrl.domain_id)  # 0=Traditional, 1=RL

Or reuse the AdamCommand wrapper methods from the sample client adam_command_client.py:

ok, msg = client.set_mode("Stand")
ok, msg = client.set_stand_motion("Greeting")
ok, msg = client.set_stand_action(0.0, 0.0, 0.0, -0.1)
ok, cur_mode, msg = client.set_control_mode(0)             # queue the switch
ok, final_mode, msg = client.wait_for_control_mode(0, timeout_sec=30)  # poll to confirm
client.get_control_state()

C++

#include "adam_command.h"

adam_control::AdamCommand client("192.168.1.100:6666");

std::string message;
bool ok = client.SetMode("Stand", message);

// Switch the control paradigm and poll hardware to confirm (30s timeout by default)
int final_mode = -1;
ok = client.SetControlModeAndWait(0, final_mode, message);

int domain_id = -1;
ok = client.GetControlState(domain_id, message);

src/adam_command.cpp can be built as libadam_command and linked into your own project (see src/CMakeLists.txt). adam_command.h also provides async APIs (e.g. SetModeAsync) for non-blocking integration.


Code Walkthrough

Core logic in mpc_client/python/adam_command_client.py:

  1. Config loading: Reads ip / port from ../ip_config.json to build the server address; placeholder IPs are rejected with a prompt.
  2. Connection check: Before connecting, uses grpc.channel_ready_future with a 5s timeout and prints troubleshooting hints on failure (PndControl running? port reachable?).
  3. State awareness: Calls GetRobotState before each command to refresh fsm_name and the *_enable_lists, enabling commands and Tab completion accordingly — action commands only in Stand, speed commands only in Walk/Run, SetErrorClear only in Stop.
  4. State-machine input: Commands enter a parameter-input state (WAIT_PARAMETER / WAIT_SPEED_INPUT / WAIT_ACTION_INPUT) with enum and numeric-range validation.
  5. Control-paradigm switch: After SetControlMode publishes the DDS rt/control_mode_cmd, it polls GetControlState (30s by default) to confirm the hardware switch, matching the C++ client.

FAQ

Symptom Likely cause Suggestion
failed to connect to all addresses Wrong IP/port or gRPC not enabled Check ip_config.json, PndControl log, firewall, port 6666 reachability
this command can only be executed when mode is 'Stand' Not switched to the required mode first SetMode to Stand (or Walk/Run) first
Invalid parameter / command outside enable_list Current state does not allow that mode/action Run GetRobotState to view the *_enable_lists
SetControlMode timeout Hardware switch is slow Keep confirming with GetControlState; check the DDS service
ModuleNotFoundError: grpc Python dependency missing pip install -r mpc_client/python/requirements.txt
C++ build cannot find nlohmann_json dev package missing sudo apt install nlohmann-json3-dev

Document Description
MPC gRPC Interface Traditional-control (MPC) gRPC API definitions and parameters
gRPC Example (RL) RL-mode client acquisition and interactive example
Arm Control Example DDS real-time upper-limb control
Dexterous Hand Control DDS finger control