In this section, we will demonstrate how to create a teleoperation node for a robot using Leap Motion data. The procedure is very simple. We can create a ROS package for this node using the following steps:
- The following is the command to create a new package. You can also find this package in chapter_11_ws/vr_leap_teleop:
$ catkin_create_pkg vr_leap_teleop roscpp rospy std_msgs visualization_msgs geometry_msgs message_generation visualization_msgs
After creation, you could build the workspace using catkin_make command.
- Now, let's create the node to convert Leap Motion data to Twist. You can create a folder called scripts inside the vr_leap_teleop package.
- Now you can copy the node called vr_leap_teleop.py from our repository at https://github.com/PacktPublishing/ROS-Robotics-Projects-SecondEdition/blob/master/chapter_11_ws/vr_leap_teleop/scripts/vr_leap_teleop.py. Let's look at how this code works.
We need the following Python modules in this node. Here, we require message definitions from the leap_motion package, which is the driver package:
import rospy from leap_motion.msg import leap from leap_motion.msg import leapros from geometry_msgs.msg import Twist
- Now, we have to set some range values in which we can check whether the current hand value is within range. We are also defining the teleop_topic name here:
teleop_topic = '/cmd_vel_mux/input/teleop' low_speed = -0.5 stop_speed = 0 high_speed = 0.5 low_turn = -0.5 stop_turn = 0 high_turn = 0.5 pitch_low_range = -30 pitch_high_range = 30 roll_low_range = -150 roll_high_range = 150
Here is the main code of this node. In this code, you can see that the topic from the Leap Motion driver is being subscribed to. When a topic is received, it will call the callback_ros() function:
def listener(): global pub rospy.init_node('leap_sub', anonymous=True) rospy.Subscriber("leapmotion/data", leapros, callback_ros) pub = rospy.Publisher(teleop_topic, Twist, queue_size=1) rospy.spin() if __name__ == '__main__': listener()
The following is the definition of the callback_ros() function. Essentially, it will receive the Leap Motion data and extract the orientation components of the palm only. Therefore, we will get yaw, pitch, and roll from this function.
We are also creating a Twist() message to send the velocity values to the robot:
def callback_ros(data): global pub msg = leapros() msg = data yaw = msg.ypr.x pitch = msg.ypr.y roll = msg.ypr.z twist = Twist() twist.linear.x = 0; twist.linear.y = 0; twist.linear.z = 0 twist.angular.x = 0; twist.angular.y = 0; twist.angular.z = 0
We are performing a basic comparison with the current roll and pitch values again within the following ranges. Here are the actions we've assigned for each movement of the robot:
Action |
Description |
Hand gesture |
Robot movement |
Hand pitch low |
Move forward |
Hand pitch high |
Move backward |
Hand roll anticlockwise |
Rotate anticlockwise |
Hand roll clockwise |
Rotate clockwise |
Here is a code snippet that takes care of one condition. In this case, if the pitch is low, then we are providing a high value for linear velocity in the x direction for moving forward:
if(pitch > pitch_low_range and pitch < pitch_low_range + 30):
twist.linear.x = high_speed; twist.linear.y = 0;
twist.linear.z = 0 twist.angular.x = 0; twist.angular.y = 0; twist.angular.z = 0
Okay, so we have built the node, and we can test it at the end of the project. In the next section, we will look at how to implement VR in ROS.