The Robot Commander

The robot commander provides a high level interface to easily control the different robots supported by Shadow Robot. It encapsulates functionality provided by different ROS packages, especially the moveit_commander, providing access via a simplified interface.

There are three classes available:

SrRobotCommander

Overview

The main purpose of the robot commander is to provide a base class to the hand commander. The RobotCommander should not be used directly unless necessary. Use the SrHandCommander instead.

Examples of usage can be found here.

In the following sections, you can find decriptions of the most relevant functions of the hand commander.

Basic terminology

A robot is described using an srdf file which contains the semantic description that is not available in the urdf. It describes a robot as a collection of groups that are representations of different sets of joints that are useful for planning. Each group can have its end-effector and group states specified. Group states are a specific set of joint values predefined for a group with a given name, for example close_hand or open_hand.

As the robot commander is a high level wrapper of the moveit_commander, its constructor takes the name of one of the robot groups for which the planning will be performed.

Setup

Import the hand commander along with basic rospy libraries:

import rospy
from sr_robot_commander.sr_hand_commander import SrHandCommander

The constructor for the SrHandCommander takes a name parameter that should match the group name of the robot to be used.

As well as creating an instance of the SrHandCommander class, we must also initialise our ros node:

rospy.init_node("sr_hand_commander_example", anonymous=True)
hand_commander = SrHandCommander("right_hand")

Getting basic information

We can get the name of the robot, group or planning reference frame:

print("Robot name: ", hand_commander.get_robot_name())
print("Group name: ", hand_commander.get_group_name())
print("Planning frame: ", hand_commander.get_planning_frame())

Get the list of names of the predefined group states from the srdf and warehouse for the current group:

# Refresh them first if they have recently changed
hand_commander.refresh_named_targets()

print("Named targets: ", hand_commander.get_named_targets())

Get the joints position and velocity:

joints_position = hand_commander.get_joints_position()
joints_velocity = hand_commander.get_joints_velocity()

print("Hand joint positions\n" + str(joints_position) + "\n")
print("Hand joint velocities\n" + str(joints_velocity) + "\n")

Get the current joint state of the group being used:

current_state = hand_commander.get_current_state()

# To get the current state while enforcing that each joint is within its limits
current_state = hand_commander.get_current_state_bounded()

Setting functions

You can change the reference frame to get pose information:

hand_commander.set_pose_reference_frame("palm")

You can also activate or deactivate the teach mode for the robot:

# Activation: stops the trajectory controllers for the robot, and sets it to teach mode.
hand_commander.set_teach_mode(True)

# Deactivation: stops the teach mode and starts trajectory controllers for the robot.
# Currently, this method blocks for a few seconds when called on a hand, while the hand parameters are reloaded.
hand_commander.set_teach_mode(False)

Plan/move to a joint-space goal

Using the methods plan_to_joint_value_target, move_to_joint_value_target or move_to_joint_value_target_unsafe, a set of the joint values can be given for the specified group to create a plan and send it for execution.

Parameters:

  • joint_states is a dictionary with joint name and value. It can contain joints’ values of which need to be changed.

  • wait indicates if the method should wait for the movement to end or not (default value is True)

  • angle_degrees should be set to true if the input angles are in degrees (default value is False)

IMPORTANT: Bear in mind that the names of the joints are different for the right and left hand.

Example

rospy.init_node("robot_commander_examples", anonymous=True)

hand_commander = SrHandCommander(name="right_hand")
joints_states = {'rh_FFJ1': 90, 'rh_FFJ2': 90, 'rh_FFJ3': 90, 'rh_FFJ4': 0.0,
                 'rh_MFJ1': 90, 'rh_MFJ2': 90, 'rh_MFJ3': 90, 'rh_MFJ4': 0.0,
                 'rh_RFJ1': 90, 'rh_RFJ2': 90, 'rh_RFJ3': 90, 'rh_RFJ4': 0.0,
                 'rh_LFJ1': 90, 'rh_LFJ2': 90, 'rh_LFJ3': 90, 'rh_LFJ4': 0.0, 'rh_LFJ5': 0.0,
                 'rh_THJ1': 40, 'rh_THJ2': 35, 'rh_THJ3': 0.0, 'rh_THJ4': 65, 'rh_THJ5': 15,
                 'rh_WRJ1': 0.0, 'rh_WRJ2': 0.0}
hand_commander.move_to_joint_value_target(joints_states, wait=False, angle_degrees=True))

