A pre- and concise Python API to control robots with simple commands.
The Robot API is an interface layer between an executor and ROS robot control. A user shall have a simple and well defined (typed) interface to call to for the execution of robot actions. The goal is to enable control of our robots independently from their types and without knowledge of their operating systems.
On the developer side, please consider the genericity of these actions when providing robot specific implementations towards this API. Special care is necessary for capabilities which are not available on all robots. In these cases, the execution shall fail gracefully and not crash the system.
Clone this repository into your catkin workspace and catkin build
as usual.
Source the resulting setup.bash
file of your built catkin workspace
afterwards.
Assuming you have the mobipick repository installed and compiled, first start up a ROS environment, e.g.:
roslaunch mobipick_gazebo mobipick_moelk.launch
rosservice call /gazebo/unpause_physics # or click the "start" button in the Gazebo GUI
roslaunch mir_gazebo fake_localization.launch __ns:="mobipick" odom_frame_id:="mobipick/odom" base_frame_id:="mobipick/base_footprint"
roslaunch mir_navigation start_planner.launch map_file:=$(rospack find pbr_maps)/maps/moelk/pbr_robot_lab.yaml prefix:="mobipick/"
roslaunch mobipick_moveit_config moveit_planning_execution.launch use_pointcloud:=true simulation:=true
To use this robot_api
, just install it as described above and use a python
console anywhere (except inside the top level robot_api
folder because trying
to import it would then only import the subfolder with the same name inside
and result in a ModuleNotFoundError: No module named 'robot_api.msg'
):
import robot_api
# Print the namespaces of available robots.
robot_api.find_robot_namespaces()
# Get a Robot object using the robot's namespace.
mobipick = robot_api.Robot("mobipick")
# Get the robot's 2D pose using localization.
mobipick.base.get_2d_pose()
# Move the robot's arm using MoveIt.
mobipick.arm.move("transport")
# Move the robot's base using move_base.
mobipick.base.move(21.0, 7.0, 3.141592)
The idea is not new at all. See for example Facebook's PyRobot or our (internal) repository of high_level_robot_api. A few design decisions merit an own software package:
When defining an interface, you might as well make it elaborate, even when it's Python. You will notice the difference when using an IDE with code completion or static type checking.
At least on user side, you don't need to worry about importing rospy
or
initializing a ROS node to use the robot_api
. Beneath it, ROS is used indeed
since a running ROS environment is a prerequisite in the first place. In the
future, ROS 2 shall be supported as well. Under these circumstances, it makes
sense for robot_api
to accept ROS message types as parameters, even if the
user does not need to know about ROS. A detailed example is Base.move()
,
which accepts several variations of ROS and non-ROS parameters.
Some considerations have been made about whether you might prefer to type
robot.move_base()
over robot.base.move()
. In the end, the syntax is chosen
to adhere to a meaningful component model. A base with localization and
navigation capabilities is assumed to exist for any robot, and its functionality
is encapsulated in a Robot()
's base
attribute. Additional components are
available as separate attributes of the Robot
class. The interface definition
at this point is still work in progress.
Your contributions and pull requests are very welcome. Many standard robot skills are yet to be supported. We suggest the following structure:
Action can be implemented in any form you want. Preferably, each action has only
one specific purpose. Convenience methods should be available as class methods of
Base
or extensions, for example, and they delegate action execution to
specific implementations based on the parameter values. Base.move()
demonstrates a sample implementation of this concept.
Feel free to add further extensions as attributes to the Robot
class but keep
in mind that these will exist then for all robots, even if they might not have
the respective components. See Base.arm
as an example of such an extension.
By default, robot_api
connects components lazily on demand. This means, for
example, actionlib
clients connect to their servers when they execute something
for the first time. Such mechnamisms are integral parts of the Robot API in
order to offer convenience to the user. The user does not need to know about
actionlib
at all. Even rospy.init_node()
is called when needed, i.e., when
a ROS function is about to executed but no ROS node is yet present in the
current process.