Wobbler - Code Walkthrough

From sdk-wiki
Jump to: navigation, search

Introduction

This example demonstrates the usage of the velocity controller to control baxter's limbs using the velocity commands. The code consists of a main() function that initializes the ros node and creates an instance of the Wobbler class. In its __init__() function, instances for the left and right limb class are created. The wobble method is called which in turn calls the set_neutral method that sets the limbs to a predefined neutral position. The wobble() method is primarily responsible for calculating the torques necessary for the harmonic motion of the joints. Terminating the example would invoke the clean_shutdown() method where the limbs are sent back to the neutral mode and the original state of the robot is restored.
Interfaces-

  • Limb.joint_names()
  • Limb.exit_control_mode()
  • Limb.move_to_neutral()
  • Limb.set_joint_velocities(<double>)

Code Walkthrough

Now, let's break down the code

30 import argparse
31 import math
32 import random
33 
34 import rospy
35 
36 from std_msgs.msg import (
37     UInt16,
38 )
39 
40 import baxter_interface
41 
42 from baxter_interface import CHECK_VERSION

This imports the baxter interface for accessing the head class. The CHECK_VERSION is imported to check if the software running on the robot would be compatible with this local version. It is not necessary to check the version in custom programs.

45 class Wobbler(object):
46 
47     def __init__(self):
48         """
49         'Wobbles' both arms by commanding joint velocities sinusoidally.
50         """
51         self._pub_rate = rospy.Publisher('robot/joint_state_publish_rate',
52                                          UInt16, queue_size=10)
53         self._left_arm = baxter_interface.limb.Limb("left")
54         self._right_arm = baxter_interface.limb.Limb("right")
55         self._left_joint_names = self._left_arm.joint_names()
56         self._right_joint_names = self._right_arm.joint_names()

The _pub_rate publisher sets the frequency at which the joint commands are updated. Two instances of the limb class are created for each of the limbs. The joint_names() method returns an array of strings that hold the names of each of the joints in that limb.

58         print("Getting robot state... ")
59         self._rs = baxter_interface.RobotEnable(CHECK_VERSION)
60         self._init_state = self._rs.state().enabled
61         print("Enabling robot... ")
62         self._rs.enable()
63 
64         # set joint state publishing to 100Hz
65         self._pub_rate.publish(100)

baxter_interface.RobotEnable(CHECK_VERSION) checks if the sdk version updated in the settings is the same as the sdk version loaded on the param server of the Robot and creates an object _rs of the RobotEnable class. The next line sets the _init_state of the robot to its current state. The enable() performs the actual enabling of the robot. It is important to note that the robot should be in enabled state before attempting to move its joints. The joint state publishing rate is set to be at 100 Hz.

67     def _reset_control_modes(self):
68         rate = rospy.Rate(100)
69         for _ in xrange(100):
70             if rospy.is_shutdown():
71                 return False
72             self._left_arm.exit_control_mode()
73             self._right_arm.exit_control_mode()
74             self._pub_rate.publish(100)
75             rate.sleep()
76         return True

The exit_control_mode() method switches the control from advanced control modes like velocity and torque control to the position control mode. In this example, it would be switching from velocity control mode to position control mode

78     def set_neutral(self):
79         """
80         Sets both arms back into a neutral pose.
81         """
82         print("Moving to neutral pose...")
83         self._left_arm.move_to_neutral()
84         self._right_arm.move_to_neutral()

The move_to_neutral() method moves the set of joints in that limb to a predefined position referred to as its neutral position. This employs the PID position controller to move the joints to that position.

86     def clean_shutdown(self):
87         print("\nExiting example...")
88         #return to normal
89         self._reset_control_modes()
90         self.set_neutral()
91         if not self._init_state:
92             print("Disabling robot...")
93             self._rs.disable()
94         return True

The _reset_control_modes() method and the set_neutral() method switches from velocity control mode to the position control mode and sets the limbs to a neutral position, as explained above. It checks if the robot was initially in disabled state before running the example and sends it back to that state if so.

 96     def wobble(self):
 97         self.set_neutral()
 98         """
 99         Performs the wobbling of both arms.
100         """
101         rate = rospy.Rate(100)
102         start = rospy.Time.now()

The wobble() method is called to perform the actual wobbling of the limbs. The hands are first set to their neutral position.

104         def make_v_func():
105             """
106             returns a randomly parameterized cosine function to control a
107             specific joint.
108             """
109             period_factor = random.uniform(0.3, 0.5)
110             amplitude_factor = random.uniform(0.1, 0.2)
111 
112             def v_func(elapsed):
113                 w = period_factor * elapsed.to_sec()
114                 return amplitude_factor * math.cos(w * 2 * math.pi)
115             return v_func
116 
117         v_funcs = [make_v_func() for _ in self._right_joint_names]

In Line # 117, make_v_func() is called for the number of joint_names in right limb. The left or right limb can be used for this purpose, since both the limbs have an equal number of joints. The period and amplitude are random values generated for each joint. return v_func() indicates that a handle of the method v_func() is generated for this particular joint with the period_factor and amplitude_factor holding the generated random values. Similarly for each joint, a handle of the v_func() method is created with a constant and random amplitude and period values. This is done to make sure that the amplitude and frequency is same for each joint, till the end of the program. Then, the cosine function is calculated for each joint based on the formula

x=a*cos(2*pi*f*t)
   where t refers to the time elapsed from the start.
         a refers to the amplitude
         f refers to the frequency
119         def make_cmd(joint_names, elapsed):
120             return dict([(joint, v_funcs[i](elapsed))
121                          for i, joint in enumerate(joint_names)])

v_funcs is a list that holds the velocity commands for all the joints in the same order. For each of the joints in the particular limb, the corresponding velcoity command is stored in a dictionary.

124         print("Wobbling. Press Ctrl-C to stop...")
125         while not rospy.is_shutdown():
126             self._pub_rate.publish(100)
127             elapsed = rospy.Time.now() - start
128             cmd = make_cmd(self._left_joint_names, elapsed)
129             self._left_arm.set_joint_velocities(cmd)
130             cmd = make_cmd(self._right_joint_names, elapsed)
131             self._right_arm.set_joint_velocities(cmd)
132             rate.sleep()

At 100 Hz, the velocity commands for the limbs are continuously calculated. set_joint_velcoties() publishes the velocity command as joint command with the mode as 2 to indicate that the commands are to be sent to the velocity controller.

134 def main():
135     """RSDK Joint Velocity Example: Wobbler
136 
137     Commands joint velocities of randomly parameterized cosine waves
138     to each joint. Demonstrates Joint Velocity Control Mode.
139     """
140     arg_fmt = argparse.RawDescriptionHelpFormatter
141     parser = argparse.ArgumentParser(formatter_class=arg_fmt,
142                                      description=main.__doc__)
143     parser.parse_args(rospy.myargv()[1:])
144 
145     print("Initializing node... ")
146     rospy.init_node("rsdk_joint_velocity_wobbler")
147 
148     wobbler = Wobbler()

The main captures the side of limb on which the user wishes to run the example. An instance of the Wobbler class is created for each of the limbs.

1     wobbler = Wobbler()
2     rospy.on_shutdown(wobbler.clean_shutdown)
3     wobbler.wobble()
4 
5     print("Done.")
6 
7 if __name__ == '__main__':
8     main()

The wobble() method is called to perform the actual wobbling as discussed above.