In this example, joint states for a hand are sent to the HandCommander, the method is prompted by the wait=False argument to not wait for the movement to finish executing before moving on to the next command and the angle_degrees=True argument tells the method that the input angles are in degrees, so require a conversion to radians.

Plan/move to a predefined group state

Using the methods plan_to_named_target or move_to_named_target will allow to plan or move the group to a predefined pose. This pose can be defined in the srdf or saved as a group state in the moveit warehouse.

Parameters:

  • name is the unique identifier of the target pose

  • wait indicates if the method should wait for the movement to end or not (default value is True)

Example

pack is a predefined pose defined in the SRDF file for the right_hand group:

<group_state group="right_hand" name="pack">
  <joint name="rh_THJ1" value="0.52"/>
  <joint name="rh_THJ2" value="0.61"/>
  <joint name="rh_THJ3" value="0.00"/>
  <joint name="rh_THJ4" value="1.20"/>
  <joint name="rh_THJ5" value="0.17"/>
  <joint name="rh_FFJ1" value="1.5707"/>
  <joint name="rh_FFJ2" value="1.5707"/>
  <joint name="rh_FFJ3" value="1.5707"/>
  <joint name="rh_FFJ4" value="0"/>
  <joint name="rh_MFJ1" value="1.5707"/>
  <joint name="rh_MFJ2" value="1.5707"/>
  <joint name="rh_MFJ3" value="1.5707"/>
  <joint name="rh_MFJ4" value="0"/>
  <joint name="rh_RFJ1" value="1.5707"/>
  <joint name="rh_RFJ2" value="1.5707"/>
  <joint name="rh_RFJ3" value="1.5707"/>
  <joint name="rh_RFJ4" value="0"/>
  <joint name="rh_LFJ1" value="1.5707"/>
  <joint name="rh_LFJ2" value="1.5707"/>
  <joint name="rh_LFJ3" value="1.5707"/>
  <joint name="rh_LFJ4" value="0"/>
  <joint name="rh_LFJ5" value="0"/>
  <joint name="rh_WRJ1" value="0"/>
  <joint name="rh_WRJ2" value="0"/>
</group_state>

Here is how to move to it:

rospy.init_node("robot_commander_examples", anonymous=True)
hand_commander = SrHandCommander(name="right_hand")

# Only plan
hand_commander.plan_to_named_target("pack")

# Plan and execute
hand_commander.move_to_named_target("pack")

Move through a trajectory of predefined group states

Using the method run_named_trajectory, it is possible to specify a trajectory composed of a set of names of previously defined group states (either from SRDF or from warehouse), plan and move to follow it.

Parameters:

  • trajectory specifies a dictionary of waypoints with the following elements:
    • name: the name of the waypoint

    • interpolate_time: time to move from last waypoint

    • pause_time: time to wait at this waypoint

Example

trajectory = [
   {
       'name': 'open',
       'interpolate_time': 3.0
   },
   {
       'name': 'pack',
       'interpolate_time': 3.0,
       'pause_time': 2
   },
   {
       'name': 'open',
       'interpolate_time': 3.0
   },
   {
       'name': 'pack',
       'interpolate_time': 3.0
   }
]

hand_commander.run_named_trajectory(trajectory)

# If you want to send the trajectory to the controller without using the planner, you can use the unsafe method:
hand_commander.run_named_trajectory_unsafe(trajectory)

Check if a plan is valid and execute it

Use the method check_plan_is_valid and execute to check if the current plan contains a valid trajectory and execute it. This only has meaning if called after a planning function has been attempted.

Example

import rospy
from sr_robot_commander.sr_hand_commander import SrHandCommander
rospy.init_node("robot_commander_examples", anonymous=True)

hand_commander = SrHandCommander()

hand_commander.plan_to_named_target("open")
if hand_commander.check_plan_is_valid():
    hand_commander.execute()

Stop the robot

Use the method send_stop_trajectory_unsafe to send a trajectory with the current joint state to stop the robot at its current position.

Example

hand_commander.send_stop_trajectory_unsafe()

SrHandCommander

Overview

The SrHandCommander inherits all methods from the robot commander and provides commands specific to the hand. It allows the state of the tactile sensors and joints’ effort to be read, and the maximum force to be set.

Setup

Import the hand commander along with basic rospy libraries and the hand finder:

import rospy
from sr_robot_commander.sr_hand_commander import SrHandCommander
from sr_utilities.hand_finder import HandFinder
rospy.init_node("hand_finder_example", anonymous=True)

