Source code for ur_cb2.cb2_robot

"""Control sending and receiving of basic motion commands with a UR robot.

The module is designed to work with a CB2 robot running SW 1.8. This module
only gives access to higher level commands. Lower level commands can be
accessed through the cb2_send and cb2_receive modules."""

# The MIT License (MIT)
#
# Copyright (c) 2016 GTRC.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import Queue
import socket
import time

import send.cb2_send as cb2_send
import receive.cb2_receive as cb2_receive


[docs]class Goal(object): """Holds movement goals Attributes: pose: A 6 float tuple or list describing the desired robot pose in either joint or cartesian coordinates cartesian: A boolean describing whether the pose is in joint (False) or cartesian (True) space move_type: A string describing the movement type. Valid values are: 'linear', 'joint', 'process' velocity: Float, the desired velocity for the specified move. For linear and process this is in m/s, for joint this is in rad/s acceleration: Float, the desired velocity for the specified move. For linear and process this is in m/s^2, for joint this is in rad/s^2 radius: Float, the blend radius in m for bending moves. Note, a radius will result in inaccurate positioning and so the robot will never reach its goal. """
[docs] def __init__(self, pose, cartesian, move_type, velocity=0.3, acceleration=1.3, radius=0.0): """Create a goal object. Args: pose (6 float tuple or list): Describe the desired robot pose in either joint or cartesian coordinates cartesian (boolean): Describe whether the pose is in joint (False) or cartesian (True) space move_type (string): Describe the movement type. Valid values are: 'linear', 'joint', 'process' velocity (float): The desired velocity for the specified move. For linear and process this is in m/s, for joint this is in rad/s acceleration (float): The desired velocity for the specified move. For linear and process this is in m/s^2, for joint this is in rad/s^2 radius (float): The radius to use for bends. Note, a radius will result in inaccurate positioning and so the robot will never reach its goal. Raises: ValueError: The move type was not a valid value ('linear', 'joint', 'process') """ self.pose = pose self.cartesian = cartesian if move_type not in ('linear', 'joint', 'process'): raise ValueError('move_type must be: linear, joint or process') self.move_type = move_type self.velocity = velocity self.acceleration = acceleration self.radius = radius
[docs]class URRobot(object): """A class to roll up all communication with a UR robot. Exposes some of the most commonly used commands made available by cb2_send and cb2_receive. To get full access to all commands, you will need to access those classes directly. The URRobot supports use by the with statement, and should be used as such, ex:: with URRobot(host, port) as robot: Do stuff... Attributes: __socket: socket.socket which is used to communicate with the robot receiver: cb2_receive.URReceiver which receives data from the robot on a separate thread sender: cb2_send.URSender which sends commands to the robot error: Float which defines the error around a point from which it is acceptable to move to the next point. goals: Queue.Queue of Goal objects defining movement goals current_goal: Goal, the current movement goal. """ sleep_time = 0.001
[docs] def __init__(self, ip, port, verbose=False): """Construct a UR Robot connection to send commands Args: ip (str): The IP address to find the Robot port (int): The port to connect to on the robot ( 3001:primary client, 3002:secondary client, 3003: real time client) verbose (bool): Whether to print information to the terminal """ self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__socket.connect((ip, port)) self.receiver = cb2_receive.URReceiver(self.__socket, verbose) self.sender = cb2_send.URSender(self.__socket, verbose) self.error = 0.0 self.goals = Queue.Queue() self.receiver.start() self.current_goal = None
def __del__(self): """Destructor for the ur_cb2 class Specifically, this stops the threads in the receiver. """ self.receiver.stop() self.__socket.close()
[docs] def move_on_error(self, multiplier=None): """Moves the robot once the robot is within error of the next move. Args: multiplier (float): Defines how far the path should be set to overshoot, this can be useful for preventing deceleration between moves. Raises: ValueError: The error value is not valid. """ if self.error <= self.current_goal.radius: raise ValueError('The error value must be greater than the radius') if (not self.goals.empty()) and (self.current_goal is not None): while not self.receiver.at_goal( self.current_goal.pose, self.current_goal.cartesian, self.error): time.sleep(self.sleep_time) if multiplier is None: self.move_now() else: self.move_now(multiplier)
[docs] def move_on_stop(self): """Moves once the robot stops. Note, the robot can be stopped both at the beginning and end of its path, therefore, this function also checks whether the robot is at its current goal. """ if not self.goals.empty(): while not (self.receiver.is_stopped() and ( self.current_goal is None or self.receiver.at_goal(self.current_goal.pose, self.current_goal.cartesian, 0.01))): time.sleep(self.sleep_time) self.move_now()
[docs] def add_goal(self, goal): """Adds a goal to the robots movement queue Args: goal (Goal): The goal to add to the queue Raises: TypeError: A Goal was not passed in """ if not isinstance(goal, Goal): raise TypeError('Requires the goal be of type Goal') self.goals.put(goal)
[docs] def clear_goals(self): """Clears the goal queue. Allows a user to directly specify the next move. """ with self.goals.mutex: self.goals.queue.clear()
[docs] def at_goal(self): """Return whether the robot is at the goal Returns: Boolean, whether the robot is at its goal point """ return self.receiver.at_goal(self.current_goal.pose, self.current_goal.cartesian)
[docs] def is_stopped(self): """Return whether the robot is stopped. Returns: Boolean, whether the robot is stopped. """ return self.receiver.is_stopped()
[docs] def move_now(self, multiplier=None): """Moves the robot. Will immediately move the robot when called, cancelling out any other motions that may be in progress. The only caveat is that there is a time cost to this call in so far as that the TCP system can only send 125 packets per second. If you call move_now too often, it will queue up calls and not move now. Args: multiplier (float): An optional multiplier on the path goal which will make the robot move past the goal to allow better blending of moves. It is ignored if None. """ self.current_goal = self.goals.get() if multiplier is not None: with self.receiver.lock: current_position = tuple( self.receiver.position if self.current_goal.cartesian else self.receiver.actual_joint_positions) move_goal = cb2_send.scale_path(current_position, self.current_goal.pose, multiplier) else: move_goal = self.current_goal.pose self.sender.radius = self.current_goal.radius if self.current_goal.move_type == 'joint': self.sender.a_joint = self.current_goal.acceleration self.sender.v_joint = self.current_goal.velocity else: self.sender.a_tool = self.current_goal.acceleration self.sender.v_tool = self.current_goal.velocity if self.current_goal.move_type == 'joint': self.sender.move_joint(move_goal, cartesian=self.current_goal.cartesian) if self.current_goal.move_type == 'linear': self.sender.move_line(move_goal, cartesian=self.current_goal.cartesian) if self.current_goal.move_type == 'process': self.sender.move_process(move_goal, cartesian=self.current_goal.cartesian)
def __enter__(self): """Enters the URRobot from a with statement""" return self def __exit__(self, *_): """Exits at the end of a context manager statement by destructing.""" self.__del__()