From 0d1697af4741ade90149d0a5db46e1cb02181a5b Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Thu, 29 Jun 2023 17:14:00 +0200 Subject: [PATCH 1/4] Add gitignore file --- .gitignore | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1deaf98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +# My Files +test.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +.vscode +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ From 3115397ca0873448006eaa61bbeb97dd8a831d71 Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Thu, 29 Jun 2023 17:14:28 +0200 Subject: [PATCH 2/4] Update abb script to work with python 3 --- abb_node/packages/abb_communications/abb.py | 324 ++++++++++---------- 1 file changed, 165 insertions(+), 159 deletions(-) diff --git a/abb_node/packages/abb_communications/abb.py b/abb_node/packages/abb_communications/abb.py index 4e411e5..840f2a0 100755 --- a/abb_node/packages/abb_communications/abb.py +++ b/abb_node/packages/abb_communications/abb.py @@ -1,4 +1,4 @@ -''' +""" Michael Dawson-Haggerty abb.py: contains classes and support functions which interact with an ABB Robot running our software stack (RAPID code module SERVER) @@ -7,10 +7,10 @@ For functions which require targets (XYZ positions with quaternion orientation), targets can be passed as [[XYZ], [Quats]] OR [XYZ, Quats] -''' +""" import socket -import json +import json import time import inspect from threading import Thread @@ -19,175 +19,173 @@ log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) - -class Robot: - def __init__(self, - ip = '192.168.125.1', - port_motion = 5000, - port_logger = 5001): - self.delay = .08 + +class Robot: + def __init__(self, ip="192.168.125.1", port_motion=5000, port_logger=5001): + self.delay = 0.08 self.connect_motion((ip, port_motion)) - #log_thread = Thread(target = self.get_net, + # log_thread = Thread(target = self.get_net, # args = ((ip, port_logger))).start() - - self.set_units('millimeters', 'degrees') + + self.ip = ip + self.port_motion = port_motion + self.set_units("millimeters", "degrees") self.set_tool() self.set_workobject() self.set_speed() self.set_zone() - def connect_motion(self, remote): - log.info('Attempting to connect to robot motion server at %s', str(remote)) + def connect_motion(self, remote): + log.info("Attempting to connect to robot motion server at %s", str(remote)) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(2.5) self.sock.connect(remote) self.sock.settimeout(None) - log.info('Connected to robot motion server at %s', str(remote)) + log.info("Connected to robot motion server at %s", str(remote)) def connect_logger(self, remote, maxlen=None): - self.pose = deque(maxlen=maxlen) + self.pose = deque(maxlen=maxlen) self.joints = deque(maxlen=maxlen) - + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(remote) s.setblocking(1) try: while True: data = map(float, s.recv(4096).split()) - if int(data[1]) == 0: + if int(data[1]) == 0: self.pose.append([data[2:5], data[5:]]) - #elif int(data[1]) == 1: self.joints.append([a[2:5], a[5:]]) + # elif int(data[1]) == 1: self.joints.append([a[2:5], a[5:]]) finally: s.shutdown(socket.SHUT_RDWR) def set_units(self, linear, angular): - units_l = {'millimeters': 1.0, - 'meters' : 1000.0, - 'inches' : 25.4} - units_a = {'degrees' : 1.0, - 'radians' : 57.2957795} + units_l = {"millimeters": 1.0, "meters": 1000.0, "inches": 25.4} + units_a = {"degrees": 1.0, "radians": 57.2957795} self.scale_linear = units_l[linear] - self.scale_angle = units_a[angular] + self.scale_angle = units_a[angular] def set_cartesian(self, pose): - ''' + """ Executes a move immediately from the current pose, to 'pose', with units of millimeters. - ''' - msg = "01 " + self.format_pose(pose) + """ + msg = "01 " + self.format_pose(pose) return self.send(msg) def set_joints(self, joints): - ''' + """ Executes a move immediately, from current joint angles, - to 'joints', in degrees. - ''' - if len(joints) != 6: return False + to 'joints', in degrees. + """ + if len(joints) != 6: + return False msg = "02 " - for joint in joints: msg += format(joint*self.scale_angle, "+08.2f") + " " - msg += "#" + for joint in joints: + msg += format(joint * self.scale_angle, "+08.2f") + " " + msg += "#" return self.send(msg) def get_cartesian(self): - ''' + """ Returns the current pose of the robot, in millimeters - ''' + """ msg = "03 #" data = self.send(msg).split() r = [float(s) for s in data] return [r[2:5], r[5:9]] def get_joints(self): - ''' - Returns the current angles of the robots joints, in degrees. - ''' + """ + Returns the current angles of the robots joints, in degrees. + """ msg = "04 #" data = self.send(msg).split() return [float(s) / self.scale_angle for s in data[2:8]] def get_external_axis(self): - ''' + """ If you have an external axis connected to your robot controller (such as a FlexLifter 600, google it), this returns the joint angles - ''' + """ msg = "05 #" data = self.send(msg).split() return [float(s) for s in data[2:8]] - + def get_robotinfo(self): - ''' + """ Returns a robot- unique string, with things such as the - robot's model number. + robot's model number. Example output from and IRB 2400: ['24-53243', 'ROBOTWARE_5.12.1021.01', '2400/16 Type B'] - ''' + """ msg = "98 #" - data = str(self.send(msg))[5:].split('*') - log.debug('get_robotinfo result: %s', str(data)) + data = str(self.send(msg))[5:].split("*") + log.debug("get_robotinfo result: %s", str(data)) return data - def set_tool(self, tool=[[0,0,0], [1,0,0,0]]): - ''' - Sets the tool centerpoint (TCP) of the robot. - When you command a cartesian move, + def set_tool(self, tool=[[0, 0, 0], [1, 0, 0, 0]]): + """ + Sets the tool centerpoint (TCP) of the robot. + When you command a cartesian move, it aligns the TCP frame with the requested frame. - + Offsets are from tool0, which is defined at the intersection of the tool flange center axis and the flange face. - ''' - msg = "06 " + self.format_pose(tool) + """ + msg = "06 " + self.format_pose(tool) self.send(msg) self.tool = tool def load_json_tool(self, file_obj): - if file_obj.__class__.__name__ == 'str': - file_obj = open(filename, 'rb'); + if file_obj.__class__.__name__ == "str": + file_obj = open(file_obj, "rb") tool = check_coordinates(json.load(file_obj)) self.set_tool(tool) - - def get_tool(self): - log.debug('get_tool returning: %s', str(self.tool)) + + def get_tool(self): + log.debug("get_tool returning: %s", str(self.tool)) return self.tool - def set_workobject(self, work_obj=[[0,0,0],[1,0,0,0]]): - ''' + def set_workobject(self, work_obj=[[0, 0, 0], [1, 0, 0, 0]]): + """ The workobject is a local coordinate frame you can define on the robot, - then subsequent cartesian moves will be in this coordinate frame. - ''' - msg = "07 " + self.format_pose(work_obj) + then subsequent cartesian moves will be in this coordinate frame. + """ + msg = "07 " + self.format_pose(work_obj) self.send(msg) - def set_speed(self, speed=[100,50,50,50]): - ''' + def set_speed(self, speed=[100, 50, 50, 50]): + """ speed: [robot TCP linear speed (mm/s), TCP orientation speed (deg/s), external axis linear, external axis orientation] - ''' - - if len(speed) != 4: return False - msg = "08 " - msg += format(speed[0], "+08.1f") + " " - msg += format(speed[1], "+08.2f") + " " - msg += format(speed[2], "+08.1f") + " " - msg += format(speed[3], "+08.2f") + " #" + """ + + if len(speed) != 4: + return False + msg = "08 " + msg += format(speed[0], "+08.1f") + " " + msg += format(speed[1], "+08.2f") + " " + msg += format(speed[2], "+08.1f") + " " + msg += format(speed[3], "+08.2f") + " #" self.send(msg) - def set_zone(self, - zone_key = 'z1', - point_motion = False, - manual_zone = []): - zone_dict = {'z0' : [.3,.3,.03], - 'z1' : [1,1,.1], - 'z5' : [5,8,.8], - 'z10' : [10,15,1.5], - 'z15' : [15,23,2.3], - 'z20' : [20,30,3], - 'z30' : [30,45,4.5], - 'z50' : [50,75,7.5], - 'z100': [100,150,15], - 'z200': [200,300,30]} - ''' + def set_zone(self, zone_key="z1", point_motion=False, manual_zone=[]): + zone_dict = { + "z0": [0.3, 0.3, 0.03], + "z1": [1, 1, 0.1], + "z5": [5, 8, 0.8], + "z10": [10, 15, 1.5], + "z15": [15, 23, 2.3], + "z20": [20, 30, 3], + "z30": [30, 45, 4.5], + "z50": [50, 75, 7.5], + "z100": [100, 150, 15], + "z200": [200, 300, 30], + } + """ Sets the motion zone of the robot. This can also be thought of as the flyby zone, AKA if the robot is going from point A -> B -> C, how close do we have to pass by B to get to C @@ -203,44 +201,44 @@ def set_zone(self, pzone_ori: mm, radius from goal where robot tool orientation is not rigidly constrained zone_ori: degrees, zone size for the tool reorientation - ''' + """ - if point_motion: - zone = [0,0,0] - elif len(manual_zone) == 3: + if point_motion: + zone = [0, 0, 0] + elif len(manual_zone) == 3: zone = manual_zone - elif zone_key in zone_dict.keys(): + elif zone_key in zone_dict.keys(): zone = zone_dict[zone_key] - else: return False - - msg = "09 " + else: + return False + + msg = "09 " msg += str(int(point_motion)) + " " - msg += format(zone[0], "+08.4f") + " " - msg += format(zone[1], "+08.4f") + " " - msg += format(zone[2], "+08.4f") + " #" + msg += format(zone[0], "+08.4f") + " " + msg += format(zone[1], "+08.4f") + " " + msg += format(zone[2], "+08.4f") + " #" self.send(msg) def buffer_add(self, pose): - ''' + """ Appends single pose to the remote buffer Move will execute at current speed (which you can change between buffer_add calls) - ''' - msg = "30 " + self.format_pose(pose) + """ + msg = "30 " + self.format_pose(pose) self.send(msg) def buffer_set(self, pose_list): - ''' + """ Adds every pose in pose_list to the remote buffer - ''' + """ self.clear_buffer() - for pose in pose_list: + for pose in pose_list: self.buffer_add(pose) if self.buffer_len() == len(pose_list): - log.debug('Successfully added %i poses to remote buffer', - len(pose_list)) + log.debug("Successfully added %i poses to remote buffer", len(pose_list)) return True else: - log.warn('Failed to add poses to remote buffer!') + log.warn("Failed to add poses to remote buffer!") self.clear_buffer() return False @@ -248,109 +246,117 @@ def clear_buffer(self): msg = "31 #" data = self.send(msg) if self.buffer_len() != 0: - log.warn('clear_buffer failed! buffer_len: %i', self.buffer_len()) - raise NameError('clear_buffer failed!') + log.warn("clear_buffer failed! buffer_len: %i", self.buffer_len()) + raise NameError("clear_buffer failed!") return data def buffer_len(self): - ''' + """ Returns the length (number of poses stored) of the remote buffer - ''' + """ msg = "32 #" data = self.send(msg).split() return int(float(data[2])) def buffer_execute(self): - ''' + """ Immediately execute linear moves to every pose in the remote buffer. - ''' + """ msg = "33 #" return self.send(msg) - def set_external_axis(self, axis_unscaled=[-550,0,0,0,0,0]): - if len(axis_values) != 6: return False + def set_external_axis(self, axis_unscaled=[-550, 0, 0, 0, 0, 0]): + if len(axis_unscaled) != 6: + return False msg = "34 " - for axis in axis_values: - msg += format(axis, "+08.2f") + " " - msg += "#" + for axis in axis_unscaled: + msg += format(axis, "+08.2f") + " " + msg += "#" return self.send(msg) def move_circular(self, pose_onarc, pose_end): - ''' - Executes a movement in a circular path from current position, + """ + Executes a movement in a circular path from current position, through pose_onarc, to pose_end - ''' - msg_0 = "35 " + self.format_pose(pose_onarc) + """ + msg_0 = "35 " + self.format_pose(pose_onarc) msg_1 = "36 " + self.format_pose(pose_end) data = self.send(msg_0).split() - if data[1] != '1': - log.warn('move_circular incorrect response, bailing!') + if data[1] != "1": + log.warn("move_circular incorrect response, bailing!") return False return self.send(msg_1) def set_dio(self, value, id=0): - ''' + """ A function to set a physical DIO line on the robot. For this to work you're going to need to edit the RAPID function - and fill in the DIO you want this to switch. - ''' - msg = '97 ' + str(int(bool(value))) + ' #' - return - #return self.send(msg) - + and fill in the DIO you want this to switch. + """ + msg = "97 " + str(int(bool(value))) + " #" + return + # return self.send(msg) + def send(self, message, wait_for_response=True): - ''' + """ Send a formatted message to the robot socket. if wait_for_response, we wait for the response and return it - ''' + """ caller = inspect.stack()[1][3] - log.debug('%-14s sending: %s', caller, message) - self.sock.send(message) + log.debug("%-14s sending: %s", caller, message) + self.sock.sendto(message.encode(), (self.ip, self.port_motion)) time.sleep(self.delay) - if not wait_for_response: return + if not wait_for_response: + return data = self.sock.recv(4096) - log.debug('%-14s recieved: %s', caller, data) + log.debug("%-14s recieved: %s", caller, data) return data - + def format_pose(self, pose): pose = check_coordinates(pose) - msg = '' + msg = "" for cartesian in pose[0]: - msg += format(cartesian * self.scale_linear, "+08.1f") + " " + msg += format(cartesian * self.scale_linear, "+08.1f") + " " for quaternion in pose[1]: - msg += format(quaternion, "+08.5f") + " " - msg += "#" - return msg - + msg += format(quaternion, "+08.5f") + " " + msg += "#" + return msg + def close(self): self.send("99 #", False) self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() - log.info('Disconnected from ABB robot.') + log.info("Disconnected from ABB robot.") def __enter__(self): return self - + def __exit__(self, type, value, traceback): self.close() + def check_coordinates(coordinates): - if ((len(coordinates) == 2) and - (len(coordinates[0]) == 3) and - (len(coordinates[1]) == 4)): + if ( + (len(coordinates) == 2) + and (len(coordinates[0]) == 3) + and (len(coordinates[1]) == 4) + ): return coordinates - elif (len(coordinates) == 7): + elif len(coordinates) == 7: return [coordinates[0:3], coordinates[3:7]] - log.warn('Recieved malformed coordinate: %s', str(coordinates)) - raise NameError('Malformed coordinate!') + log.warn("Recieved malformed coordinate: %s", str(coordinates)) + raise NameError("Malformed coordinate!") + -if __name__ == '__main__': - formatter = logging.Formatter("[%(asctime)s] %(levelname)-7s (%(filename)s:%(lineno)3s) %(message)s", "%Y-%m-%d %H:%M:%S") +if __name__ == "__main__": + formatter = logging.Formatter( + "[%(asctime)s] %(levelname)-7s (%(filename)s:%(lineno)3s) %(message)s", + "%Y-%m-%d %H:%M:%S", + ) handler_stream = logging.StreamHandler() handler_stream.setFormatter(formatter) handler_stream.setLevel(logging.DEBUG) - log = logging.getLogger('abb') + log = logging.getLogger("abb") log.setLevel(logging.DEBUG) log.addHandler(handler_stream) - From 6d459e4d572b91ce2a95d449a1a115eace5b22a1 Mon Sep 17 00:00:00 2001 From: milistu <81291567+Milutin-S@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:39:01 +0200 Subject: [PATCH 3/4] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23b1fba..e1faca2 100755 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# open-abb-driver -### Control ABB robots remotely with ROS, Python, or C++ +# open-abb-driver | Python 3 Upgrade 🚀 +### Control ABB robots remotely with ROS, Python 3, or C++ ## What is it? -open-abb-driver consists of two main parts. The first is a program which is written in the ABB robot control language, RAPID, which allows remote clients to send requests for actions (such as joint moves, cartesian moves, speed changes, etc.). The second is a series of libraries to interact with the robot from remote computers, using several different control schemes. You can use the ROS driver, which allows control using ROS services and publishers. You can also include the Python or C++ libraries to communicate with the robot directly (both located in abb_node/packages/abb_comm), and bypass ROS completely. +open-abb-driver consists of two main parts. The first is a program that is written in the ABB robot control language, RAPID, which allows remote clients to send requests for actions (such as joint moves, cartesian moves, speed changes, etc.). The second is a series of libraries to interact with the robot from remote computers, using several different control schemes. You can use the ROS driver, which allows control using ROS services and publishers. You can also include the Python or C++ libraries to communicate with the robot directly (both located in abb_node/packages/abb_comm), and bypass ROS completely. ## Requirements * ABB IRC5 controller From 2c42ed71ce3f19a980ff67f1e8d87cd1d761a893 Mon Sep 17 00:00:00 2001 From: Milutin-S Date: Thu, 29 Jun 2023 17:48:21 +0200 Subject: [PATCH 4/4] Update README --- README.md | 57 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index e1faca2..3b8eb83 100755 --- a/README.md +++ b/README.md @@ -1,37 +1,42 @@ # open-abb-driver | Python 3 Upgrade 🚀 -### Control ABB robots remotely with ROS, Python 3, or C++ - +### Control ABB robots remotely with ROS, Python, or C++ ## What is it? -open-abb-driver consists of two main parts. The first is a program that is written in the ABB robot control language, RAPID, which allows remote clients to send requests for actions (such as joint moves, cartesian moves, speed changes, etc.). The second is a series of libraries to interact with the robot from remote computers, using several different control schemes. You can use the ROS driver, which allows control using ROS services and publishers. You can also include the Python or C++ libraries to communicate with the robot directly (both located in abb_node/packages/abb_comm), and bypass ROS completely. + +open-abb-driver consists of two main parts. The first is a program that is written in the ABB robot control language, RAPID, which allows remote clients to send requests for actions (such as joint moves, cartesian moves, speed changes, etc.). The second is a series of libraries to interact with the robot from remote computers, using several different control schemes. You can use the ROS driver, which allows control using ROS services and publishers. You can also include the Python or C++ libraries to communicate with the robot directly (both located in abb_node/packages/abb_comm), and bypass ROS completely. ## Requirements -* ABB IRC5 controller -* 6 DOF robotic manipulator -* Robot must have the following factory software options - * "PC Interface" - * "Multitasking" (required for position feedback stream) + +- ABB IRC5 controller +- 6 DOF robotic manipulator +- Robot must have the following factory software options + - "PC Interface" + - "Multitasking" (required for position feedback stream) ## Quick Start + ### Robot Setup -* Install the RAPID module 'SERVER' - * Using RobotStudio online mode is the easiest way to do this, check out the [wiki article](https://github.com/robotics/open-abb-driver/wiki/Configuring-an-ABB-Robot-for-OAD) for details. -* For position feedback, install the RAPID module 'LOGGER' into another task. -* In SERVER.mod, check to make sure the "ipController" specified is the same as your robot. The default robot IP is 192.168.125.1 -* Start the programs on the robot - * Production Window->PP to Main, then press the play button. + +- Install the RAPID module 'SERVER' + - Using RobotStudio online mode is the easiest way to do this, check out the [wiki article](https://github.com/robotics/open-abb-driver/wiki/Configuring-an-ABB-Robot-for-OAD) for details. +- For position feedback, install the RAPID module 'LOGGER' into another task. +- In SERVER.mod, check to make sure the "ipController" specified is the same as your robot. The default robot IP is 192.168.125.1 +- Start the programs on the robot + - Production Window->PP to Main, then press the play button. ### Computer Setup -* Verify that your computer is on the same subnet as the robot. - * Try pinging the robot (default IP is 192.168.125.1). -* Before trying ROS, it's pretty easy to check functionality using the [simple python interface.](https://github.com/robotics/open-abb-driver/wiki/Python-Control) - * Note that you must either copy abb_node/packages/abb_comm/abb.py to your local directory or somewhere included in your PYTHONPATH environment. -* To set up the ROS node (Fuerte only at the moment), copy abb_node to somewhere in your $ROS_PACKAGE_PATH. - * If you did that correctly, try: - - ``` - roscd abb_node - rosmake abb_node - roslaunch abb_node abb_tf.launch - ``` + +- Verify that your computer is on the same subnet as the robot. + - Try pinging the robot (default IP is 192.168.125.1). +- Before trying ROS, it's pretty easy to check functionality using the [simple python interface.](https://github.com/robotics/open-abb-driver/wiki/Python-Control) + - Note that you must either copy abb_node/packages/abb_comm/abb.py to your local directory or somewhere included in your PYTHONPATH environment. +- To set up the ROS node (Fuerte only at the moment), copy abb_node to somewhere in your $ROS_PACKAGE_PATH. + + - If you did that correctly, try: + + ``` + roscd abb_node + rosmake abb_node + roslaunch abb_node abb_tf.launch + ```