Robot Driver#

To execute motions on a real robot, we offer a range of drivers for common robot manufacturers such as ABB, Universal Robot, or Yaskawa. All drivers share a common interface, however some features depend on specific hardware or internal software requirements and are thus only available on selected platforms. Our list of supported robots includes information about the corresponding driver. There is also a SimulatedDriver that can be used for any robot for testing, development, or visualization purposes.

All drivers subclass the base Driver class that defines the primary interface. In the following, we will guide you through this API and itā€™s general behavior.

Installation#

Please refer to each driverā€™s section for the installation procedure, as usually the robot controller needs to be prepared in a specific way. For installing the code that is running on the control PC, you can either download the Python package via pip install jacobi-<...>-driver or the C++ Debian package from our downloads page.

Connect to a Robot#

A driver is constructed by passing a Planner as well as information on how to connect to the robot:

driver = Driver(planner, host='192.168.1.130')

Usually, this is the robotā€™s IP address and/or the port to connect to. For some robots, networking configuration needs to be set on the controller side (e.g. for ABB). Please make sure to have a direct connection to your robot, and also be careful about firewall settings on your control PC.

The planner is used to plan motions on-the-fly (e.g. a stop trajectory for braking) within the specified obstacle environment and with the defined robot limits.

On construction, the driver will first try to connect to the robot. If the connection fails, e.g. because of the default timeout of 2s, a JacobiError is raised. Second, the driver will enable the robot for subsequent motions, e.g. by turning the motors on. Then, you can check and change the connection status via

is_connected = driver.is_connected  # Should be true

driver.disconnect()  # E.g. to disconnect on purpose
is_connected = driver.reconnect()

The reconnect method needs to be called to reconnect after an external disconnect event.

Simulated Driver

The simulated driver can connect to Studio Live to visualize the robot motions easily. To connect to a SimulatedDriver, enable the sync_with_studio parameter in the constructor. Alternatively, you can log to the console via log_stream.

from jacobi.drivers import SimulatedDriver

driver = SimulatedDriver(planner, sync_with_studio=True)

Read the Robotā€™s State#

Given an established connection, you can read the robotā€™s current state or just the joint position by

state = driver.get_current_state()

position = driver.get_current_joint_position()

Reading from the robot also works while executing a motion. Whether the robot is currently executing a motion, the is_running property can be used.

Run a Trajectory#

Given a trajectory that was planned beforehand, the easiest way to execute it on a real robot is by

# Plan offline
trajectory = planner.plan('motion-1')

result = driver.run(trajectory)

which blocks the code and waits for the robot to finish the motion. The result type holds information about the success or possible errors of the exeuction. This method requires the trajectory to start at the current position of the robot, otherwise a ErrorStartPositionDeviation result is returned. In general, the driver doesnā€™t make use of exceptions due to its real-time capability.

Asynchronous Programming

Asynchronous programming is very useful for applications interfacing with the real world. In C++, the motion library is based on std::future and std::promise ecosystem of the standard library. We added a small wrapper called FutureResult to make it easier to use.

For Python, we integrated our drivers into the asyncio standard library. This makes asynchronous execution very easy using the async / await commands.

You can run a trajectory asynchronously via to supervise the motion execution, control external IO, or change the motion while the robot is moving.

future_result = driver.run_async(trajectory)

# Do something here, e.g. change the motion on-the-fly or control some external IO
await future_result

The await method then blocks code execution until the motion is completed. Besides the run method, there are a few more methods to move the robot - and all have an asynchronous counterpart with an _async suffix.

Stop the Robot#

To stop the current motion of the robot, simply call

driver.stop()

e.g. during an asynchronous motion. We differentiate between a fast and soft stop, with the fast stop being the default. The fast stop sends - if available - a native stop signal to the controller which will then take over the braking motion. Alternativel, a soft stop is calculated and executed by our driver: It executes a time-optimal trajectory to zero velocity and zero acceleration within the specified limits of the robot. This is of course completely independent of any safety or emergency stops, which are always on the controller level. Again, there is also an asynchronous variant called stop_async().

Move To a Goal#

To plan and execute motions easily starting from the current joint position of the robot, the driver includes the move_to command:

# The goal point can be a Config, Waypoint, Frame, or CartesianWaypoint.
point = Frame(x=0.3, y=0.2, z=0.3)
driver.move_to(point)

This is why we pass the Planner to the driver - to have a unified interface for planning and executing.

Plan On-the-fly#

The real power of our motion planner is itā€™s capability to plan motions on-the-fly. Then, calling

# Set a new goal
new_goal = Frame(x=0.5, y=0.2, z=0.3)  # [m]

# And move there by either
await driver.move_to_async(new_goal)

will overwrite the current motion of the robot, plan a new motion using the planner object that we passed for construction, and run the resulting trajectory to the new goal (either synchronously or asynchronously). In particular, the new motion starts at the current state of the robot that might have a non-zero current velocity.

Real-time Capability#

In general, the motion planning must be real-time capable within a single control cycle to allow for planning while the robot is moving. Just to clarify, the motion planning is still allowed to fail (e.g. as a valid collision-free motion might not even exist), however it must report this within the control cycle.

The planner of our Jacobi Motion library guarantees real-time capability if either:

  1. There are no obstacles within the environment, including end-effector or item obstacles of the robot. This also holds true if the obstacles are far away from the robotā€™s trajectory, so that a direct motion is not blocked. To enforce this behaviour, all move_to() or move_to_async() methods take the boolean ignore_collisions parameter as input that guarantees a real-time computation if set to true. In this case, we guarantee a time-optimal solution as well.

  2. The motion was trained beforehand, and the planner has loaded the corresponding motion plan.

Pre-defined Motions#

We can use the motion interface to run pre-defined motions on-the-fly. Then, we query a motion given its name and an optional goal point in case a region was specified:

driver.move_along('motion-1', new_goal);

This allows to use all the motion parameters for executing complex motions.