Difference between pages "Joint Position Example" and "Networking"

(Difference between pages)
Jump to: navigation , search
(Joint Position Joystick Code Walkthrough comments)
 
(ROS Naming Conventions)
 
Line 1: Line 1:
__NOTOC__
+
 
 
<div class="title-block">
 
<div class="title-block">
 
+
<span style="font-size:120%;">'''The Sawyer Research Robot uses ROS to communicate with the user's Development Workstation. This requires an ethernet network to be established between Sawyer and the Workstation with full bi-directional connectivity.  If you have trouble connecting to your Sawyer, see this page for our Recommended Network Setup and other common network debugging steps.'''</span>
<span style="font-size:120%;">'''The joint position example(keyboard, joystick) demonstrates basic joint position control. The key or button mapped to either increasing or decreasing the angle of a particular joint on Sawyer's arm.'''</span>
 
 
 
 
</div>
 
</div>
  
<div class="content-block">
 
== Usage ==
 
 
Start the joint position keyboard example program, ex:
 
  
<syntaxhighlight lang="bash" enclose="div">
+
{{TOClimit|limit=2}}
$ rosrun intera_examples joint_position_keyboard.py
 
</syntaxhighlight>
 
  
Start the joint position joystick example program, ex:
 
 
<syntaxhighlight lang="bash" enclose="div">
 
$ rosrun intera_examples joint_position_joystick.py
 
</syntaxhighlight>
 
 
Upon startup, you will be prompted with the following:
 
 
<syntaxhighlight lang="bash" enclose="div">
 
Initializing node...
 
Getting robot state...
 
Enabling robot...
 
[INFO] [WallTime: 1399575163.891211] Robot Enabled
 
Controlling joints. Press ? for help, Esc to quit.
 
</syntaxhighlight>
 
 
</div>
 
  
 
<div class="content-block">
 
<div class="content-block">
 +
== Basics ==
 +
These Network Configuration settings are available in the [[Field Service Menu (FSM)]]
  
== Arguments ==
+
=== Basic Requirements ===
 
+
Sawyer must be connected to a development workstation which uses ROS over an ethernet network to communicate bi-directionally.
'''Important Arguments:''' 
 
 
 
See the joint position example available arguments on the command line by passing:
 
<syntaxhighlight lang="bash" enclose="div">
 
$ rosrun intera_examples joint_position_keyboard.py -h
 
</syntaxhighlight>
 
  
<syntaxhighlight lang="bash" enclose="div">
+
Sawyer's [[Robot Hostname|hostname]] can be configured using the [[Field Service Menu (FSM)]] if you do not like the one given out at the factory which matches its [[Serial Number|serial number]].
usage: joint_position_keyboard.py [-h] [-l LIMB]
 
  
RSDK Joint Position Example: Keyboard Control
+
=== Network address assignment ===
    Use your dev machine's keyboard to control joint positions.
 
    Each key corresponds to increasing or decreasing the angle
 
    of a joint on Sawyer's arm. The increasing and descreasing
 
    are represented by number key and letter key next to the number.
 
   
 
  
optional arguments:
+
Sawyer supports IPV4 addressing for the following network configurations:
  -h, --help            show this help message and exit
+
* Automatic address assignment (“Automatic” mode)
  -l LIMB, --limb LIMB
+
** If a DHCP server is present in the network, the DHCP server automatically assigns a network address to the robot, and may (or may not) assign a DNS server for host name resolution.
                        Limb on which to run the joint position keyboard example
+
** If no DHCP server is present, the robot will use the Autoip protocol to assign itself a link-local address in the 169.254.0.0/16 address block.
</syntaxhighlight>
+
** All assignments in this mode are automatic, and no options can be configured manually.
 +
* Manual address assignment from the Field Service Menu (“Manual” mode)
 +
** The user must specify a valid IPV4 address for the robot, and may optionally specify a network mask, default gateway address, and DNS server address. All network options are configured manually.
  
For joint position joystick example:
+
=== Host name resolution ===
<syntaxhighlight lang="bash" enclose="div">
 
$ rosrun intera_examples joint_position_joystick.py -h
 
</syntaxhighlight>
 
  
<syntaxhighlight lang="bash" enclose="div">
+
In all addressing modes, Sawyer provides link-local advertising of the robot’s host name as “<robot name>.local” using the Avahi mDNS service. Computers that are located in the same subnet as the robot and that support mDNS can always resolve the robot’s host name as “<robot name>.local” even if no other host name resolution service is present. Sawyer will also be able to communicate with these computers using their host names.
usage: joint_position_joystick.py [-h] [-l LIMB] [-j JOYSTICK]
 
  
SDK Joint Position Example: Joystick Control
+
In “Automatic” addressing mode, Sawyer will be able to resolve external host names only if the network’s DHCP server supplies a DNS server address. If the DHCP server does not provide a DNS server address, Sawyer will only be able to communicate with external computers by their IP addresses.
    Use a game controller to control the angular joint positions
 
    of Sawyer's arms.
 
    Attach a game controller to your dev machine and run this
 
    example along with the ROS joy_node to control the position
 
    of each joint in Sawyer's arm using the joystick. Be sure to
 
    provide your *joystick* type to setup appropriate key mappings.
 
    Each stick axis maps to a joint angle; which joints are currently
 
    controlled can be incremented by using the mapped increment buttons.
 
    Ex:
 
      (x,y -> e0,e1) >>increment>> (x,y -> e1,e2)
 
  
