Skip to content
Python REST API Client for Pulse Robotic Arm
Branch: master
Clone or download
v1-wizard Merge branch 'develop' into 'master'
Release 1.4.3 to master

See merge request rozum-soft/libraries/pulse-api!6
Latest commit 906f845 Jun 14, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
pulseapi cleanup import Apr 30, 2019
.gitignore add .idea to ignore Apr 19, 2019
.gitlab-ci.yml ci trigger on tags Jun 14, 2019
MANIFEST.in add code Apr 19, 2019
README.md fix links May 4, 2019
requirements.txt update requirements Jun 14, 2019
setup.py bump package versions Jun 14, 2019

README.md

Pulse Robot Python API

This folder contains Python wrapper for the Pulse Robot REST API. Tested with Python 3. Compatibility with Python 2 is not guaranteed but the underlying API (called pdhttp) supports Python 2.

Requirements

Python 3.4+

Installation

To get the latest version, use the following command:

pip install pulse-api -i https://pip.rozum.com/simple

To install a specific version:

pip install pulse-api==v1.v2.v3 -i https://pip.rozum.com/simple where v1, v2, and v3 (e.g., pulse-api==1.4.3) are version numbers as listed below in the compatibility table.

Note: To install the underlying API (pdhttp), use: pip install pdhttp --trusted-host pip.rozum.com -i https://pip.rozum.com/simple

Software compatibility table

Pulse Desk UI version Python API version
1.4.3 1.4.3

Getting started

Examples use the latest version of the library.

Examples:

Documentation and further information

Quickstart

WARNING! Before launching this example, make sure that there are no facilities within 0.6 meters around the manipulator.

import math
from pulseapi import RobotPulse, pose, position, PulseApiException, MT_LINEAR

host = '127.0.0.1:8080'  # replace with a valid robot address
robot = RobotPulse(host)  # create an instance of the API wrapper class

# create motion targets
home_pose = pose([0 -90, 0, -90, -90, 0])
start_pose = pose([0, -90, 90, -90, -90, 0])
pose_targets = [
    pose([10, -90, 90, -90, -90, 0]),
    pose([10 -90, 0, -90, -90, 0]),
]
position_target = position([-0.42, -0.12, 0.35], [math.pi, 0, 0])
position_targets = [
    position([-0.37, -0.12, 0.35], [math.pi, 0, 0]),
    position([-0.42, -0.12, 0.35], [math.pi, 0, 0]),
    position([-0.42, -0.17, 0.35], [math.pi, 0, 0]),
    position([-0.37, -0.17, 0.35], [math.pi, 0, 0]),
]

SPEED = 30  # set the desired speed

while True:
    try:
        robot.set_pose(home_pose, SPEED)
        robot.await_motion()  # checks every 0.1 s whether the motion is finished 
        
        robot.set_pose(start_pose, SPEED)
        robot.await_motion()
        
        robot.set_position(position_target, SPEED)
        robot.await_motion()
        
        # command the robot to go through multiple position waypoints (execute a trajectory)
        robot.run_positions(position_targets, SPEED)
        robot.await_motion()
        
        # set the linear motion type
        robot.run_positions(position_targets, SPEED, motion_type=MT_LINEAR)
        robot.await_motion()
        
        # limit the TCP velocity not to exceed 0.01 m/s (1 cm/s)
        robot.run_positions(position_targets, SPEED, 
                            motion_type=MT_LINEAR, tcp_max_velocity=0.01)
        robot.await_motion(0.5)  # checks every 0.5 s whether the motion is finished
        
        # limit the TCP velocity not to exceed 0.1 m/s (10 cm/s)
        robot.run_poses(pose_targets, SPEED, tcp_max_velocity=0.1)
        
    except PulseApiException as e:
        # handle possible errors
        print('Exception {}while calling robot at {} '.format(e, robot.host))
        break

Back to the table of contents

API initialization

from pulseapi import RobotPulse
# create an instance of the API wrapper class
host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

Back to the table of contents

Motion control

Possible motion targets:

  • Positions (set_position, run_positions and get_position methods) - to control the location of the robot's TCP (tool center point). Use the position helper function to create a motion target.
  • Poses (set_pose, run_poses and get_pose methods) - to control motor angles. Use the pose helper function to create a motion target.

Possible motion types:

  • Joint (MT_JOINT, default)
  • Linear (MT_LINEAR)

