Getting Started
Overview
This documentation provides comprehensive information on how to use the Robot class. The Robot class integrates all features and handles operations, making it easier for developers to control and interact with the robot through REST and WebSocket APIs.
Table of Contents
Introduction
The Robot class is designed to facilitate communication and control of a robot through REST and WebSocket APIs. This class encapsulates various features such as interaction, navigation, behavior control, grasping, and more, making it a powerful tool for robot management and automation.
Key Methods
connect(ip: str, api_key: str) -> BaseRobot.Connection: Connects to the WebSocket and REST server.subscribe(topic: str): Subscribes to a WebSocket topic.unsubscribe(topic: str): Unsubscribes from a WebSocket topic.publish(topic: str, data: str): Publishes data to a WebSocket topic.register_callback(topic: str, callback: Callable[[dict], None]): Registers a callback function for a WebSocket topic.
Robot Class Usage
Initialization
To use the Robot class, you need to initialize it.
from pymirokai.robot import Robot
robot = Robot()
Alternatively, you can have a robot object already connected with automatic disconnection at the end of a scope:
from pymirokai.robot import connect
async with connect(robot_ip, api_key) as robot:
# Example of code using the connected robot object
await robot.say("Hello world").completed()
# When the scope is finished, the robot object is disconnected
Connecting to the Robot
To connect to a robot with the appropriate API URLs and configuration parameters, use the connect function.
connection = robot.connect(robot_ip, api_key)
To wait for the connection to finish, you can do:
await connection.connected()
Disconnecting from the Robot
To disconnect from the robot and wait for the disconnection to be finished, use the cancel_and_complete() method on the connection object returned by robot.connect.
await connection.cancel_and_complete()
Sending Commands
The Robot class provides various methods to send commands to the robot. These methods return a Mission object that allows you to wait for the command to be started on the robot and to wait for the command to be finished. The Mission object also allows you to cancel the command.
Example Commands
Some commands return a Mission object:
mission = robot.go_to_relative(Coordinates(x=1.0, y=2.0, theta=0.0))
mission = robot.say("Hello, world!")
Some commands are simply getters/setters on the robot:
response = robot.get_detected_runes()
response = robot.set_sound_level(50)
response = robot.set_robot_max_velocity(50)
Here is an expanded and clearer section for your documentation that improves the mission tree visualization and provides deeper context on how the mission system works, including cancellation behavior and structural principles.
Mission Management & Nesting
The robot executes all skills as missions, and each mission has a unique ID. These missions can be nested when one mission launches another, and the hierarchy is preserved throughout execution. This allows for full traceability, automatic error propagation, and cancellation management.
At the top of the hierarchy is the client_id, which represents the origin of the command (e.g. local script, web UI, remote, etc.). All missions belong to a client session and are rooted from this ID.
How Mission Nesting Works
When you call a skill that itself launches sub-skills (via other mission calls), the resulting missions are nested. Each mission ID encodes its parentage using the pattern:
<skill_name>-<parent_mission_id>
This format makes mission ancestry traceable and readable, and allows the system to:
Propagate cancellation and completion status to sub-missions.
Create a full execution tree of robot behavior.
Example Mission Tree
Let’s say you create a skill hierarchy where:
skill_lvl1callsskill_lvl1.1andskill_lvl1.2skill_lvl1.1callsskill_lvl1.1.1andskill_lvl1.1.2
Here’s what the mission tree looks like if the client_id is b5f8493d:
b5f8493d (client session)
└── skill_lvl1-b5f8493d
├── skill_lvl1.1-skill_lvl1-b5f8493d
│ ├── skill_lvl1.1.1-skill_lvl1.1-skill_lvl1-b5f8493d
│ └── skill_lvl1.1.2-skill_lvl1.1-skill_lvl1-b5f8493d
└── skill_lvl1.2-skill_lvl1-b5f8493d
This structure reveals:
Parent-child relationships between missions.
The depth of execution (mission stack).
That
skill_lvl1.2is a sibling ofskill_lvl1.1, not nested inside it.
You can expand this approach to any depth and number of parallel sub-missions.
Cancellation Propagation
An important rule in the mission system is cancellation propagation:
If a parent mission is cancelled, all of its sub-missions will also be cancelled automatically.
This ensures:
The robot does not continue executing subtasks after a failure or user cancellation.
All nested resources are released properly.
There is no orphaned behavior.
For instance, cancelling skill_lvl1.1-skill_lvl1-b5f8493d will automatically cancel both skill_lvl1.1.1 and skill_lvl1.1.2.
Mission Interface
To wait for a mission to be started:
await mission.started()
To wait for a mission to be finished:
await mission.completed()
Note that both previous commands raise exceptions when an error occurs or when the mission has been cancelled.
To wait for a mission to be finished without being bothered by exceptions, use completed(ignore_exceptions=True):
await mission.completed(ignore_exceptions=True)
To cancel a mission:
await mission.cancel()
To cancel and wait for the mission to be finished:
await mission.cancel_and_complete()
Subscribing to Topics
To subscribe to WebSocket topics, use the subscribe method.
robot.subscribe("navigation_state")
robot.subscribe("battery_voltage")
Registering Callbacks
You can register callback functions to handle messages received from specific topics.
def handle_navigation_state(message: dict) -> None:
print(f"Navigation State: {message}")
def handle_battery_voltage(message: dict) -> None:
print(f"Battery Voltage: {message}")
robot.register_callback("navigation_state", handle_navigation_state)
robot.register_callback("battery_voltage", handle_battery_voltage)
Example Usage
Here is an example script that you can launch on the robot:
"""Example file to test robot abilities."""
import argparse
import asyncio
import logging
from pymirokai.enums import Arm
from pymirokai.enums.navigate import Direction
from pymirokai.models.data_models import EnchantedObject, Coordinates
from pymirokai.robot import connect, Robot
from pymirokai.utils.get_local_ip import get_local_ip
from pymirokai.utils.run_until_interruption import run_until_interruption
logger = logging.getLogger("pymirokai")
async def run(ip: str, api_key: str) -> None:
"""Run the robot, demonstrating various features."""
async with connect(ip=ip, api_key=api_key) as robot:
await robot_behavior(robot)
await asyncio.Future() # Wait indefinitely
async def robot_behavior(robot: Robot) -> None:
"""Demonstrate various features."""
logger.info("======= SUBSCRIBE TO TOPICS =======")
robot.subscribe("navigation_state")
await asyncio.sleep(0.5)
logger.info("======= START SCENARIO =======")
logger.info("----------- Wave and say hello -----------")
waving = await robot.wave().started()
await robot.say("Bonjour, je m'appelle Miroki.").completed()
await waving.completed()
await asyncio.sleep(1)
logger.info("----------- Rotate 90° to the left -----------")
await robot.turn(degrees=90, direction=Direction.LEFT).completed()
logger.info("----------- Grasp handle with left arm -----------")
grasping_handleB = robot.grasp_known_object(rune=EnchantedObject(id="handleB"), arm=Arm.LEFT)
try:
await grasping_handleB.started()
except Exception as e:
logger.error(f"Error while starting to grasp handleB: {e}")
try:
await grasping_handleB.completed()
except Exception as e:
logger.error(f"Error while grasping handleB: {e}")
logger.info("----------- Grasp handle with right arm directly -----------")
grasping = robot.grasp_object_with_right_hand()
await asyncio.sleep(2)
await grasping.cancel_and_complete()
logger.info("----------- Rotate 90° on the right -----------")
# with list format, you need to send a list with [x, y, theta]
await robot.turn(degrees=90, direction=Direction.RIGHT).completed()
logger.info("----------- Wait for the robot to start the move -----------")
await asyncio.sleep(1)
logger.info("----------- Wait until the move is over -----------")
logger.info("----------- Move forward (2m) -----------")
await robot.go_to_relative(Coordinates(x=2)).completed()
await asyncio.sleep(1)
logger.info("----------- Give the handle to the user -----------")
await robot.animate_arm(anim_name="GIVE_HAND_NO_OPEN_HAND_BACK", arm=Hand.RIGHT).started()
logger.info("----------- Laugh and use TTS -----------")
await robot.say("C'est pour toi").completed()
logger.info("----------- Print values of subscribed topics -----------")
robot.register_callback("navigation_state", print)
robot.register_callback("battery_voltage", print)
async def main() -> None:
"""Main entry point for the script."""
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"-i",
"--ip",
help="Set the IP of the robot you want to connect.",
type=str,
default=get_local_ip(),
)
parser.add_argument(
"-k",
"--api-key",
help="Set the API key of the robot you want to connect.",
type=str,
default="",
)
args = parser.parse_args()
await run(ip=args.ip, api_key=args.api_key)
if __name__ == "__main__":
run_until_interruption(main)
Note that run_until_interruption provided by pymirokai is very useful to start the coroutine and stop it when we send SIGINT or SIGTERM signals.
Custom Skills
While many powerful skills come pre-integrated with the robot, you can also build and run your own custom skills to tailor the robot’s behavior to your specific use case.
What Are Custom Skills?
Custom skills are Python-based behaviors or actions that you define and execute through the robot’s Skill Manager. These skills can run on the robot itself or remotely on your computer.
On-Robot Skills: Execute directly on the robot, using only the pre-installed libraries.
Remote Skills: Execute from your development machine, giving you access to external Python packages, GPUs/CPUs, local storage, or any system resources.
This flexible architecture allows for:
Lightweight skills on-device (low-latency, embedded use).
Heavy-duty processing remotely (e.g. AI models, data pipelines, integrations).
Running the Skills Manager
To launch your own Skill Manager and start running custom skills remotely, simply use the following command after installing pymirokai:
start_skills_manager [-h] [-i IP] [-k API_KEY] [-d UPLOAD_DIR]
Arguments:
-i,--ip: The robot’s IP address.-k,--api-key: The API key for accessing the robot.-d,--upload-dir: Path to the directory containing your custom skill files.
Example:
start_skills_manager --ip 192.168.0.42 --api-key YOUR_API_KEY --upload-dir ./my_custom_skills/
This starts a local skill manager, connects it to the robot, and allows your custom skills to be uploaded and executed.
Why Use Remote Skills?
Running skills remotely is ideal when you:
Need access to powerful computing resources (e.g. for AI inference or image processing).
Want to integrate with local services, databases, or APIs.
Prefer a fast development cycle without redeploying code to the robot.
Creating Your Own Skills
To learn how to create, register, and invoke custom skills, refer to the Create custom skills guide.
It includes:
Skill structure and naming conventions.
Input/output formats.
How to invoke skills via the
Robotclass.
By using custom skills, you unlock full control over your robot’s behavior and can rapidly experiment, prototype, and deploy personalized features.
Conclusion
This documentation provides an overview of the Robot class. By following the examples and guidelines provided, you can effectively control and interact with the robot using the Robot class. For
more detailed information on specific methods and parameters, refer to the detailed pymirokai documentation and the API documentation.