Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions definitions/ur_module.info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ actions:
argument_type: float
required: false
default: 0.6
joint_angle_locations:
name: joint_angle_locations
description: Use joint angles for the location
argument_type: bool
required: false
default: true
locations:
target:
name: target
Expand Down
85 changes: 85 additions & 0 deletions src/ur_interface/integrated_controller.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The name IntegratedController does not clearly indicate that this is the highest-level interface to the UR robot. Anyone looking to use the interface directly without going through the REST node may not find this an intuitive entry point. A more descriptive name that reflects its role in the stack, such as URInterface, would make the module's purpose clearer for both direct use and future maintenance.

  • The resource client calls in gripper_pick and gripper_place are placed at the end of each method, after all motion has completed. This breaks the logical order of operations — in gripper_pick the resource should be transferred from the source to the end effector immediately after the gripper closes, not after the robot returns to the above-goal position. Similarly in gripper_place the resource should transfer from the end effector to the target immediately after the gripper opens. As it stands, if any subsequent movement step fails after the physical pick or place has already happened, the resource manager will never be updated and will be out of sync with the actual robot state. The same problem exists in the other direction — if the resource client call itself fails, the movement steps will still have executed against a resource state that was never validated, leaving the physical and tracked states inconsistent. Resource state transitions should happen at the exact point in the sequence where the physical state changes. Additionally resource client calls don't have any error handling. If a pop or push fails there is no exception caught.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Controller for integrating UR with various attachments"""

import logging
import traceback
from enum import Enum
from typing import Optional

from madsci.client.resource_client import ResourceClient
from madsci.common.types.auth_types import OwnershipInfo

from ur_interface.ur_controller import URController
from ur_interface.ur_dashboard import URDashboard
from ur_interface.ur_tools.grippers.finger_gripper_functions_mixin import FingerGripperMixin
from ur_interface.ur_tools.grippers.robotiq_2_finger_gripper_interface import Robotiq2FingerGripper
from ur_interface.ur_tools.pipettes.pipette_functions_mixin import PipetteMixin
from ur_interface.ur_tools.pipettes.opentrons_pipette_epics_interface import EpicsOpentronsPipette

class UREndEffector(str, Enum):
"""Possible end effectors for the UR arm"""

ROBOTIQ2FINGERGRIPPER = "ROBOTIQ2FINGERGRIPPER"
SCREWDRIVER = "SCREWDRIVER"
OPENTRONSPIPETTE = "OPENTRONSPIPETTE"
WMTOOLCHANGER = "WMTOOLCHANGER"


end_effectors = {UREndEffector.ROBOTIQ2FINGERGRIPPER: Robotiq2FingerGripper, UREndEffector.OPENTRONSPIPETTE: EpicsOpentronsPipette}


class IntegratedController(FingerGripperMixin, PipetteMixin):
"""
This is the primary class for UR robots.
It integrates various interfaces to achieve comprehensive control, encompassing robot initialization via the UR dashboard,
robot motion using URx, and the management of robot end-effectors such as grippers, screwdrivers, electronic pipettes, and cameras."
"""

def __init__(
self,
hostname: str = None,
resource_client: ResourceClient = None,
end_effector: UREndEffector = None,
end_effector_params: dict = {},
end_effector_resource_id: str = None,
resource_owner: OwnershipInfo = None,
tool_resource_id: str = None,
tcp_pose: list = [0, 0, 0, 0, 0, 0],
base_reference_frame: list = None,
logger: Optional[logging.Logger] = None,
):
"""Constructor for the UR class.
:param hostname: Hostname or ip.
:param logger: Logger object for logging messages
"""

if not hostname:
raise TypeError("Hostname cannot be None Type!")

self.hostname = hostname
self.resource_client = resource_client
self.end_effector_params = end_effector_params
self.tool_resource_id = tool_resource_id
self.resource_owner = resource_owner
self.logger = logger or self._setup_logger()
self.acceleration = 0.5
self.velocity = 0.5
self.robot_current_joint_angles = None
self.end_effector_resource_id = end_effector_resource_id

try:
self.ur_dashboard = URDashboard(hostname=self.hostname)
self.ur_controller = URController(
hostname=self.hostname, logger=self.logger, tcp_pose=tcp_pose, base_reference_frame=base_reference_frame
)

self.ur_controller.ur_connection.set_tcp(tcp_pose)
self.create_end_effector_controller(end_effector)
except Exception as e:
self.logger.error(f"Failed to initialize UR: {e}\n{traceback.format_exc()}")
raise e

def create_end_effector_controller(self, end_effector: UREndEffector) -> None:
"""Create appropriate end-effector controller."""
self.end_effector = end_effectors[end_effector](**self.end_effector_params)
if self.end_effector.tool_params:
self.ur_controller.ur_connection.set_tool_communication(**self.end_effector.tool_params)
Loading
Loading