Auxiliary methods:

  • await_motion - periodically requests robot status (default: every 0.1 s) and waits until the robot finishes movements. To be replaced soon.
  • status_motion - returns the actual state of the robotic arm: running (arm in motion), idle (arm not in motion), in the zero gravity mode, or in error state.
  • freeze - sets the arm in the "freeze" state. The arm stops moving, retaining its last position.
    Note: In the state, it is not advisable to move the arm by hand as this can cause damage to its components.
  • relax - sets the arm in the "relaxed" state. The arm stops moving without retaining its last position. In this state, the user can move the robotic arm by hand (e.g., to verify/test a motion trajectory).
  • pack - asking the arm to reach a compact pose for transportation.
  • status_motors - returns the actual states of the six servo motors integrated into the joints of the robotic arm.

WARNING! The following example is sample code. Before running, you must replace reference motion targets in the sample with the ones applicable to your specific case. Before launching this example, make sure that manipulator would not cause any damage to your facilities.

import math
import time
from pulseapi import position, pose, RobotPulse, MT_LINEAR, MotionStatus

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

# create motion targets
pose_target = pose([0, -90, 90, -90, -90, 0])
position_target = position([-0.42, -0.12, 0.35], [math.pi, 0, 0])
position_targets = [
    position([-0.37, -0.12, 0.35], [math.pi, 0, 0]),
    position([-0.42, -0.12, 0.35], [math.pi, 0, 0]),
    position([-0.42, -0.17, 0.35], [math.pi, 0, 0]),
    position([-0.37, -0.17, 0.35], [math.pi, 0, 0]),
]
SPEED = 30  # set the desired speed

# use the motion status command as shown below
def my_await_motion(robot_instance, asking_interval=0.1):
    status = robot_instance.status_motion()
    while status != MotionStatus.IDLE:
        time.sleep(asking_interval)
        status = robot_instance.status_motion()

robot.set_pose(pose_target, SPEED)
robot.await_motion()  # checks every 0.1 s whether the motion is finished 
print('Current pose:\n{}'.format(robot.get_pose()))

robot.set_position(position_target, SPEED)
robot.await_motion(0.5) # checks every 0.5 s whether the motion is finished
print('Current position:\n{}'.format(robot.get_position()))

# command the robot to go through multiple position waypoints (execute a trajectory)
robot.run_positions(position_targets, SPEED)
my_await_motion(robot)

# set the linear motion type
robot.run_positions(position_targets, SPEED, motion_type=MT_LINEAR)
robot.await_motion()

# limit the TCP velocity not to exceed 0.01 m/s (1 cm/s)
robot.run_positions(position_targets, SPEED, 
    motion_type=MT_LINEAR, tcp_max_velocity=0.01)
robot.await_motion()

# stop the arm in the last position
robot.freeze()

# get status from motors
print(robot.status_motors())

Back to the table of contents

Controlling accessories and signals

Available methods:

  • close_gripper, open_gripper with a preset timeout before executing further commands (default: 500 ms). Supported grippers: Schunk and OnRobot.
  • set_digital_output_high set_digital_output_low, get_digital_output - to work with output ports on the controlbox.
  • get_digital_input to work with input ports on the controlbox.

Signals:

  • SIG_LOW - port is inactive
  • SIG_HIGH - port is active
from pulseapi import RobotPulse, SIG_LOW, SIG_HIGH

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

# ask the robot to close the gripper and continue execution of commands after 500 ms
robot.close_gripper() 

# ask the robot to open the gripper and begin to execute further commands immediately 
robot.open_gripper(0)

# set the first output port to the active state
robot.set_digital_output_high(1)

# execute required operations when input port 3 is active
if robot.get_digital_input(3) == SIG_HIGH:
    print('Input port 3 is active')
# execute required operations when input port 1 is inactive
if robot.get_digital_input(1) == SIG_LOW:
    print('Input port 1 is inactive')

Back to the table of contents

Tool API

Use the Tool API methods when you need to calculate a robot motion trajectory with regard to the used tool and to take the tool into account when the robot calculates collisions.

Available methods:

  • change_tool_info - set tool info for trajectory calculations.
  • change_tool_shape - set tool shape for collision validation.
  • get_tool_info, get_tool_shape - receive information about current tool settings.

Helper functions:

  • tool_info - creates a tool info instance to be passed into change_tool_info method.
  • tool_shape - creates a tool shape instance to be passed into change_tool_shape method.
from pulseapi import RobotPulse, position, Point
from pulseapi import create_simple_capsule_obstacle, tool_shape, tool_info

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

# get info about the current tool
current_tool_info = robot.get_tool_info()
current_tool_shape = robot.get_tool_shape()
print('Current tool info\n{}'.format(current_tool_info))
print('Current tool shape\n{}'.format(current_tool_shape))