The constructor for the SrHandCommander takes a name parameter that should match the group name of the robot to be used. Also it takes the hand prefix, parameters and serial number that can be retrieved using the HandFinder.

Example

# Using the HandFinder
hand_finder = HandFinder()
hand_parameters = hand_finder.get_hand_parameters()
hand_serial = hand_parameters.mapping.keys()[0]

# If name is not provided, it will set "right_hand" or "left_hand" by default, depending on the hand.
hand_commander = SrHandCommander(name = "rh_first_finger",
                                 hand_parameters=hand_parameters,
                                 hand_serial=hand_serial)

# Alternatively you can launch the hand directly
hand_commander = SrHandCommander(name = "right_hand", prefix = "rh")

Getting information

Use the get_joints_effort method to get a dictionary with efforts of the group joints.

hand_joints_effort = hand_commander.get_joints_effort()
print("Hand joints effort \n " + str(hand_joints_effort) + "\n")

Use the get_tactile_type to get a string indicating the type of tactile sensors present (e.g. PST, biotac, UBI0) or get_tactile_state to get an object containing tactile data. The structure of the data is different for every tactile_type .

tactile_type = hand_commander.get_tactile_type()
tactile_state = hand_commander.get_tactile_state()

print("Hand tactile type\n" + tactile_type + "\n")
print("Hand tactile state\n" + str(tactile_state) + "\n")

Set the maximum force

Use the method set_max_force to set the maximum force for a hand joint.

Parameters:

  • joint_name name of the joint.

  • value maximum force value

Example

## The limits in the current implementation of the firmware are from 200 to 1000 (measured in custom units)
hand_commander.set_max_force("rh_FFJ3", 600)

SrArmCommander

The SrArmCommander inherits all methods from the `robot commander](https://dexterous-hand.readthedocs.io/en/latest/user_guide/2_software_description.html#srrobotcommander) and provides commands specific to the arm. It allows movement to a certain position in cartesian space, to a configuration in joint space or move using a trajectory.

Setup

Import the arm commander along with basic rospy libraries and the arm finder:

import rospy
from sr_robot_commander.sr_arm_commander import SrArmCommander
from sr_utilities.arm_finder import ArmFinder

The constructors for SrArmCommander take a name parameter that should match the group name of the robot to be used and has the option to add ground to the scene.

arm_commander = SrArmCommander(name="right_arm", set_ground=True)

Use the ArmFinder to get the parameters (such as prefix) and joint names of the arm currently running on the system:

arm_finder = ArmFinder()

# To get the prefix or mapping of the arm joints. Mapping is the same as prefix but without underscore.
arm_finder.get_arm_parameters().joint_prefix.values()
arm_finder.get_arm_parameters().mapping.values()

# To get the arm joints
arm_finder.get_arm_joints()

Getting basic information

To return the reference frame for planning in cartesian space:

reference_frame = arm_commander.get_pose_reference_frame()

Plan/move to a position target

Using the method move_to_position_target, the end effector of the arm can be moved to a certain point in space represented by (x, y, z) coordinates. The orientation of the end effector can take any value.

Parameters:

  • xyz desired position of end-effector

  • end_effector_link name of the end effector link (default value is empty string)

  • wait indicates if the method should wait for the movement to end or not (default value is True)

Example

rospy.init_node("robot_commander_examples", anonymous=True)
arm_commander = SrArmCommander(name="right_arm", set_ground=True)

new_position = [0.25527, 0.36682, 0.5426]

# To only plan
arm_commander.plan_to_position_target(new_position)

# To plan and move
arm_commander.move_to_position_target(new_position)

Plan/move to a pose target

Using the method move_to_pose_target allows the end effector of the arm to be moved to a certain pose (position and orientation) in the space represented by (x, y, z, rot_x, rot_y, rot_z).

Parameters:

  • pose desired pose of end-effector: a Pose message, a PoseStamped message or a list of 6 floats: [x, y, z, rot_x, rot_y, rot_z] or a list of 7 floats [x, y, z, qx, qy, qz, qw]

  • end_effector_link name of the end effector link (default value is empty string)

  • wait indicates if the method should wait for the movement to end or not (default value is True)

Example

rospy.init_node("robot_commander_examples", anonymous=True)
arm_commander = SrArmCommander(name="right_arm", set_ground=True)

new_pose = [0.5, 0.3, 1.2, 0, 1.57, 0]

# To only plan
arm_commander.plan_to_pose_target(new_pose)

# To plan and move
arm_commander.move_to_pose_target(new_pose)