This code is written by: https://github.com/AlvarEhr ; More upstream API version is located here: https://github.com/PCrnjak/PAROL6-python-API
- Software Origin: This control system is based on the
experimental_kinematicsbranch of thePAROL_commander_softwarerepository. The core communication functions were derived from theSerial_sender_good_latest.pyfile; however, the approach to motion planning has been altered from the original implementation. This system was created by editing theCommander_minimal_version.pyfile, which was used as a base. - Automatic Homing on Startup: By default, the
headless_commander.pyscript will immediately command the robot to home itself upon startup. This is done for convenience but can be disabled. To prevent automatic homing, comment out or delete the corresponding line inheadless_commander.py. - AI-Assisted Development: This code was developed with significant AI assistance. While the core logic has been corrected and improved, it has not been exhaustively tested in all scenarios. Users should proceed with caution and verify functionality for their specific needs.
This control software includes several built-in safety features designed to prevent damage to the robot and ensure predictable operation:
- E-Stop Monitoring: The system constantly checks the physical E-Stop button. If triggered, all motion is immediately halted, the command queue is cleared, and the robot is disabled. The system must be manually re-enabled by pressing the
'e'key after the E-Stop is released. - Synchronized Speed Calculation: For moves defined by a speed percentage (
MoveJoint,MovePose), the system now calculates the maximum possible synchronized speed for all joints involved. This prevents individual joints from exceeding their limits and ensures predictable, smooth motion. - Inverse Kinematics (IK) Validation: The system verifies that a valid kinematic solution exists for any pose-based command. If the target pose is unreachable, the command will fail safely before any motion occurs.
WARNING: These are software-based safety measures and are not a substitute for responsible operation and a safe work environment. The user assumes all responsibility for safe operation. Always be attentive when the robot is active, ensure you have immediate access to the physical E-Stop, and operate the robot in a clear area.
Follow the official PAROL commander software installation guide:
- Repository: PAROL Commander Software
- Branch: Use the
experimental_kinematicsbranch - Installation Guide: Official Instructions
After installing the base software, install these additional packages:
# Install Python 3 and pip (if not already installed)
# https://www.python.org/downloads/
# Install Git (if not already installed)
# https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
# Core dependencies (from official installation)
pip3 install roboticstoolbox-python==1.0.3
pip3 install numpy==1.23.4
pip3 install scipy==1.11.4
pip3 install spatialmath
pip3 install pyserial
pip3 install oclock
pip3 install keyboardThe system uses a UDP-based client-server architecture that separates robot control from command generation:
-
The Robot Controller (
headless_commander.py):- Runs on the computer physically connected to the robot via USB/Serial
- Maintains a high-frequency control loop (100Hz) for real-time robot control
- Handles all complex calculations (inverse kinematics, trajectory planning)
- Requires heavy dependencies (roboticstoolbox, numpy, scipy)
- Listens for UDP commands on port 5001
-
The Remote Client (
robot_api.py):- Can run on any computer (same or different from controller)
- Sends simple text commands via UDP
- Requires minimal dependencies (mostly Python standard library)
- Extremely lightweight - can run on resource-constrained devices
- Optionally receives acknowledgments on port 5002
-
Support Modules:
smooth_motion.py: Advanced trajectory generation algorithmsPAROL6_ROBOT.py: Robot-specific parameters and kinematic model
The UDP protocol was chosen for several reasons:
- Simplicity: No connection management overhead
- Low Latency: Direct message passing without handshaking
- Lightweight Client: Client only needs to send text strings
- Cross-Platform: Works on any OS with network support
- Flexible Deployment: Client can run anywhere on the network
- Client calls API function (e.g.,
move_robot_joints()) - API formats command as text string (e.g.,
"MOVEJOINT|90|-45|90|0|45|180|5.5|None") - Command sent via UDP to controller
- Controller queues and executes command
- Optional: Acknowledgment sent back to client
- Optional: Client checks status using command ID
The system includes an optional acknowledgment tracking feature that provides feedback on command execution:
- Tracking States: Commands can report status as
QUEUED,EXECUTING,COMPLETED,FAILED,CANCELLED, orINVALID - Zero Overhead: When not used, the tracking system has zero resource overhead - no threads or sockets are created
- Non-Blocking Mode: Commands can be sent with
non_blocking=Trueto return immediately with a command ID, allowing asynchronous operation - Status Checking: Use
check_command_status(command_id)to poll command status later
Example of non-blocking usage:
# Send command and get ID immediately
cmd_id = move_robot_joints([90, -45, 90, 0, 45, 180],
duration=5,
wait_for_ack=True,
non_blocking=True)
# Do other work...
time.sleep(1)
# Check status later
status = check_command_status(cmd_id)
if status and status['completed']:
print(f"Command finished with status: {status['status']}")- Purpose: Initiates the robot's built-in homing sequence.
- Parameters:
wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 30.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import home_robot home_robot() # Simple usage home_robot(wait_for_ack=True, timeout=30) # With tracking
- Purpose: Moves joints to a target configuration (in degrees).
- Parameters:
joint_angles(List[float]): List of 6 target angles in degrees for joints 1-6duration(float, optional): Total time for the movement in secondsspeed_percentage(int, optional): Speed as percentage (0-100)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import move_robot_joints # Simple move by speed move_robot_joints([90, -45, 90, 0, 45, 180], speed_percentage=75) # Move with acknowledgment tracking result = move_robot_joints([0, -90, 180, 0, 0, 180], duration=5.5, wait_for_ack=True) if result['status'] == 'COMPLETED': print("Move completed successfully")
- Purpose: Moves the end-effector to a Cartesian pose via a joint-based path.
- Parameters:
pose(List[float]): Target pose [x, y, z, Rx, Ry, Rz] in mm and degreesduration(float, optional): Total time for the movement in secondsspeed_percentage(int, optional): Speed as percentage (0-100)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import move_robot_pose move_robot_pose([250, 0, 200, 180, 0, 90], speed_percentage=50)
- Purpose: Moves the end-effector to a target pose in a guaranteed straight-line path.
- Parameters:
pose(List[float]): Target pose [x, y, z, Rx, Ry, Rz] in mm and degreesduration(float, optional): Time for the movement in secondsspeed_percentage(float, optional): Speed as percentage (1-100)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import move_robot_cartesian move_robot_cartesian([200, -50, 180, 180, 0, 135], duration=4.0)
- Purpose: Jogs a single joint by time or angular distance.
- Parameters:
joint_index(int): Joint to move (0-5 for positive direction, 6-11 for negative)speed_percentage(int): Jog speed as percentage (0-100)duration(float, optional): Time to jog in secondsdistance_deg(float, optional): Distance to jog in degreeswait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationordistance_deg, but not both. - Python API Usage:
from robot_api import jog_robot_joint # Jog joint 1 for 2 seconds jog_robot_joint(joint_index=0, speed_percentage=40, duration=2.0) # Jog joint 3 backwards by 15 degrees jog_robot_joint(joint_index=8, speed_percentage=60, distance_deg=15)
- Purpose: Jogs multiple joints simultaneously.
- Parameters:
joints(List[int]): List of joint indices (0-5 positive, 6-11 negative)speeds(List[float]): List of corresponding speeds (1-100%)duration(float): Duration in secondswait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import jog_multiple_joints # Jog joints 1, 4, and 6 simultaneously jog_multiple_joints([0, 3, 5], [70, 40, 60], 1.2)
- Purpose: Jogs the end-effector continuously along an axis.
- Parameters:
frame(str): Reference frame ('TRF' for Tool, 'WRF' for World)axis(str): Axis and direction ('X+', 'X-', 'Y+', 'Y-', 'Z+', 'Z-', 'RX+', 'RX-', 'RY+', 'RY-', 'RZ+', 'RZ-')speed_percentage(int): Jog speed as percentage (0-100)duration(float): Time to jog in secondswait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import jog_cartesian jog_cartesian(frame='TRF', axis='Z+', speed_percentage=50, duration=1.5)
These commands create smooth, curved trajectories with continuous velocity profiles. All commands support reference frame selection via the frame parameter:
- WRF (World Reference Frame): Default. All coordinates are interpreted relative to the robot's base coordinate system.
- TRF (Tool Reference Frame): All coordinates are interpreted relative to the tool's current position and orientation. This means:
- Positions are relative to the tool's origin
- Planes (XY, XZ, YZ) are the tool's local planes, not world planes
- If the tool is rotated, the entire motion rotates with it
- Purpose: Execute a smooth circular motion.
- Parameters:
center(List[float]): Center point [x, y, z] in mmradius(float): Circle radius in mmplane(str, optional): Plane of circle ('XY', 'XZ', or 'YZ'). Default: 'XY'frame(str, optional): Reference frame ('WRF' or 'TRF'). Default: 'WRF'start_pose(List[float], optional): Starting pose [x, y, z, rx, ry, rz], or None for current position. Default: Noneduration(float, optional): Time to complete circle in secondsspeed_percentage(float, optional): Speed as percentage (1-100)clockwise(bool, optional): Direction of motion. Default: Falsewait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 10.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import smooth_circle # Draw a 50mm radius circle in world XY plane smooth_circle(center=[200, 0, 200], radius=50, plane='XY', duration=5.0) # Draw a circle in tool's XY plane (follows tool orientation) smooth_circle(center=[0, 30, 0], radius=25, plane='XY', frame='TRF', duration=4.0)
- Purpose: Execute a smooth arc motion defined by center point.
- Parameters:
end_pose(List[float]): End pose [x, y, z, rx, ry, rz] in mm and degreescenter(List[float]): Arc center point [x, y, z] in mmframe(str, optional): Reference frame ('WRF' or 'TRF'). Default: 'WRF'start_pose(List[float], optional): Starting pose, or None for current position. Default: Noneduration(float, optional): Time to complete arc in secondsspeed_percentage(float, optional): Speed as percentage (1-100)clockwise(bool, optional): Direction of motion. Default: Falsewait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 10.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import smooth_arc_center # Arc in world coordinates smooth_arc_center(end_pose=[250, 50, 200, 0, 0, 90], center=[200, 0, 200], duration=3.0) # Arc in tool coordinates (relative to tool position/orientation) smooth_arc_center(end_pose=[30, 30, 0, 0, 0, 45], center=[15, 15, 0], frame='TRF', duration=3.0)
- Purpose: Execute a smooth arc motion defined by radius and angle.
- Parameters:
end_pose(List[float]): End pose [x, y, z, rx, ry, rz] in mm and degreesradius(float): Arc radius in mmarc_angle(float): Arc angle in degreesframe(str, optional): Reference frame ('WRF' or 'TRF'). Default: 'WRF'start_pose(List[float], optional): Starting pose, or None for current position. Default: Noneduration(float, optional): Time to complete arc in secondsspeed_percentage(float, optional): Speed as percentage (1-100)clockwise(bool, optional): Direction of motion. Default: Falsewait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 10.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import smooth_arc_parametric # Parametric arc in world frame smooth_arc_parametric(end_pose=[250, 50, 200, 0, 0, 90], radius=50, arc_angle=90, duration=3.0) # Parametric arc in tool frame smooth_arc_parametric(end_pose=[40, 0, 0, 0, 0, 60], radius=25, arc_angle=60, frame='TRF', speed_percentage=40)
- Purpose: Create smooth spline through waypoints.
- Parameters:
waypoints(List[List[float]]): List of poses [x, y, z, rx, ry, rz] to pass throughframe(str, optional): Reference frame ('WRF' or 'TRF'). Default: 'WRF'start_pose(List[float], optional): Starting pose, or None for current position. Default: Noneduration(float, optional): Total time for motion in secondsspeed_percentage(float, optional): Speed as percentage (1-100)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 10.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. - Python API Usage:
from robot_api import smooth_spline # Spline through world coordinates waypoints = [ [200, 0, 100, 0, 0, 0], [250, 50, 150, 0, 15, 45], [200, 100, 200, 0, 30, 90] ] smooth_spline(waypoints, duration=8.0) # Spline through tool-relative coordinates tool_waypoints = [ [20, 0, 0, 0, 0, 0], [20, 20, 10, 0, 0, 30], [0, 20, 20, 0, 0, 60] ] smooth_spline(tool_waypoints, frame='TRF', duration=6.0)
- Purpose: Execute helical motion.
- Parameters:
center(List[float]): Helix center point [x, y, z] in mmradius(float): Helix radius in mmpitch(float): Vertical distance per revolution in mmheight(float): Total height of helix in mmframe(str, optional): Reference frame ('WRF' or 'TRF'). Default: 'WRF'start_pose(List[float], optional): Starting pose, or None for current position. Default: Noneduration(float, optional): Time to complete helix in secondsspeed_percentage(float, optional): Speed as percentage (1-100)clockwise(bool, optional): Direction of motion. Default: Falsewait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 10.0non_blocking(bool, optional): Return immediately with command ID. Default: False
-
Note: You must provide either
durationorspeed_percentage, but not both. -
Note: In TRF mode, the helix rises along the tool's Z-axis, not the world Z-axis.
- Python API Usage:
from robot_api import smooth_helix # Vertical helix in world frame smooth_helix(center=[200, 0, 150], radius=30, pitch=20, height=100, duration=10.0) # Helix along tool's Z-axis (follows tool orientation) smooth_helix(center=[0, 30, 0], radius=20, pitch=15, height=60, frame='TRF', duration=8.0)
- Purpose: Blend multiple motion segments smoothly.
- Parameters:
segments(List[Dict]): List of segment dictionaries defining the motion pathblend_time(float, optional): Time for blending between segments in seconds. Default: 0.5frame(str, optional): Reference frame ('WRF' or 'TRF') for all segments. Default: 'WRF'start_pose(List[float], optional): Starting pose, or None for current position. Default: Noneduration(float, optional): Total time for entire motion in secondsspeed_percentage(float, optional): Speed as percentage (1-100)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 15.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import smooth_blend # Blend in world coordinates segments = [ {'type': 'LINE', 'end': [250, 0, 200, 0, 0, 0], 'duration': 2.0}, {'type': 'CIRCLE', 'center': [250, 0, 200], 'radius': 50, 'plane': 'XY', 'duration': 4.0, 'clockwise': False}, {'type': 'LINE', 'end': [200, 0, 200, 0, 0, 0], 'duration': 2.0} ] smooth_blend(segments, blend_time=0.5, duration=10.0) # Blend in tool coordinates (all segments relative to tool) tool_segments = [ {'type': 'LINE', 'end': [30, 0, 0, 0, 0, 0], 'duration': 2.0}, {'type': 'CIRCLE', 'center': [30, 20, 0], 'radius': 20, 'plane': 'XY', 'duration': 4.0, 'clockwise': False}, {'type': 'LINE', 'end': [0, 20, 0, 0, 0, 0], 'duration': 2.0} ] smooth_blend(tool_segments, frame='TRF', blend_time=0.5, duration=10.0)
- Purpose: Chain multiple smooth motions with automatic continuity.
- Parameters:
motions(List[Dict]): List of motion dictionariesensure_continuity(bool, optional): Automatically set start_pose for continuity. Default: Trueframe(str, optional): Reference frame ('WRF' or 'TRF') for all motions. Default: 'WRF'wait_for_ack(bool, optional): Enable command tracking. Default: Truetimeout(float, optional): Timeout per motion in seconds. Default: 30.0
- Returns: List of results for each motion
- Python API Usage:
from robot_api import chain_smooth_motions # Chain motions in world frame (default) motions = [ {'type': 'circle', 'center': [200, 0, 200], 'radius': 50, 'duration': 5}, {'type': 'arc', 'end_pose': [250, 50, 200, 0, 0, 90], 'center': [225, 25, 200], 'duration': 3} ] chain_smooth_motions(motions, ensure_continuity=True) # Chain motions in tool frame tool_motions = [ {'type': 'circle', 'center': [0, 30, 0], 'radius': 25, 'duration': 4}, {'type': 'arc', 'end_pose': [30, 30, 0, 0, 0, 45], 'center': [15, 15, 0], 'duration': 3} ] chain_smooth_motions(tool_motions, frame='TRF', ensure_continuity=True)
- Purpose: High-level trajectory execution with best method selection.
- Parameters:
trajectory(List[List[float]]): List of poses [x, y, z, rx, ry, rz]timing_mode(str, optional): Either 'duration' or 'speed'. Default: 'duration'timing_value(float, optional): Duration in seconds or speed percentage. Default: 5.0motion_type(str, optional): Either 'spline' or 'linear'. Default: 'spline'frame(str, optional): Reference frame ('WRF' or 'TRF') for spline motion. Default: 'WRF'wait_for_ack(bool, optional): Enable command tracking. Default: Truetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 30.0
-
Note: The
frameparameter only applies whenmotion_type='spline'. Linear motions are always in WRF. - Python API Usage:
from robot_api import execute_trajectory # Execute trajectory in world frame trajectory = [[200, 0, 200, 0, 0, 0], [250, 50, 200, 0, 0, 45], [200, 100, 200, 0, 0, 90]] execute_trajectory(trajectory, timing_mode='duration', timing_value=10.0, motion_type='spline') # Execute trajectory in tool frame (spline only) tool_trajectory = [[20, 0, 0, 0, 0, 0], [20, 20, 0, 0, 0, 30], [0, 20, 10, 0, 0, 60]] execute_trajectory(tool_trajectory, frame='TRF', timing_mode='speed', timing_value=40, motion_type='spline')
- Purpose: Controls the pneumatic gripper.
- Parameters:
action(str): Action to perform ('open' or 'close')port(int): Digital output port (1 or 2)wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import control_pneumatic_gripper control_pneumatic_gripper(action='open', port=1)
- Purpose: Controls the electric gripper.
- Parameters:
action(str): Action to perform ('move' or 'calibrate')position(int, optional): Target position (0-255). Default: 255speed(int, optional): Movement speed (0-255). Default: 150current(int, optional): Max motor current in mA (100-1000). Default: 500wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import control_electric_gripper control_electric_gripper(action='calibrate') control_electric_gripper(action='move', position=200, speed=150)
- Purpose: Pauses command queue execution.
- Parameters:
duration(float): Pause time in secondswait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import delay_robot delay_robot(2.5)
- Purpose: Immediately stops all motion and clears command queue.
- Parameters:
wait_for_ack(bool, optional): Enable command tracking. Default: Falsetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 2.0non_blocking(bool, optional): Return immediately with command ID. Default: False
- Python API Usage:
from robot_api import stop_robot_movement stop_robot_movement()
These commands request current robot state without moving the robot:
- Purpose: Get current end-effector pose.
- Parameters: None
- Returns: List [x, y, z, Rx, Ry, Rz] in mm and degrees, or None if failed
- Python API Usage:
from robot_api import get_robot_pose pose = get_robot_pose() if pose: print(f"Current pose: {pose}")
- Purpose: Get current joint angles.
- Parameters: None
- Returns: List of 6 angles in degrees, or None if failed
- Python API Usage:
from robot_api import get_robot_joint_angles angles = get_robot_joint_angles()
- Purpose: Get current joint speeds.
- Parameters: None
- Returns: List of 6 speeds in steps/sec, or None if failed
- Purpose: Get digital I/O status.
- Parameters:
verbose(bool, optional): Print formatted status to console. Default: False
- Returns: List [IN1, IN2, OUT1, OUT2, ESTOP] (0 or 1 values), or None if failed
- Purpose: Get electric gripper status.
- Parameters:
verbose(bool, optional): Print formatted status to console. Default: False
- Returns: List [ID, Position, Speed, Current, StatusByte, ObjectDetected], or None if failed
- Purpose: Get robot pose as transformation matrix.
- Parameters: None
- Returns: 4x4 numpy array, or None if failed
- Purpose: Check if robot has stopped moving.
- Parameters:
threshold_speed(float, optional): Speed threshold in steps/sec. Default: 2.0
- Returns: Boolean (True if stopped, False otherwise)
- Purpose: Check if E-stop is currently pressed.
- Parameters: None
- Returns: Boolean (True if pressed, False otherwise)
- Purpose: Get comprehensive robot status.
- Parameters: None
- Returns: Dictionary containing pose, angles, speeds, IO, gripper status, stopped state, and E-stop state
- Purpose: High-level trajectory execution with best method selection.
- Parameters:
trajectory(List[List[float]]): List of poses [x, y, z, rx, ry, rz]timing_mode(str, optional): Either 'duration' or 'speed'. Default: 'duration'timing_value(float, optional): Duration in seconds or speed percentage. Default: 5.0motion_type(str, optional): Either 'spline' or 'linear'. Default: 'spline'wait_for_ack(bool, optional): Enable command tracking. Default: Truetimeout(float, optional): Timeout for acknowledgment in seconds. Default: 30.0
- Python API Usage:
from robot_api import execute_trajectory trajectory = [[200, 0, 200, 0, 0, 0], [250, 50, 200, 0, 0, 45], [200, 100, 200, 0, 0, 90]] execute_trajectory(trajectory, timing_mode='duration', timing_value=10.0, motion_type='spline')
- Purpose: Wait for robot to stop moving.
- Parameters:
timeout(float, optional): Maximum time to wait in seconds. Default: 10.0poll_rate(float, optional): How often to check in seconds. Default: 0.1
- Returns: Boolean (True if robot stopped, False if timeout)
- Purpose: Execute move with automatic retry on failure.
- Parameters:
move_func(callable): The movement function to call*args: Arguments for the movement functionmax_retries(int, optional): Maximum number of retry attempts. Default: 3retry_delay(float, optional): Delay between retries in seconds. Default: 1.0**kwargs: Keyword arguments for the movement function
- Returns: Result from the movement function or error dictionary
- Purpose: Chain multiple smooth motions with automatic continuity.
- Parameters:
motions(List[Dict]): List of motion dictionariesensure_continuity(bool, optional): Automatically set start_pose for continuity. Default: Truewait_for_ack(bool, optional): Enable command tracking. Default: Truetimeout(float, optional): Timeout per motion in seconds. Default: 30.0
- Returns: List of results for each motion
- Python API Usage:
from robot_api import chain_smooth_motions motions = [ {'type': 'circle', 'center': [200, 0, 200], 'radius': 50, 'duration': 5}, {'type': 'arc', 'end_pose': [250, 50, 200, 0, 0, 90], 'center': [225, 25, 200], 'duration': 3} ] chain_smooth_motions(motions, ensure_continuity=True)
- Purpose: Check status of a previously sent command.
- Parameters:
command_id(str): The command ID returned from a non-blocking command
- Returns: Dictionary with status information, or None if tracker not initialized
- Dictionary Contents:
status(str): Current status ('QUEUED', 'EXECUTING', 'COMPLETED', 'FAILED', 'CANCELLED', 'INVALID')details(str): Additional status detailscompleted(bool): Whether command has finishedsent_time(datetime): When command was sentack_time(datetime, optional): When acknowledgment was received
- Purpose: Check if command tracking system is active.
- Parameters: None
- Returns: Boolean (True if tracking is active, False otherwise)
- Purpose: Get resource usage statistics for tracking system.
- Parameters: None
- Returns: Dictionary with tracking statistics
- Dictionary Contents:
active(bool): Whether tracking is activecommands_tracked(int): Number of commands being trackedmemory_bytes(int): Approximate memory usagethread_active(bool): Whether tracking thread is running
The system is designed with a client-server architecture where most dependencies are only needed on the server (robot controller) side. The client API (robot_api.py) uses only standard Python libraries for UDP communication, making it lightweight and portable.
Install Python 3 and the following packages on the computer connected to the robot:
# Core robotics libraries
pip3 install roboticstoolbox-python==1.0.3
pip3 install numpy==1.23.4
pip3 install scipy==1.11.4
pip3 install spatialmath
# Serial communication and timing
pip3 install pyserial
pip3 install oclock
# User input
pip3 install keyboardThe client API is designed to be lightweight with minimal dependencies:
# Only needed for get_robot_pose() matrix conversion
pip3 install numpy==1.23.4
pip3 install spatialmath
# All other functionality uses only Python standard library:
# socket, threading, time, uuid, datetime, collections, typingNote: If you only need to send commands and don't use get_robot_pose(), the client requires NO external dependencies - only Python's built-in libraries.
Required files in the same folder:
headless_commander.py- Main server/controllerPAROL6_ROBOT.py- Robot configuration and kinematic modelsmooth_motion.py- Advanced trajectory generationGUI/files/folder structure - For imports to work correctly
Optional:
com_port.txt- Contains the USB COM port (e.g., COM5)
Only required file:
robot_api.py- Client API for sending commands
The client can run on any computer on the same network as the server, or on the same computer in a different process.
-
Connect Robot: Ensure the robot is connected via USB to the controller computer.
-
Start Controller: On the robot controller computer, navigate to the folder containing the server files and run:
python headless_commander.py
The controller will:
- Connect to the robot via serial port (prompts if
com_port.txtnot found) - Start listening for UDP commands on port 5001
- Optionally home the robot on startup (unless disabled)
- Connect to the robot via serial port (prompts if
Commands can be sent from:
- Same Computer: Run Python scripts or interactive sessions in another terminal
- Different Computer: Ensure network connectivity and update
SERVER_IPinrobot_api.py
- Send Commands: Use the API functions from
robot_api.py:from robot_api import * # Example sequence home_robot() move_robot_joints([90, -90, 160, 12, 12, 180], duration=5.5) delay_robot(0.5) # Smooth motion example smooth_circle([200, 0, 200], radius=50, duration=5.0) # Non-blocking example with status checking cmd_id = move_robot_pose([250, 0, 200, 180, 0, 90], speed_percentage=50, wait_for_ack=True, non_blocking=True) # Check status after some time import time time.sleep(2) status = check_command_status(cmd_id) if status: print(f"Command status: {status['status']}")
If running client and server on different computers:
-
Update Server IP: In
robot_api.py, modify theSERVER_IPvariable:SERVER_IP = "192.168.1.100" # Replace with robot controller's IP SERVER_PORT = 5001 # Default port (usually no change needed)
-
Firewall Settings: Ensure UDP port 5001 is open on the robot controller computer.
-
Network Requirements:
- Both computers must be on the same network
- Low latency recommended for real-time control
- Command acknowledgments use port 5002 (optional feature)
The acknowledgment system allows for sophisticated command management:
from robot_api import *
import time
# Send multiple commands non-blocking
cmd1 = move_robot_joints([90, -45, 90, 0, 45, 180],
duration=3,
wait_for_ack=True,
non_blocking=True)
cmd2 = smooth_circle([200, 0, 200], radius=30,
duration=5,
wait_for_ack=True,
non_blocking=True)
# Monitor both commands
while True:
status1 = check_command_status(cmd1)
status2 = check_command_status(cmd2)
if status1 and status1['completed'] and status2 and status2['completed']:
print("Both commands completed!")
break
time.sleep(0.1)- Serial Connection Issues: Check COM port in Device Manager (Windows) and update
com_port.txt - Command Not Executing: Verify robot is homed and E-stop is not pressed
- Tracking Not Working: Ensure
wait_for_ack=Trueis set for commands - IK Failures: Target pose may be unreachable; check robot workspace limits
- Smooth Motion Errors: Verify waypoints are reachable and properly formatted
For additional support, refer to the PAROL commander software repository.
Or you can head over to the PAROL6 Discord channel for extra support