# create new tool properties
new_tool_info = tool_info(position([0, 0, 0.07], [0, 0, 0]), name='CupHolder')
new_tool_shape = tool_shape([
    create_simple_capsule_obstacle(0.03, Point(0, 0, 0), Point(0,0,0.07))
])

# change tool properties
robot.change_tool_info(new_tool_info)
robot.change_tool_shape(new_tool_shape)
print('New tool info\n{}'.format(robot.get_tool_info()))
print('New tool shape\n{}'.format(robot.get_tool_shape()))

Back to the table of contents

Base API

Use the Base API methods when you need to calculate a robot motion trajectory relative to a specific point in space.

Available methods:

  • change_base
  • get_base
from pulseapi import RobotPulse, position

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

current_base = robot.get_base()
print('Current base\n{}'.format(current_base))

# move the new base point along x and y axes
new_base = position([0.05, 0.05, 0], [0, 0, 0])
robot.change_base(new_base)

print('New base\n{}'.format(robot.get_base()))

Back to the table of contents

Environment API

Use the Environment API to add virtual obstacles to be taken into account when calculating collisions.

Available methods:

  • add_to_environment - adds an obstacle to an environment. Use the helper functions below to describe obstacles.
  • get_all_from_environment - returns all obstacles from an environment.
  • get_from_environment_by_name - returns an obstacle with a specific name from an environment.
  • remove_all_from_environment - removes all obstacles from an environment.
  • remove_from_environment_by_name - removes an obstacle with a specific name from an environment.

Helper functions:

  • create_box_obstacle
  • create_capsule_obstacle
  • create_plane_obstacle
from pulseapi import RobotPulse, Point, position
from pulseapi import create_plane_obstacle, create_box_obstacle, create_capsule_obstacle

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

print('Current environment\n{}'.format(robot.get_all_from_environment()))
# add obstacles to the environment for calculating collisions
box = create_box_obstacle(Point(0.1, 0.1, 0.1), position((1, 1, 1), (0, 0, 0)), 'example_box')
capsule = create_capsule_obstacle(0.1, Point(0.5, 0.5, 0.2), Point(0.5, 0.5, 0.5), 'example_capsule')
plane = create_plane_obstacle([Point(-0.5, 0.4, 0), Point(-0.5, 0, 0), Point(-0.5, 0, 0.1)], 'example_plane')
robot.add_to_environment(box)
robot.add_to_environment(capsule)
robot.add_to_environment(plane)
print('New environment\n{}'.format(robot.get_all_from_environment()))
print('Get example box\n{}'.format(robot.get_from_environment_by_name(box.name)))
# remove specific obstacles
robot.remove_from_environment_by_name(box.name)
print('Environment without box\n{}'.format(robot.get_all_from_environment()))
# remove all obstacles from an environment
robot.remove_all_from_environment()
print('Empty environment\n{}'.format(robot.get_all_from_environment()))

Back to the table of contents

Exception handling

For information about errors, see the API reference. The client wraps errors from the robot into PulseApiException.

Available methods:

  • recover - the function recovers the arm after an emergency, setting its motion status to IDLE. Recovery is possible only after an emergency that is not fatal (corresponds to the ERROR status).

For example, we can trigger an API exception by sending pose into set_position method.

from pulseapi import RobotPulse, PulseApiException, pose, MotionStatus

host = '127.0.0.1:8080'  # replace with a valid robot address 
robot = RobotPulse(host)

try:
    robot.set_position(pose([0, -90, 90, -90, -90, 0]), 10)
    robot.await_motion()
except PulseApiException as e:
    print('Exception {}while calling robot at {} '.format(e, robot.host))
    if robot.status_motion() == MotionStatus.ERROR:
        robot.recover()
        print('Robot recovered from error')

Back to the table of contents

Versions API

Use the Version API methods to get information about the software and hardware versions. You may need to use the methods for contacting support specialists when you notice strange robot behaviour.

Available methods:

  • hardware - returns the hardware versions for all motors, the USB-CAN dongle, safety board and wrist.
  • software - returns the software version for all motors, the USB-CAN dongle, safety board and wrist.
  • robot_software - returns the version of the robot control software.
from pulseapi import Versions

host = '127.0.0.1:8080'  # replace with a valid robot address
versions = Versions(host)

print(versions.hardware())
print(versions.software())
print(versions.robot_software())

Back to the table of contents

Documentation and further information

For further details, see the API reference guide.

You can’t perform that action at this time.