PyRobot Next Version as a Modular Framework
In this example we will run through a basic manipulation example to demonstrate PyRobot as a modular framework.
This tutorial is using a simulated version of the robot. Before we get started with this, ensure the following:
- The appropriate python virtual environment has been sourced.
load_pyrobot_env
Creating World
A World represent an environment with four types of modules: robots, sensors, algorithms, and obstacles.
#import appopriate dependencies
from pyrobot import World
# Create world.
world = World(config_name='env/locobot_arm_env.yaml')
Now we have created a default manipulation world object configured by {$path-to-pyrobot-root}/pyrobot/src/pyrobot/hydra_config/env/locobot_arm_env.yaml. Let's take a look at what's inside this config file.
environment:
world_frame: 'map'
# A locobot is instantiated at [0,0,0] in the world
robots:
- robot: 'locobot'
label: 'locobot'
...
# There's no sensor and obstacles in the environment
sensors: null
obstacles: null
# 4 Algorithms are instantiated with the world
algorithms:
...
- algorithm: 'locobot_moveit_kin_planner'
label: 'moveit_planner'
...
- algorithm: 'locobot_kdl_kinematics'
label: 'kdl_kinematics'
...
# manages roslaunch files: this chuck specify the world would be spinning up in gazebo mode (simulation).
ros_launch:
ns: ''
name: 'world'
mode: gazebo
habitat: null
gazebo: null
realrobot: null
Here we initiated a world object with a robot module with label locobot configured by {$path-to-pyrobot-root}/pyrobot/src/pyrobot/hydra_config/robot/locobot.yaml, and two algorithm module configured by {$path-to-pyrobot-root}/pyrobot/src/pyrobot/hydra_config/algorithm/{$algorithm-name}.yaml under mode gazebo.
Robot Control
We can access locobot by:
bot = world.robots['locobot']
Let's also take a look at what's inside locobot.yaml.
name: 'locobot'
ros_launch:
mode: gazebo
name: ${name}
wait: 30
...
gazebo:
# roslaunch command for spinning up locobot in gazebo mode
common: "roslaunch locobot_control main.launch use_sim:=true"
...
...
modules:
- object:
_target_: pyrobot.robots.locobot.arm.LoCoBotArm
...
name: 'arm'
conf:
...
- object:
_target_: pyrobot.robots.locobot.base.LoCoBotBase
...
name: 'base'
conf:
...
- object:
_target_: pyrobot.robots.locobot.gripper.LoCoBotGripper
...
name : 'gripper'
conf:
...
- object:
_target_: pyrobot.robots.locobot.camera.LoCoBotCamera
...
name : 'camera'
conf:
...
locobot is a dictionary consists of four components: arm, base, gripper and camera, which extend Arm, Base, Gripper and Camera base class respectively. Therefore, they are guaranteed to have the same interface defined by the base class, for example, arm would have go_home, get_joint_positions/velocities/torque, set_joint_positions/velocities/torque as its member function, and their behavior would be the same across hardware. We can query locobot arm component by:
joint = [0.408, 0.721, -0.471, -1.4, 0.920]
arm = bot['arm']
arm.set_joint_positions(joint)
arm.go_home()
Algorithm Control
Similarly, we can access the planner by:
planner = world.algorithms['moveit_planner']
Let's now take a look at its configuration.
name: 'locobot_moveit_kin_planner'
ros_launch:
mode: test
...
test:
common: "roslaunch locobot_moveit_config move_group.launch allow_trajectory_execution:=true fake_execution:=false info:=true" # write corresponding roslaunch command here
algorithm:
_target_: pyrobot.algorithms.motion_planner_impl.moveit_kin_planner.MoveitKinPlanner # object definition path
robot_labels: ['locobot'] # name of the robot it would be interact with
conf: # any algorithm specific configs
IK_POSITION_TOLERANCE: 0.005
IK_ORIENTATION_TOLERANCE: 0.05
dependencies:
- algorithm: 'locobot_kdl_kinematics'
Same as LoCoBotArm object, MoveitKinPlanner also extends MotionPlanner base class, and therefore guarenteed to have member functions plan_end_effector_pose, plan_joint_angles and compute_cartesian_path.
pos = np.array([0.339, 0.0116, 0.255])
ori = np.array([0.245, 0.613, -0.202, 0.723]),
planner.plan_end_effector_pose(pos, ori)
Plug-in-and-play Algorithms
If we look into the implementation of MoveitKinPlanner, we'll see the member function plan_end_effector_pose relies on a kinematic module to compute ik from given pose. In config file the default kinematic module is set to locobot_kdl_kinematics, but you can query it using its base class name "Kinematics".
from pyrobot.algorithms.kinematics import Kinematics
isinstance(planner['Kinematics'], Kinematics)
>>> True
Using base class name suggests you can substitute such dependency with any other algorithm extending the same class and get similar behavior.
from pyrobot import make_algorithm
old_kinematics = planner['Kinematics']
new_kinematics = make_algorithm("algorithm/locobot_kdl_kinematics") # making a new copy
planner['Kinematics'] = new_kinematics
planner.plan_end_effector_pose(pos, ori) # same behavior!
print(planner['Kinematics'] is old_kinematics)
>>> False