required arguments:
+
In “Manual” addressing mode, Sawyer will be able to resolve external host names only if the user specifies a DNS server address (and a default gateway address if computers in a different subnet are involved). If the user does not provide a DNS server address, Sawyer will only be able to communicate with computers by their IP addresses.  If the user does not provide a default gateway address, Sawyer will only be able to communicate with computers within the same subnet.
  -j, --joystick        specify the type of joystick to use
 
 
 
optional arguments:
 
  -h, --help            show this help message and exit
 
  -l LIMB, --limb LIMB
 
                        Limb on which to run the joint position joystick example
 
</syntaxhighlight>
 
  
 +
=== ROS Naming Conventions ===
  
 +
Intera robots supports 3 ROS naming conventions.  These control how the ROS Master publishes the access information for the individual nodes published by the robot.  Regardless of how you connect to the robot, you will need to be able to reach it by the address configured by its ROS naming convention if you want to be able to interact with it. (i.e. if you can reach the robot by its IP, but it is configured with the ROS_HOSTNAME convention and you can't reach it by its hostname, then you will be unable to do meaningful work with the robot)
 +
* '''ROS_HOSTNAME.local:''' This publishes the nodes at <code><robot hostname>.local</code>
 +
** This is the least versatile, but the most stable configuration, and is our recommended when it is available.
 +
** This convention only works in a local setup, where your robot and development machine are on the same subnet, but will '''almost always''' work in that network configuration.
 +
** This will only break if
 +
*** A naming conflict is introduced on the local network or
 +
*** You manually change the hostname of the robot.
 +
* '''ROS_IP:'''  This publishes the nodes at the IP4 Address associated with the robot
 +
** This is the most versatile naming convention, but also the most fragile.
 +
** It will work in any network configuration where you have connectivity to the robot, but will break if the IP of the robot changes
 +
* '''ROS_HOSTNAME:''' This publishes the nodes at the hostname of the robot.
 +
** This configuration has medium versatility and stability.
 +
** It only works if there is a working DNS running on your network, and pointed at the robot.
 +
** This will only break if
 +
*** A naming conflict is introduced in the network or
 +
*** If the the DNS-resolved hostname of the robot is changed, which can be done by the Server admin, but is unlikely to happen.
 
</div>
 
</div>
  
 
<div class="content-block">
 
<div class="content-block">
  
== Joint Position Keyboard Code Walkthrough ==  
+
== Network Configuration through a Router ==
 
 
Now, let's break down the code.
 
 
 
<syntaxhighlight lang="python" line start="33" enclose="div">
 
"""
 
SDK Joint Position Example: keyboard
 
"""
 
import argparse
 
  
import rospy
+
=== Recommended Network Configuration ===
  
import intera_interface
+
The recommended network configuration is to connect your development workstation and your Sawyer to an all in one SOHO (Small Office/Home Office) router/firewall similar to the Linksys EA-Series routers. This type of router provides DHCP and other networking services, and has the benefit of keeping the network traffic off of your main network. It also allows the development workstation access to internet in particular, the Rethink Robotics github repo for code and documentation.
import intera_external_devices
 
  
from intera_interface import CHECK_VERSION
+
=== Router Configuration Diagram ===
</syntaxhighlight>
 
  
This imports the intera interface for accessing the limb and the gripper class. The <code>intera_external_devices</code> is imported to use its getch function, that captures the key presses on the keyboard. The <code>CHECK_VERSION</code> 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.
+
[[File:Router_Config.png|800px]]
  
<syntaxhighlight lang="python" line start="43" enclose="div">
+
* Sawyer and the user's computer can both be set to "Automatic" addressing mode, which will draw IP information directly from the router.
def map_keyboard(side):
 
    limb = intera_interface.Limb(side)
 
   
 
    try:
 
        gripper = intera_interface.Gripper(side)
 
    except:
 
        has_gripper = False
 
        rospy.logerr("Could not initalize the gripper.")
 
    else:
 
        has_gripper = True
 
  
    joints = limb.joint_names()
+
=== Sawyer ===
</syntaxhighlight>
 
  
The instance of Limb class, <code>limb</code> and the instance of Gripper class, <code>gripper</code> are created. <code>joint_names()</code> method returns an array of joints of the limb.
+
* Sawyer can be set to use either ROS_IP or ROS_HOSTNAME.local:
 +
*# ROS_HOSTNAME.local is our '''recommended''' ros naming protocol for the robot in this setup.  It is very stable in this network configuration, and would only need to be adjusted if you switch to a separate subnet from the robot.
 +
*#* If you can <code>ping <robot hostname>.local</code>, then the network is working correctly
 +
*# ROS_IP is the most reliable naming method, if you know that the robot is going to keep its .IP.  This naming protocol works across all network configurations as long as you have reliable routing to the robot.
 +
*#* If you can <code> ping <robot ip></code> then the network is working correctly.
 +
*#* '''Note:''' You can set the robot to use a Static IP if you are interested in using this naming convention because you are unsure of your future network configurations, but be sure to talk to your network administrator if you are hooking your router up to a larger network.
  
<syntaxhighlight lang="python" line start="56" enclose="div">
+
=== Dev Machine ===
    def set_j(limb, joint_name, delta):
 
        current_position = limb.joint_angle(joint_name)
 
        joint_command = {joint_name: current_position + delta}
 
        limb.set_joint_positions(joint_command)
 
  
    def set_g(action):
+
* In this network configuration, you will want to have your [[RSDK Shell|intera.sh]] script point to either:
        if has_gripper:
+
*# ROS_IP = "<your development machine's IPV4 Address>"
            if action == "close":
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's IPV4 Address></code>
                gripper.close()
+
*# ROS_HOSTNAME = "<your development machine's hostname>.local"
            elif action == "open":
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's hostname>.local</code>
                gripper.open()
 
            elif action == "calibrate":
 
                gripper.calibrate()
 
</syntaxhighlight>
 
  
The <code>set_j()</code> function is invoked whenever a valid key press occurs. The <code>limb</code> refers to the limb instance of Sawyer's limb. <code>delta</code> refers to the required displacement of the joint from its current position. The method <code>joint_angle()</code> returns the current position of that joint. Then the joint command message is updated for that corresponding joint to indicate the new position. <code>set_joint_position()</code> method publishes the joint commands to the position controller.
+
=== Connect Directly To Corporate Or University Network ===
The <code>set_g</code> function calls gripper action function(<code>open()</code>, or <code>close()</code> or <code>calibrate()</code>) when corresponding action is provided.
 
  
<syntaxhighlight lang="python" line start="70" enclose="div">
+
Another viable networking configuration is to connect Sawyer directly to your corporate or university network. Here you need to make sure that a DHCP server is available, and that your name server can resolve Sawyer's hostname to an IP address. If you plan on using DHCP reservation using Sawyer's MAC address, you can get the MAC address by using [[TTY#TTY3 - Network Info Display|TTY3]].
    bindings = {
 
        '1': (set_j, [limb, joints[0], 0.1], joints[0]+" increase"),
 
        'q': (set_j, [limb, joints[0], -0.1], joints[0]+" decrease"),
 
        '2': (set_j, [limb, joints[1], 0.1], joints[1]+" increase"),
 
        'w': (set_j, [limb, joints[1], -0.1], joints[1]+" decrease"),
 
        '3': (set_j, [limb, joints[2], 0.1], joints[2]+" increase"),
 
        'e': (set_j, [limb, joints[2], -0.1], joints[2]+" decrease"),
 
        '4': (set_j, [limb, joints[3], 0.1], joints[3]+" increase"),
 
        'r': (set_j, [limb, joints[3], -0.1], joints[3]+" decrease"),
 
        '5': (set_j, [limb, joints[4], 0.1], joints[4]+" increase"),
 
        't': (set_j, [limb, joints[4], -0.1], joints[4]+" decrease"),
 
        '6': (set_j, [limb, joints[5], 0.1], joints[5]+" increase"),
 
        'y': (set_j, [limb, joints[5], -0.1], joints[5]+" decrease"),
 
        '7': (set_j, [limb, joints[6], 0.1], joints[6]+" increase"),
 
        'u': (set_j, [limb, joints[6], -0.1], joints[6]+" decrease"),
 
        '8': (set_g, "close", side+" gripper close"),
 
        'i': (set_g, "open", side+" gripper open"),
 
        '9': (set_g, "calibrate", side+" gripper calibrate")
 
    }
 
</syntaxhighlight>
 
  
The <code>bindings</code> is a dictionary that holds the set of characters in the keyboard and their corresponding joints.
+
You can set Sawyer to use either "Automatic" addressing or a static IP configuration in this configuration.
  
<syntaxhighlight lang="python" line start="89" enclose="div">
+
'''Note:''' If you want to set a static IP, you will need to talk to your network administrator to get an appropriate IP for you to assign, in order to avoid network collisions.
    done = False
 
    print("Controlling joints. Press ? for help, Esc to quit.")
 
    while not done and not rospy.is_shutdown():
 
        c = intera_external_devices.getch()
 
        if c:
 
            #catch Esc or ctrl-c
 
            if c in ['\x1b', '\x03']:
 
                done = True
 
                rospy.signal_shutdown("Example finished.")
 
</syntaxhighlight>
 
  
The <code>done</code> variable captures whether "esc" or "ctrl-c" was hit. The while loop iterates as long as the "esc" or "ctrl-c" is hit or ros-shutdown signal is given. c captures the keyboard input. If "esc" or "ctrl-c" is given then <code>done</code> gets updated as true, and a shutdown signal will be sent.
+
=== Sawyer ===
  
<syntaxhighlight lang="python" line start="98" enclose="div">
+
In this network configuration, you can use either ROS_IP or ROS_HOSTNAME ROS naming conventions
            elif c in bindings:
+
* In this setup, we recommend using the ROS_HOSTNAME convention.  This will rely on your DNS and on not having someone else register a duplicate of your hostname (so pick a unique one), but will be very stable in this network configuration.
                cmd = bindings[c]
+
** If you can <code>ping <robot hostname></code> then the network is working.
                if c == '8' or c == 'i' or c == '9':
+
* As mentioned in the previous section, ROS_IP is the most versatile ROS naming convention that works across the most network configurations, but is susceptible to automatic IP changes when a lease is exceeded.
                    cmd[0](cmd[1])
+
** If you can <code>ping <robot IP></code> then the network is working.
                    print("command: %s" % (cmd[2],))
 
                else:
 
                    #expand binding to something like "set_j(right, 'j0', 0.1)"
 
                    cmd[0](*cmd[1])
 
                    print("command: %s" % (cmd[2],))
 
</syntaxhighlight>
 
  
The user inputted key is checked whether it is in the <code>bindings</code> dictionary. <code>bindings[c]</code> returns the value of the key c from the dictionary. The 0th index of the value refers to the function to be called and the next index holds the arguments to be sent along with that function. <code>cmd[2]</code> is the description of the joint command from the dictionary above.
+
=== Dev Machine ===
  
<syntaxhighlight lang="python" line start="107" enclose="div">
+
* In this network configuration, you will want to have your [[RSDK Shell|intera.sh]] script point to either:
            else:
+
*# ROS_IP = "<your development machine's IPV4 Address>"
                print("key bindings: ")
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's IPV4 Address></code>
                print("  Esc: Quit")
+
*# ROS_HOSTNAME = "<your development machine's hostname>"
                print("  ?: Help")
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's hostname></code>
                for key, val in sorted(bindings.items(),
 
                                      key=lambda x: x[1][2]):
 
                    print(" %s: %s" % (key, val[2]))
 
</syntaxhighlight>
 
 
 
This case is executed when the key pressed is not a valid input. So, the key is sorted and is printed along with its corresponding description.
 
 
 
<syntaxhighlight lang="python" line start="115" enclose="div">
 
def main():
 
    """RSDK Joint Position Example: Keyboard Control
 
    Use your dev machine's keyboard to control joint positions.
 
    Each key corresponds to increasing or decreasing the angle
 
    of a joint on Sawyer's arm. The increasing and descreasing
 
    are represented by number key and letter key next to the number.
 
    """
 
    epilog = """
 
See help inside the example with the '?' key for key bindings.
 
    """
 
    rp = intera_interface.RobotParams()
 
    valid_limbs = rp.get_limb_names()
 
    if not valid_limbs:
 
        rp.log_message(("Cannot detect any limb parameters on this robot. "
 
                        "Exiting."), "ERROR")
 
        return
 
    arg_fmt = argparse.RawDescriptionHelpFormatter
 
    parser = argparse.ArgumentParser(formatter_class=arg_fmt,
 
                                    description=main.__doc__,
 
                                    epilog=epilog)
 
    parser.add_argument(
 
        "-l", "--limb", dest="limb", default=valid_limbs[0],
 
        choices=valid_limbs,
 
        help="Limb on which to run the joint position keyboard example"
 
    )
 
    args = parser.parse_args(rospy.myargv()[1:])
 
 
 
    print("Initializing node... ")
 
    rospy.init_node("sdk_joint_position_keyboard")
 
    print("Getting robot state... ")
 
    rs = intera_interface.RobotEnable(CHECK_VERSION)
 
    init_state = rs.state().enabled
 
</syntaxhighlight>
 
 
 
The node is initialized and then the software version is checked for compatiblity with the local version. <code>init_state</code> captures Sawyer's initial state. This is to make sure that Sawyer is sent back to the same state after the program exits.
 
 
 
<syntaxhighlight lang="python" line start="150" enclose="div">
 
    def clean_shutdown():
 
        print("\nExiting example.")
 
        if not init_state:
 
            print("Disabling robot...")
 
            rs.disable()
 
    rospy.on_shutdown(clean_shutdown)
 
</syntaxhighlight>
 
 
 
As explained above, the robot is checked if it was initially disabled and if so, it is disabled upon the program's termination.
 
 
 
<syntaxhighlight lang="python" line start="157" enclose="div">
 
    rospy.loginfo("Enabling robot...")
 
    rs.enable()
 
    map_keyboard(args.limb)
 
    print("Done.")
 
 
 
 
 
if __name__ == '__main__':
 
    main()
 
</syntaxhighlight>
 
 
 
The robot is enabled before the programs execution. It is important to note that Sawyer should be auto-enabled before trying to move its joints. <code>map_keyboard()</code> function captures the key input and moves the joint as explained above.
 
  
 
</div>
 
</div>
Line 279: Line 114:
 
<div class="content-block">
 
<div class="content-block">
  
== Joint Position Joystick Code Walkthrough ==  
+
== Direct Network Configuration ==
  
Now, let's break down the code.
+
If you do not have a DHCP server or other networking infrastructure, or would just prefer to connect your development workstation directly to Sawyer, you can do so using the following network configuration and steps.
  
<syntaxhighlight lang="python" line start="33" enclose="div">
+
=== Switch Configuration Diagram ===
import argparse
 
  
import rospy
+
[[File:Switch_Config.png|600px]]
  
import intera_interface
+
=== Sawyer ===
import intera_external_devices
 
  
from intera_interface import CHECK_VERSION
+
In this setup, you will need to set your robot to use "Automatic" addressing, and can choose between ROS_IP and ROS_HOSTNAME.local for your ROS naming convention.
</syntaxhighlight>
+
# ROS_HOSTNAME.local is our '''recommended''' ros naming protocol for the robot in this setup.  It is very stable in this network configuration, and would only need to be adjusted if you switch to a separate subnet from the robot.
 +
#* If you can <code>ping <robot hostname>.local</code>, then the network is working correctly
 +
# ROS_IP is the most reliable naming method, if you know that the robot is going to keep its .IP.  This naming protocol works across all network configurations as long as you have reliable routing to the robot.
 +
#* If you can <code> ping <robot ip></code> then the network is working correctly.
 +
#* '''Note:''' You can set the robot to use a Static IP if you are interested in using this naming convention because you are unsure of your future network configurations, but be sure to talk to your network administrator if you are hooking your router up to a larger network.
  
This imports the intera interface for accessing the limb and the gripper class. The <code>intera_external_devices</code> is imported to use its <code>joystick</code> function, that captures the key presses on the joystick. The <code>CHECK_VERSION</code> 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.
+
=== Dev Machine ===
  
<syntaxhighlight lang="python" line start="43" enclose="div">
+
* In this network configuration, you will want to have your [[RSDK Shell|intera.sh]] script point to either:
def rotate(l):
+
*# ROS_IP = "<your development machine's IPV4 Address>"
    """
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's IPV4 Address></code>
    Rotates a list left.
+
*# ROS_HOSTNAME = "<your development machine's hostname>.local"
    @param l: the list
+
*#* Verify 2-way connectivity by [[SSH]]ing into the robot and attempting to <code>ping <your development machine's hostname>.local</code>
    """
 
    if len(l):
 
        v = l[0]
 
        l[:-1] = l[1:]
 
        l[-1] = v
 
</syntaxhighlight>
 
  
The <code>rotate</code> function rotates a list, it will be used in bindings. This moves all the elements in the list to one position to their left. So the element at 0th position moves to the nth position, element at nth position moves to (n-1)th position, and so on.
+
=== Avahi Configuration Steps: ===
  
<syntaxhighlight lang="python" line start="54" enclose="div">
+
These steps assume your ethernet connection to the robot is on <code>eth0</code>.
def set_j(cmd, limb, joints, index, delta):
+
* Shutdown Sawyer and disconnect ethernet cable.
    """
+
* Connect the laptop/workstation to Sawyer using a Category-5 ethernet cable.
    Set the selected joint to current pos + delta.
+
* Power up Sawyer.
    @param cmd: the joint command dictionary
+
* Disable firewall on laptop if necessary:
    @param limb: the limb to get the pos from
+
<source lang="bash">
    @param joints: a list of joint names
+
     $ sudo ufw disable 
    @param index: the index in the list of names
+
</source>
    @param delta: delta to update the joint by
 
    joint/index is to make this work in the bindings.
 
    """
 
    joint = joints[index]
 
     cmd[joint] = delta + limb.joint_angle(joint)
 
</syntaxhighlight>
 
  
<code>joint_angle()</code> method returns the current angle of the corresponding joint. This function builds the dictionary that is to be passed as a joint command. The last line assigns the new position which has an offset of <code>delta</code> from the current position.
+
* Turn off the Ubuntu Network-Manager to prevent interference:
 +
* Go to the Networking Icon drop-down menu in the top-right of the Desktop.
 +
* Make sure 'Enable Networking' is '''unchecked''' (if checked, select the option in the menu to disable)
  
<syntaxhighlight lang="python" line start="69" enclose="div">
+
* Check status of eth0:
def map_joystick(joystick, side):
+
<source lang="bash">
    """
+
     $ ifconfig eth0 
     Maps joystick input to joint position commands.
+
</source>
    @param joystick: an instance of a Joystick
+
''You should not see any IP addresses under 'inet'.''
    """
 
    limb = intera_interface.Limb(side)
 
    gripper = None
 
    try:
 
        gripper = intera_interface.Gripper(side)
 
    except:
 
        rospy.loginfo("Could not detect a connected electric gripper.")
 
</syntaxhighlight>
 
  
The instance of Limb class and the instance of Gripper class are created. No gripper will raise could not detect gripper info.
+
* Use Avahi to designate an IP address to eth0 (do not close the terminal after running avahi-autoipd):
 +
<source lang="bash">
 +
    $ sudo avahi-autoipd eth0 
 +
</source>
  
<syntaxhighlight lang="python" line start="82" enclose="div">
+
* Make sure an IP address is successfully claimed. eg:
     def set_g(action):
+
<source lang="bash">
        if gripper is not None:
+
     Found user 'avahi-autoipd' (UID 104) and group 'avahi-autoipd' (GID 111).
            if action == "close":
+
    Successfully called chroot().
                gripper.close()
+
    Successfully dropped root privileges.
            elif action == "open":
+
    Starting with address 169.254.8.16
                gripper.open()
+
    Callout BIND, address 169.254.8.16 on interface eth0
            elif action == "calibrate":
+
    Successfully claimed IP address 169.254.8.16
                gripper.calibrate()
+
</source>
</syntaxhighlight>
+
''Keep this Terminal running in the background.''
  
The <code>set_g</code> function calls gripper action function(<code>open()</code>, or <code>close()</code> or <code>calibrate()</code>) when corresponding action is provided.
+
* Open a '''New Terminal''' to continue.
 +
* Find the local hostname of the robot by running avahi-browse:
  
<syntaxhighlight lang="python" line start="91" enclose="div">
+
<source lang="bash">
     limb_cmd = {}
+
     $ avahi-browse -a -
 
+
</source>
    #available joints
+
    The default local hostname of the robot is the [[Serial Number]] followed by local. Ex: '011303P0017.local'.
    joints = limb.joint_names()
+
</div>
 
 
    #abbreviations
 
    jhi = lambda s: joystick.stick_value(s) > 0
 
    jlo = lambda s: joystick.stick_value(s) < 0
 
    bdn = joystick.button_down
 
    bup = joystick.button_up
 
</syntaxhighlight>
 
 
 
Create instance of joint names and abbreviations for joystick values, button up and button down. The <code>joint_names()</code> method returns a list of joints associated with the limb. The <code>stick_value()</code> method returns the dead-banded, scaled and offset value of the axis. The abbreviations for all these functions are created to be used within the bindings dictionary below.
 
 
 
<syntaxhighlight lang="python" line start="102" enclose="div">
 
    def print_help(bindings_list):
 
        print("Press Ctrl-C to quit.")
 
        for bindings in bindings_list:
 
            for (test, _cmd, doc) in bindings:
 
                if callable(doc):
 
                    doc = doc()
 
                print("%s: %s" % (str(test[1][0]), doc))
 
</syntaxhighlight>
 
 
 
The information about the joystick buttons and the corresponding joints are displayed. This information is available in <code>bindings_list</code> as below. The <code>doc</code> refers to the last element of the tuple and it is checked if it is a callable function. So, if there is a lambda function that evaluates and returns a string, then that function is called. Finally, the results are displayed.
 
 
 
<syntaxhighlight lang="python" line start="110" enclose="div">
 
    bindings_list = []
 
    bindings = [
 
        ((jlo, ['leftStickHorz']), (set_j, [limb_cmd, limb, joints, 0, 0.1]),
 
            lambda: "Increase " + joints[0]),
 
        ((jhi, ['leftStickHorz']), (set_j, [limb_cmd, limb, joints, 0, -0.1]),
 
            lambda: "Decrease " + joints[0]),
 
        ((jlo, ['leftStickVert']), (set_j, [limb_cmd, limb, joints, 1, 0.1]),
 
            lambda: "Increase " + joints[1]),
 
        ((jhi, ['leftStickVert']), (set_j, [limb_cmd, limb, joints, 1, -0.1]),
 
            lambda: "Decrease " + joints[1]),
 
        ((jlo, ['rightStickHorz']), (set_j, [limb_cmd, limb, joints, 2, 0.1]),
 
            lambda: "Increase " + joints[2]),
 
        ((jhi, ['rightStickHorz']), (set_j, [limb_cmd, limb, joints, 2, -0.1]),
 
            lambda: "Decrease " + joints[2]),
 
        ((jlo, ['rightStickVert']), (set_j, [limb_cmd, limb, joints, 3, 0.1]),
 
            lambda: "Increase " + joints[3]),
 
        ((jhi, ['rightStickVert']), (set_j, [limb_cmd, limb, joints, 3, -0.1]),
 
            lambda: "Decrease " + joints[3]),
 
        ((bdn, ['leftBumper']), (rotate, [joints]), side + ": cycle joint"),
 
        ((bdn, ['function1']), (print_help, [bindings_list]), "help"),
 
        ((bdn, ['function2']), (print_help, [bindings_list]), "help"),
 
        ]
 
    if gripper:
 
        bindings.extend([
 
            ((bdn, ['rightTrigger']), (set_g, ['close'], gripper), side + " gripper close"),
 
            ((bup, ['rightTrigger']), (set_g, ['open'], gripper), side + " gripper open"),
 
            ((bdn, ['btnLeft']), (set_g, ['calibrate'], gripper), "right calibrate")
 
            ])
 
    bindings_list.append(bindings)
 
</syntaxhighlight>
 
 
 
The first element of every tuple refers to the command that has to be executed. As indicated above <code>set_j, bdn, bup, jhi</code> and <code>jlo</code> refers to the function and their acronyms. The second tuple has a list that holds the list of arguments to be passed along with that function. The last element holds a string or a function that evaluates and forms a string to detail the joint under consideration.
 
 
 
<syntaxhighlight lang="python" line start="140" enclose="div">
 
    rate = rospy.Rate(100)
 
    print_help(bindings_list)
 
    print("Press Ctrl-C to stop. ")
 
    while not rospy.is_shutdown():
 
        for (test, cmd, doc) in bindings:
 
            if test[0](*test[1]):
 
                cmd[0](*cmd[1])
 
                if callable(doc):
 
                    print(doc())
 
                else:
 
                    print(doc)
 
        if len(limb_cmd):
 
            limb.set_joint_positions(limb_cmd)
 
            limb_cmd.clear()
 
        rate.sleep()
 
    return False
 
</syntaxhighlight>
 
 
 
The test tuple holds the function that needs to be called for a particular value to be tested. For instance, the first tuple holds the abbreviation bdn and the parameter rightTrigger that is passed along. This test function checks whether the joystick's button_down is pressed. If this is true then the cmd, that refers to the second tuple (gripper.close,[]) is parsed as above. For the first binding, the expression cmd[0](*cmd[1]) returns the function call gripper.close([]). The next line checks if the last tuple is a function (lambda) or not (string). Since the joints being controlled are updated dynamically, the lambda function is used to retrieve the current joints rj[0] and rj[1].
 
The dictionaries limb_cmd hold the joint commands for the limb. These dictionaries get populated when the set_j method is called by the previous section of code.
 
 
 
<syntaxhighlight lang="python" line start="158" enclose="div">
 
def main():
 
    """SDK Joint Position Example: Joystick Control
 
    Use a game controller to control the angular joint positions
 
    of Sawyer's arms.
 
    Attach a game controller to your dev machine and run this
 
    example along with the ROS joy_node to control the position
 
    of each joint in Sawyer's arm using the joystick. Be sure to
 
    provide your *joystick* type to setup appropriate key mappings.
 
    Each stick axis maps to a joint angle; which joints are currently
 
    controlled can be incremented by using the mapped increment buttons.
 
    Ex:
 
      (x,y -> e0,e1) >>increment>> (x,y -> e1,e2)
 
    """
 
    epilog = """
 
See help inside the example with the "Start" button for controller
 
key bindings.
 
    """
 
    rp = intera_interface.RobotParams()
 
    valid_limbs = rp.get_limb_names()
 
    if not valid_limbs:
 
        rp.log_message(("Cannot detect any limb parameters on this robot. "
 
                        "Exiting."), "ERROR")
 
        return
 
    arg_fmt = argparse.RawDescriptionHelpFormatter
 
    parser = argparse.ArgumentParser(formatter_class=arg_fmt,
 
                                    description=main.__doc__,
 
                                    epilog=epilog)
 
    required = parser.add_argument_group('required arguments')
 
    required.add_argument(
 
        '-j', '--joystick', required=True,
 
        choices=['xbox', 'logitech', 'ps3'],
 
        help='specify the type of joystick to use'
 
    )
 
    parser.add_argument(
 
        "-l", "--limb", dest="limb", default=valid_limbs[0],
 
        choices=valid_limbs,
 
        help="Limb on which to run the joint position joystick example"
 
    )
 
    args = parser.parse_args(rospy.myargv()[1:])
 
 
 
    joystick = None
 
    if args.joystick == 'xbox':
 
        joystick = intera_external_devices.joystick.XboxController()
 
    elif args.joystick == 'logitech':
 
        joystick = intera_external_devices.joystick.LogitechController()
 
    elif args.joystick == 'ps3':
 
        joystick = intera_external_devices.joystick.PS3Controller()
 
    else:
 
        parser.error("Unsupported joystick type '%s'" % (args.joystick))
 
 
 
    print("Initializing node... ")
 
    rospy.init_node("sdk_joint_position_joystick")
 
    print("Getting robot state... ")
 
    rs = intera_interface.RobotEnable(CHECK_VERSION)
 
    init_state = rs.state().enabled
 
</syntaxhighlight>
 
 
 
The type of joystick input is captured as entered by the user and an instance of the corresponding interface is created.
 
The node is initialized and the robot is enabled.
 
 
 
<syntaxhighlight lang="python" line start="217" enclose="div">
 
    def clean_shutdown():
 
        print("\nExiting example.")
 
        if not init_state:
 
            print("Disabling robot...")
 
            rs.disable()
 
    rospy.on_shutdown(clean_shutdown)
 
 
 
    rospy.loginfo("Enabling robot...")
 
    rs.enable()
 
 
 
    map_joystick(joystick, args.limb)
 
    print("Done.")
 
  
 +
<div class="content-block">
  
if __name__ == '__main__':
+
== Troubleshooting ==
    main()
 
</syntaxhighlight>
 
  
The <code>map_joystick()</code> method performs the mapping and execution of the commands as explained above.
+
See the [http://www.ros.org/wiki/ROS/NetworkSetup ROS Network Setup Guide] for common ROS network issues and helpful debugging steps to check your base ROS connectivity.
  
 
</div>
 
</div>

Revision as of 21:59, 28 November 2016

The Sawyer Research Robot uses ROS to communicate with the user's Development Workstation. This requires an ethernet network to be established between Sawyer and the Workstation with full bi-directional connectivity. If you have trouble connecting to your Sawyer, see this page for our Recommended Network Setup and other common network debugging steps.



Basics

These Network Configuration settings are available in the Field Service Menu (FSM)

Basic Requirements

Sawyer must be connected to a development workstation which uses ROS over an ethernet network to communicate bi-directionally.

Sawyer's hostname can be configured using the Field Service Menu (FSM) if you do not like the one given out at the factory which matches its serial number.

Network address assignment

Sawyer supports IPV4 addressing for the following network configurations:

  • Automatic address assignment (“Automatic” mode)
    • If a DHCP server is present in the network, the DHCP server automatically assigns a network address to the robot, and may (or may not) assign a DNS server for host name resolution.
    • If no DHCP server is present, the robot will use the Autoip protocol to assign itself a link-local address in the 169.254.0.0/16 address block.
    • All assignments in this mode are automatic, and no options can be configured manually.
  • Manual address assignment from the Field Service Menu (“Manual” mode)
    • The user must specify a valid IPV4 address for the robot, and may optionally specify a network mask, default gateway address, and DNS server address. All network options are configured manually.

Host name resolution

In all addressing modes, Sawyer provides link-local advertising of the robot’s host name as “<robot name>.local” using the Avahi mDNS service. Computers that are located in the same subnet as the robot and that support mDNS can always resolve the robot’s host name as “<robot name>.local” even if no other host name resolution service is present. Sawyer will also be able to communicate with these computers using their host names.

In “Automatic” addressing mode, Sawyer will be able to resolve external host names only if the network’s DHCP server supplies a DNS server address. If the DHCP server does not provide a DNS server address, Sawyer will only be able to communicate with external computers by their IP addresses.

In “Manual” addressing mode, Sawyer will be able to resolve external host names only if the user specifies a DNS server address (and a default gateway address if computers in a different subnet are involved). If the user does not provide a DNS server address, Sawyer will only be able to communicate with computers by their IP addresses. If the user does not provide a default gateway address, Sawyer will only be able to communicate with computers within the same subnet.

ROS Naming Conventions

Intera robots supports 3 ROS naming conventions. These control how the ROS Master publishes the access information for the individual nodes published by the robot. Regardless of how you connect to the robot, you will need to be able to reach it by the address configured by its ROS naming convention if you want to be able to interact with it. (i.e. if you can reach the robot by its IP, but it is configured with the ROS_HOSTNAME convention and you can't reach it by its hostname, then you will be unable to do meaningful work with the robot)

  • ROS_HOSTNAME.local: This publishes the nodes at <robot hostname>.local
    • This is the least versatile, but the most stable configuration, and is our recommended when it is available.
    • This convention only works in a local setup, where your robot and development machine are on the same subnet, but will almost always work in that network configuration.
    • This will only break if
      • A naming conflict is introduced on the local network or
      • You manually change the hostname of the robot.
  • ROS_IP: This publishes the nodes at the IP4 Address associated with the robot
    • This is the most versatile naming convention, but also the most fragile.
    • It will work in any network configuration where you have connectivity to the robot, but will break if the IP of the robot changes
  • ROS_HOSTNAME: This publishes the nodes at the hostname of the robot.
    • This configuration has medium versatility and stability.
    • It only works if there is a working DNS running on your network, and pointed at the robot.
    • This will only break if
      • A naming conflict is introduced in the network or
      • If the the DNS-resolved hostname of the robot is changed, which can be done by the Server admin, but is unlikely to happen.

Network Configuration through a Router

Recommended Network Configuration

The recommended network configuration is to connect your development workstation and your Sawyer to an all in one SOHO (Small Office/Home Office) router/firewall similar to the Linksys EA-Series routers. This type of router provides DHCP and other networking services, and has the benefit of keeping the network traffic off of your main network. It also allows the development workstation access to internet in particular, the Rethink Robotics github repo for code and documentation.

Router Configuration Diagram

Router Config.png

  • Sawyer and the user's computer can both be set to "Automatic" addressing mode, which will draw IP information directly from the router.

Sawyer

  • Sawyer can be set to use either ROS_IP or ROS_HOSTNAME.local:
    1. ROS_HOSTNAME.local is our recommended ros naming protocol for the robot in this setup. It is very stable in this network configuration, and would only need to be adjusted if you switch to a separate subnet from the robot.
      • If you can ping <robot hostname>.local, then the network is working correctly
    2. ROS_IP is the most reliable naming method, if you know that the robot is going to keep its .IP. This naming protocol works across all network configurations as long as you have reliable routing to the robot.
      • If you can ping <robot ip> then the network is working correctly.
      • Note: You can set the robot to use a Static IP if you are interested in using this naming convention because you are unsure of your future network configurations, but be sure to talk to your network administrator if you are hooking your router up to a larger network.

Dev Machine

  • In this network configuration, you will want to have your intera.sh script point to either:
    1. ROS_IP = "<your development machine's IPV4 Address>"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's IPV4 Address>
    2. ROS_HOSTNAME = "<your development machine's hostname>.local"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's hostname>.local

Connect Directly To Corporate Or University Network

Another viable networking configuration is to connect Sawyer directly to your corporate or university network. Here you need to make sure that a DHCP server is available, and that your name server can resolve Sawyer's hostname to an IP address. If you plan on using DHCP reservation using Sawyer's MAC address, you can get the MAC address by using TTY3.

You can set Sawyer to use either "Automatic" addressing or a static IP configuration in this configuration.

Note: If you want to set a static IP, you will need to talk to your network administrator to get an appropriate IP for you to assign, in order to avoid network collisions.

Sawyer

In this network configuration, you can use either ROS_IP or ROS_HOSTNAME ROS naming conventions

  • In this setup, we recommend using the ROS_HOSTNAME convention. This will rely on your DNS and on not having someone else register a duplicate of your hostname (so pick a unique one), but will be very stable in this network configuration.
    • If you can ping <robot hostname> then the network is working.
  • As mentioned in the previous section, ROS_IP is the most versatile ROS naming convention that works across the most network configurations, but is susceptible to automatic IP changes when a lease is exceeded.
    • If you can ping <robot IP> then the network is working.

Dev Machine

  • In this network configuration, you will want to have your intera.sh script point to either:
    1. ROS_IP = "<your development machine's IPV4 Address>"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's IPV4 Address>
    2. ROS_HOSTNAME = "<your development machine's hostname>"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's hostname>

Direct Network Configuration

If you do not have a DHCP server or other networking infrastructure, or would just prefer to connect your development workstation directly to Sawyer, you can do so using the following network configuration and steps.

Switch Configuration Diagram

Switch Config.png

Sawyer

In this setup, you will need to set your robot to use "Automatic" addressing, and can choose between ROS_IP and ROS_HOSTNAME.local for your ROS naming convention.

  1. ROS_HOSTNAME.local is our recommended ros naming protocol for the robot in this setup. It is very stable in this network configuration, and would only need to be adjusted if you switch to a separate subnet from the robot.
    • If you can ping <robot hostname>.local, then the network is working correctly
  2. ROS_IP is the most reliable naming method, if you know that the robot is going to keep its .IP. This naming protocol works across all network configurations as long as you have reliable routing to the robot.
    • If you can ping <robot ip> then the network is working correctly.
    • Note: You can set the robot to use a Static IP if you are interested in using this naming convention because you are unsure of your future network configurations, but be sure to talk to your network administrator if you are hooking your router up to a larger network.

Dev Machine

  • In this network configuration, you will want to have your intera.sh script point to either:
    1. ROS_IP = "<your development machine's IPV4 Address>"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's IPV4 Address>
    2. ROS_HOSTNAME = "<your development machine's hostname>.local"
      • Verify 2-way connectivity by SSHing into the robot and attempting to ping <your development machine's hostname>.local

Avahi Configuration Steps:

These steps assume your ethernet connection to the robot is on eth0.

  • Shutdown Sawyer and disconnect ethernet cable.
  • Connect the laptop/workstation to Sawyer using a Category-5 ethernet cable.
  • Power up Sawyer.
  • Disable firewall on laptop if necessary:
    $ sudo ufw disable
  • Turn off the Ubuntu Network-Manager to prevent interference:
  • Go to the Networking Icon drop-down menu in the top-right of the Desktop.
  • Make sure 'Enable Networking' is unchecked (if checked, select the option in the menu to disable)
  • Check status of eth0:
    $ ifconfig eth0

You should not see any IP addresses under 'inet'.

  • Use Avahi to designate an IP address to eth0 (do not close the terminal after running avahi-autoipd):
    $ sudo avahi-autoipd eth0
  • Make sure an IP address is successfully claimed. eg:
    Found user 'avahi-autoipd' (UID 104) and group 'avahi-autoipd' (GID 111).
    Successfully called chroot().
    Successfully dropped root privileges.
    Starting with address 169.254.8.16
    Callout BIND, address 169.254.8.16 on interface eth0
    Successfully claimed IP address 169.254.8.16

Keep this Terminal running in the background.

  • Open a New Terminal to continue.
  • Find the local hostname of the robot by running avahi-browse:
    $ avahi-browse -a -r
   The default local hostname of the robot is the Serial Number followed by local. Ex: '011303P0017.local'.

Troubleshooting

See the ROS Network Setup Guide for common ROS network issues and helpful debugging steps to check your base ROS connectivity.