diff --git a/.github/workflows/ci-format.yml b/.github/workflows/ci-format.yml index 94e0e760a..f93239780 100644 --- a/.github/workflows/ci-format.yml +++ b/.github/workflows/ci-format.yml @@ -16,11 +16,11 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.10.4 - name: Install system hooks run: sudo apt-get install clang-format-14 cppcheck - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 with: extra_args: --all-files --hook-stage manual diff --git a/.github/workflows/coverage-build.yml b/.github/workflows/coverage-build.yml index f21160d56..b26bc2d2b 100644 --- a/.github/workflows/coverage-build.yml +++ b/.github/workflows/coverage-build.yml @@ -7,9 +7,9 @@ on: jobs: coverage: name: coverage build - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 container: - image: ubuntu:jammy + image: ubuntu:noble strategy: fail-fast: false env: @@ -45,7 +45,7 @@ jobs: file: ros_ws/lcov/total_coverage.info flags: unittests name: codecov-umbrella - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: colcon-logs-${{ matrix.os }} path: ros_ws/log diff --git a/.github/workflows/reusable_ici.yml b/.github/workflows/reusable_ici.yml index b47569fcd..3a39b4c92 100644 --- a/.github/workflows/reusable_ici.yml +++ b/.github/workflows/reusable_ici.yml @@ -51,3 +51,4 @@ jobs: ROS_DISTRO: ${{ inputs.ros_distro }} ROS_REPO: ${{ inputs.ros_repo }} CMAKE_ARGS: -DUR_ROBOT_DRIVER_BUILD_INTEGRATION_TESTS=ON + ADDITIONAL_DEBS: docker.io netcat-openbsd # Needed for integration tests diff --git a/.github/workflows/update-ci.yml b/.github/workflows/update-ci.yml index e9c71f597..aa75b4f4b 100644 --- a/.github/workflows/update-ci.yml +++ b/.github/workflows/update-ci.yml @@ -15,7 +15,7 @@ jobs: steps: # Setup pre-commit - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.10.4 - name: Install pre-commit @@ -28,7 +28,7 @@ jobs: # Create pull request - name: Create pull-request id: cpr - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: branch: update-ci/pre-commit-autoupdate delete-branch: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f928e585..5538e9f38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,13 +33,13 @@ repos: # Python hooks - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.1 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 24.2.0 hooks: - id: black args: ["--line-length=100"] @@ -51,7 +51,7 @@ repos: args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D401,D404"] - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 args: ["--ignore=E501,W503"] diff --git a/Universal_Robots_ROS2_Driver.iron.repos b/Universal_Robots_ROS2_Driver.iron.repos index 38777168b..f59c5d0e5 100644 --- a/Universal_Robots_ROS2_Driver.iron.repos +++ b/Universal_Robots_ROS2_Driver.iron.repos @@ -14,11 +14,11 @@ repositories: ros2_control: type: git url: https://github.com/ros-controls/ros2_control.git - version: master + version: iron ros2_controllers: type: git url: https://github.com/ros-controls/ros2_controllers - version: master + version: iron kinematics_interface: type: git url: https://github.com/ros-controls/kinematics_interface.git diff --git a/ur/CHANGELOG.rst b/ur/CHANGELOG.rst index 1c1a15d5c..97ec20612 100644 --- a/ur/CHANGELOG.rst +++ b/ur/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package ur ^^^^^^^^^^^^^^^^^^^^^^^^ +2.4.5 (2024-05-16) +------------------ + +2.4.4 (2024-04-04) +------------------ + +2.4.3 (2024-02-02) +------------------ + 2.4.2 (2023-11-23) ------------------ diff --git a/ur/package.xml b/ur/package.xml index 2561105d6..11f8431ff 100644 --- a/ur/package.xml +++ b/ur/package.xml @@ -2,7 +2,7 @@ ur - 2.4.2 + 2.4.5 Metapackage for universal robots Felix Exner Robert Wilbrandt diff --git a/ur_calibration/CHANGELOG.rst b/ur_calibration/CHANGELOG.rst index c0d4b2dcb..11eae704b 100644 --- a/ur_calibration/CHANGELOG.rst +++ b/ur_calibration/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package ur_calibration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2.4.5 (2024-05-16) +------------------ + +2.4.4 (2024-04-04) +------------------ + +2.4.3 (2024-02-02) +------------------ + 2.4.2 (2023-11-23) ------------------ diff --git a/ur_calibration/README.md b/ur_calibration/README.md index 06567be28..70646842a 100644 --- a/ur_calibration/README.md +++ b/ur_calibration/README.md @@ -8,71 +8,4 @@ make use of this in ROS, you first have to extract the calibration information f Though this step is not necessary, to control the robot using this driver, it is highly recommended to do so, as end effector positions might be off in the magnitude of centimeters. -## Nodes -### calibration_correction -This node extracts calibration information directly from a robot, calculates the URDF correction and -saves it into a .yaml file. - -In the launch folder of the ur_calibration package is a helper script: - -```bash -$ ros2 launch ur_calibration calibration_correction.launch.py \ -robot_ip:= target_filename:="${HOME}/my_robot_calibration.yaml" -``` - -For the parameter `robot_ip` insert the IP address on which the ROS pc can reach the robot. As -`target_filename` provide an absolute path where the result will be saved to. - -## Creating a calibration / launch package for all local robots -When dealing with multiple robots in one organization it might make sense to store calibration data -into a package dedicated to that purpose only. To do so, create a new package (if it doesn't already -exist) - -```bash -# Replace your actual colcon_ws folder -$ cd /src -$ ros2 pkg create _ur_launch --build-type ament_cmake --dependencies ur_client_library \ ---description "Package containing calibrations and launch files for our UR robots." -# Create a skeleton package -$ mkdir -p _ur_launch/etc -$ mkdir -p _ur_launch/launch -$ echo 'install(DIRECTORY etc launch DESTINATION share/${PROJECT_NAME})' >> _ur_launch/CMakeLists.txt -$ colcon build --packages-select _ur_launch -``` - -We can use the new package to store the calibration data in that package. We recommend naming each -robot individually, e.g. *ex-ur10-1*. - -```bash -$ ros2 launch ur_calibration calibration_correction.launch.py \ -robot_ip:= \ -target_filename:="$(ros2 pkg prefix _ur_launch)/share/_ur_launch/etc/ex-ur10-1_calibration.yaml" -``` - -To make life easier, we create a launchfile for this particular robot. We base it upon the -respective launchfile in the driver: - -```bash -# Replace your actual colcon_ws folder -$ cd /src/_ur_launch/launch -$ cp $(ros2 pkg prefix ur_robot_driver)/share/ur_robot_driver/launch/ur_control.launch.py ex-ur10-1.launch.py -``` - -Next, modify the parameter section of the new launchfile to match your actual calibration: - -```py -kinematics_params = PathJoinSubstitution( - [FindPackageShare("_ur_launch"), "etc", "", "ex-ur10-1_calibration.yaml"] - ) - -``` - -Then, anybody cloning this repository can startup the robot simply by launching - -```bash -# Replace your actual colcon_ws folder -$ cd -$ colcon build --packages-select _ur_launch -$ ros2 launch _ur_launch ex-ur10-1.launch.py -robot_ip:=xxx.yyy.zzz.www ur_type:=ur5e use_mock_hardware:=false launch_rviz:=true -``` +For details please see [doc/index.rst](doc/index.rst) diff --git a/ur_calibration/doc/index.rst b/ur_calibration/doc/index.rst new file mode 100644 index 000000000..d8056e35a --- /dev/null +++ b/ur_calibration/doc/index.rst @@ -0,0 +1,45 @@ + +ur_calibration +============== + +Package for extracting the factory calibration from a UR robot and changing it to be used by ``ur_description`` to gain a correct URDF model. + +Each UR robot is calibrated inside the factory giving exact forward and inverse kinematics. To also +make use of this in ROS, you first have to extract the calibration information from the robot. + +Though this step is not necessary, to control the robot using this driver, it is highly recommended +to do so, as end effector positions might be off in the magnitude of centimeters. + +Nodes +----- + +calibration_correction +^^^^^^^^^^^^^^^^^^^^^^ + +This node extracts calibration information directly from a robot, calculates the URDF correction and +saves it into a .yaml file. + +In the launch folder of the ur_calibration package is a helper script: + +.. code-block:: bash + + $ ros2 launch ur_calibration calibration_correction.launch.py \ + robot_ip:= target_filename:="${HOME}/my_robot_calibration.yaml" + +For the parameter ``robot_ip`` insert the IP address on which the ROS pc can reach the robot. As +``target_filename`` provide an absolute path where the result will be saved to. + +With that, you can launch your specific robot with the correct calibration using + +.. code-block:: bash + + $ ros2 launch ur_robot_driver ur_control.launch.py \ + ur_type:=ur5e \ + robot_ip:=192.168.56.101 \ + kinematics_params_file:="${HOME}/my_robot_calibration.yaml" + +Adapt the robot model matching to your robot. + +Ideally, you would create a package for your custom workcell, as explained in `the custom workcell +tutorial +`_. diff --git a/ur_calibration/package.xml b/ur_calibration/package.xml index 6baaa18d8..55fd38fc8 100644 --- a/ur_calibration/package.xml +++ b/ur_calibration/package.xml @@ -1,7 +1,7 @@ ur_calibration - 2.4.2 + 2.4.5 Package for extracting the factory calibration from a UR robot and change it such that it can be used by ur_description to gain a correct URDF Felix Exner diff --git a/ur_controllers/CHANGELOG.rst b/ur_controllers/CHANGELOG.rst index 19d1440d0..7ddef74fc 100644 --- a/ur_controllers/CHANGELOG.rst +++ b/ur_controllers/CHANGELOG.rst @@ -2,6 +2,17 @@ Changelog for package ur_controllers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2.4.5 (2024-05-16) +------------------ +* Use latched publishing for robot_mode and safety_mode +* Contributors: Felix Exner + +2.4.4 (2024-04-04) +------------------ + +2.4.3 (2024-02-02) +------------------ + 2.4.2 (2023-11-23) ------------------ * Update read_state_from_hardware diff --git a/ur_controllers/README.md b/ur_controllers/README.md index c6599dcd0..b5f4adc34 100644 --- a/ur_controllers/README.md +++ b/ur_controllers/README.md @@ -11,66 +11,4 @@ robot family. Currently this contains * A **scaled_joint_trajectory_controller** that is similar to the *joint_trajectory_controller*, but it uses the speed scaling reported by the robot to reduce progress in the trajectory. -## About this package -This package contains controllers not being available in the default `ros_control` set. They are -created to support more features offered by the UR robot family. Any of these controllers are -example implementations for certain features and are intended to be generalized and merged -into the default `ros_control` controller set at some future point. - -## Controller description -This packages offers a couple of specific controllers that will be explained in the following -sections. -### ur_controllers/SpeedScalingStateBroadcaster -This controller publishes the current actual execution speed as reported by the robot. Values are -floating points between 0 and 1. - -In the [`ur_robot_driver`](../ur_robot_driver) this is calculated by multiplying the two [RTDE](https://www.universal-robots.com/articles/ur/real-time-data-exchange-rtde-guide/) data -fields `speed_scaling` (which should be equal to the value shown by the speed slider position on the -teach pendant) and `target_speed_fraction` (Which is the fraction to which execution gets slowed -down by the controller). -### position_controllers/ScaledJointTrajectoryController and velocity_controllers/ScaledJointTrajectoryController -These controllers work similar to the well-known -[`joint_trajectory_controller`](http://wiki.ros.org/joint_trajectory_controller). - -However, they are extended to handle the robot's execution speed specifically. Because the default -`joint_trajectory_controller` would interpolate the trajectory with the configured time constraints (ie: always assume maximum velocity and acceleration supported by the robot), -this could lead to significant path deviation due to multiple reasons: - - The speed slider on the robot might not be at 100%, so motion commands sent from ROS would - effectively get scaled down resulting in a slower execution. - - The robot could scale down motions based on configured safety limits resulting in a slower motion - than expected and therefore not reaching the desired target in a control cycle. - - Motions might not be executed at all, e.g. because the robot is E-stopped or in a protective stop - - Motion commands sent to the robot might not be interpreted, e.g. because there is no - [`external_control`](https://github.com/UniversalRobots/Universal_Robots_ROS_Driver#prepare-the-robot) - program node running on the robot controller. - - The program interpreting motion commands could be paused. - -The following plot illustrates the problem: -![Trajectory execution with default trajectory controller](doc/traj_without_speed_scaling.png -"Trajectory execution with default trajectory controller") - -The graph shows a trajectory with one joint being moved to a target point and back to its starting -point. As the joint's speed is limited to a very low setting on the teach pendant, speed scaling -(black line) activates and limits the joint speed (green line). As a result, the target -trajectory (light blue) doesn't get executed by the robot, but instead the pink trajectory is executed. -The vertical distance between the light blue line and the pink line is the path error in each -control cycle. We can see that the path deviation gets above 300 degrees at some point and the -target point at -6 radians never gets reached. - -All of the cases mentioned above are addressed by the scaled trajectory versions. Trajectory execution -can be transparently scaled down using the speed slider on the teach pendant without leading to -additional path deviations. Pausing the program or hitting the E-stop effectively leads to -`speed_scaling` being 0 meaning the trajectory will not be continued until the program is continued. -This way, trajectory executions can be explicitly paused and continued. - -With the scaled version of the trajectory controller the example motion shown in the previous diagram becomes: -![Trajectory execution with scaled_joint_trajectory_controller](doc/traj_with_speed_scaling.png -"Trajectory execution with scaled_joint_trajectory_controller") - -The deviation between trajectory interpolation on the ROS side and actual robot execution stays minimal and the -robot reaches the intermediate setpoint instead of returning "too early" as in the example above. - -Under the hood this is implemented by proceeding the trajectory not by a full time step but only by -the fraction determined by the current speed scaling. If speed scaling is currently at 50% then -interpolation of the current control cycle will start half a time step after the beginning of the -previous control cycle. +For more details please see [doc/index.rst](doc/index.rst) diff --git a/ur_controllers/doc/index.rst b/ur_controllers/doc/index.rst new file mode 100644 index 000000000..6d7828462 --- /dev/null +++ b/ur_controllers/doc/index.rst @@ -0,0 +1,99 @@ +ur_controllers +============== + +This package contains controllers and hardware interface for ``ros_control`` that are special to the UR +robot family. Currently this contains: + + +* A **speed_scaling_interface** to read the value of the current speed scaling into controllers. +* A **scaled_joint_command_interface** that provides access to joint values and commands in + combination with the speed scaling value. +* A **speed_scaling_state_controller** that publishes the current execution speed as reported by + the robot to a topic interface. Values are floating points between 0 and 1. +* A **scaled_joint_trajectory_controller** that is similar to the *joint_trajectory_controller*\ , + but it uses the speed scaling reported by the robot to reduce progress in the trajectory. + +About this package +------------------ + +This package contains controllers not being available in the default ``ros_control`` set. They are +created to support more features offered by the UR robot family. Any of these controllers are +example implementations for certain features and are intended to be generalized and merged +into the default ``ros_control`` controller set at some future point. + +Controller description +---------------------- + +This packages offers a couple of specific controllers that will be explained in the following +sections. + +ur_controllers/SpeedScalingStateBroadcaster +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This controller publishes the current actual execution speed as reported by the robot. Values are +floating points between 0 and 1. + +In the `ur_robot_driver +`_ +this is calculated by multiplying the two `RTDE +`_ data +fields ``speed_scaling`` (which should be equal to the value shown by the speed slider position on the +teach pendant) and ``target_speed_fraction`` (Which is the fraction to which execution gets slowed +down by the controller). + +position_controllers/ScaledJointTrajectoryController and velocity_controllers/ScaledJointTrajectoryController +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These controllers work similar to the well-known +`joint_trajectory_controller `_. + +However, they are extended to handle the robot's execution speed specifically. Because the default +``joint_trajectory_controller`` would interpolate the trajectory with the configured time constraints (ie: always assume maximum velocity and acceleration supported by the robot), +this could lead to significant path deviation due to multiple reasons: + + +* The speed slider on the robot might not be at 100%, so motion commands sent from ROS would + effectively get scaled down resulting in a slower execution. +* The robot could scale down motions based on configured safety limits resulting in a slower motion + than expected and therefore not reaching the desired target in a control cycle. +* Motions might not be executed at all, e.g. because the robot is E-stopped or in a protective stop +* Motion commands sent to the robot might not be interpreted, e.g. because there is no + `external_control `_ + program node running on the robot controller. +* The program interpreting motion commands could be paused. + +The following plot illustrates the problem: + +.. image:: traj_without_speed_scaling.png + :target: traj_without_speed_scaling.png + :alt: Trajectory execution with default trajectory controller + + +The graph shows a trajectory with one joint being moved to a target point and back to its starting +point. As the joint's speed is limited to a very low setting on the teach pendant, speed scaling +(black line) activates and limits the joint speed (green line). As a result, the target +trajectory (light blue) doesn't get executed by the robot, but instead the pink trajectory is executed. +The vertical distance between the light blue line and the pink line is the path error in each +control cycle. We can see that the path deviation gets above 300 degrees at some point and the +target point at -6 radians never gets reached. + +All of the cases mentioned above are addressed by the scaled trajectory versions. Trajectory execution +can be transparently scaled down using the speed slider on the teach pendant without leading to +additional path deviations. Pausing the program or hitting the E-stop effectively leads to +``speed_scaling`` being 0 meaning the trajectory will not be continued until the program is continued. +This way, trajectory executions can be explicitly paused and continued. + +With the scaled version of the trajectory controller the example motion shown in the previous diagram becomes: + +.. image:: traj_with_speed_scaling.png + :target: traj_with_speed_scaling.png + :alt: Trajectory execution with scaled_joint_trajectory_controller + + +The deviation between trajectory interpolation on the ROS side and actual robot execution stays minimal and the +robot reaches the intermediate setpoint instead of returning "too early" as in the example above. + +Under the hood this is implemented by proceeding the trajectory not by a full time step but only by +the fraction determined by the current speed scaling. If speed scaling is currently at 50% then +interpolation of the current control cycle will start half a time step after the beginning of the +previous control cycle. diff --git a/ur_controllers/package.xml b/ur_controllers/package.xml index b92732d7f..6a8d3ef48 100644 --- a/ur_controllers/package.xml +++ b/ur_controllers/package.xml @@ -2,7 +2,7 @@ ur_controllers - 2.4.2 + 2.4.5 Provides controllers that use the speed scaling interface of Universal Robots. Denis Stogl diff --git a/ur_controllers/src/gpio_controller.cpp b/ur_controllers/src/gpio_controller.cpp index 670cf9c8b..d5e6d16ee 100644 --- a/ur_controllers/src/gpio_controller.cpp +++ b/ur_controllers/src/gpio_controller.cpp @@ -278,23 +278,19 @@ ur_controllers::GPIOController::on_activate(const rclcpp_lifecycle::State& /*pre } try { + auto qos_latched = rclcpp::SystemDefaultsQoS(); + qos_latched.transient_local(); // register publisher io_pub_ = get_node()->create_publisher("~/io_states", rclcpp::SystemDefaultsQoS()); tool_data_pub_ = get_node()->create_publisher("~/tool_data", rclcpp::SystemDefaultsQoS()); - robot_mode_pub_ = - get_node()->create_publisher("~/robot_mode", rclcpp::SystemDefaultsQoS()); + robot_mode_pub_ = get_node()->create_publisher("~/robot_mode", qos_latched); - safety_mode_pub_ = - get_node()->create_publisher("~/safety_mode", rclcpp::SystemDefaultsQoS()); - - auto program_state_pub_qos = rclcpp::SystemDefaultsQoS(); - program_state_pub_qos.transient_local(); - program_state_pub_ = - get_node()->create_publisher("~/robot_program_running", program_state_pub_qos); + safety_mode_pub_ = get_node()->create_publisher("~/safety_mode", qos_latched); + program_state_pub_ = get_node()->create_publisher("~/robot_program_running", qos_latched); set_io_srv_ = get_node()->create_service( "~/set_io", std::bind(&GPIOController::setIO, this, std::placeholders::_1, std::placeholders::_2)); diff --git a/ur_dashboard_msgs/CHANGELOG.rst b/ur_dashboard_msgs/CHANGELOG.rst index 0b4bad754..edf82b39d 100644 --- a/ur_dashboard_msgs/CHANGELOG.rst +++ b/ur_dashboard_msgs/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog for package ur_dashboard_msgs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2.4.5 (2024-05-16) +------------------ + +2.4.4 (2024-04-04) +------------------ + +2.4.3 (2024-02-02) +------------------ + 2.4.2 (2023-11-23) ------------------ diff --git a/ur_dashboard_msgs/package.xml b/ur_dashboard_msgs/package.xml index bff6e1b39..26f1b9acd 100644 --- a/ur_dashboard_msgs/package.xml +++ b/ur_dashboard_msgs/package.xml @@ -2,7 +2,7 @@ ur_dashboard_msgs - 2.4.2 + 2.4.5 Messages around the UR Dashboard server. Felix Exner diff --git a/ur_moveit_config/CHANGELOG.rst b/ur_moveit_config/CHANGELOG.rst index afc30cff1..b662833d2 100644 --- a/ur_moveit_config/CHANGELOG.rst +++ b/ur_moveit_config/CHANGELOG.rst @@ -2,6 +2,20 @@ Changelog for package ur_moveit_config ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2.4.5 (2024-05-16) +------------------ +* Fix multi-line strings in DeclareLaunchArgument (`#948 `_) +* Contributors: Matthijs van der Burgh + +2.4.4 (2024-04-04) +------------------ + +2.4.3 (2024-02-02) +------------------ +* fix move_group_node crash during initialization (`#906 `_) +* Add UR30 support (`#899 `_) +* Contributors: Chen Chen, Felix Exner (fexner) + 2.4.2 (2023-11-23) ------------------ * moveit_servo package executable name has changed (`#854 `_) diff --git a/ur_moveit_config/doc/index.rst b/ur_moveit_config/doc/index.rst new file mode 100644 index 000000000..d23eefa9b --- /dev/null +++ b/ur_moveit_config/doc/index.rst @@ -0,0 +1,25 @@ +.. _ur_moveit_config: + +================ +ur_moveit_config +================ + +This package contains an **example** MoveIt! configuration for Universal Robots arms. Since the +default description contains only the arm, this MoveIt! configuration package also only contains the +arm without any objects around it. +In a real-world scenario it is recommended to create a robot_description modelling the robot with its surroundings (e.g. table where it is mounted on, objects in its environment, etc.) and to generate a +*scenario_moveit_config* package from that description as explained in the :ref:`Custom workcell +tutorial `. + +Usage +----- + +With a running driver (real hardware, URSim or mocked hardware), simply start the MoveIt! +interaction using + +.. code-block:: + + ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur5e launch_rviz:=true + +Now you should be able to use the MoveIt Plugin in rviz2 to plan and execute trajectories with the +robot as explained `here `_. diff --git a/ur_moveit_config/launch/ur_moveit.launch.py b/ur_moveit_config/launch/ur_moveit.launch.py index cd7436054..a73bd73be 100644 --- a/ur_moveit_config/launch/ur_moveit.launch.py +++ b/ur_moveit_config/launch/ur_moveit.launch.py @@ -156,9 +156,18 @@ def launch_setup(context, *args, **kwargs): # Planning Configuration ompl_planning_pipeline_config = { "move_group": { - "planning_plugin": "ompl_interface/OMPLPlanner", - "request_adapters": """default_planner_request_adapters/AddTimeOptimalParameterization default_planner_request_adapters/FixWorkspaceBounds default_planner_request_adapters/FixStartStateBounds default_planner_request_adapters/FixStartStateCollision default_planner_request_adapters/FixStartStatePathConstraints""", - "start_state_max_bounds_error": 0.1, + "planning_plugins": ["ompl_interface/OMPLPlanner"], + "request_adapters": [ + "default_planning_request_adapters/ResolveConstraintFrames", + "default_planning_request_adapters/ValidateWorkspaceBounds", + "default_planning_request_adapters/CheckStartStateBounds", + "default_planning_request_adapters/CheckStartStateCollision", + ], + "response_adapters": [ + "default_planning_response_adapters/AddTimeOptimalParameterization", + "default_planning_response_adapters/ValidateSolution", + "default_planning_response_adapters/DisplayMotionPath", + ], } } ompl_planning_yaml = load_yaml("ur_moveit_config", "config/ompl_planning.yaml") @@ -299,8 +308,8 @@ def generate_launch_description(): DeclareLaunchArgument( "description_package", default_value="ur_description", - description="Description package with robot URDF/XACRO files. Usually the argument \ - is not set, it enables use of a custom description.", + description="Description package with robot URDF/XACRO files. Usually the argument " + "is not set, it enables use of a custom description.", ) ) declared_arguments.append( @@ -314,8 +323,8 @@ def generate_launch_description(): DeclareLaunchArgument( "moveit_config_package", default_value="ur_moveit_config", - description="MoveIt config package with robot SRDF/XACRO files. Usually the argument \ - is not set, it enables use of a custom moveit config.", + description="MoveIt config package with robot SRDF/XACRO files. Usually the argument " + "is not set, it enables use of a custom moveit config.", ) ) declared_arguments.append( @@ -350,9 +359,9 @@ def generate_launch_description(): DeclareLaunchArgument( "prefix", default_value='""', - description="Prefix of the joint names, useful for \ - multi-robot setup. If changed than also joint names in the controllers' configuration \ - have to be updated.", + description="Prefix of the joint names, useful for " + "multi-robot setup. If changed than also joint names in the controllers' configuration " + "have to be updated.", ) ) declared_arguments.append( diff --git a/ur_moveit_config/package.xml b/ur_moveit_config/package.xml index d0ae92982..a15a90202 100644 --- a/ur_moveit_config/package.xml +++ b/ur_moveit_config/package.xml @@ -2,7 +2,7 @@ ur_moveit_config - 2.4.2 + 2.4.5 An example package with MoveIt2 configurations for UR robots. diff --git a/ur_robot_driver/CHANGELOG.rst b/ur_robot_driver/CHANGELOG.rst index 2ae004d0e..c7ffeb40b 100644 --- a/ur_robot_driver/CHANGELOG.rst +++ b/ur_robot_driver/CHANGELOG.rst @@ -1,3 +1,26 @@ +2.4.5 (2024-05-16) +------------------ +* Remove dependency to docker.io (`#985 `_) +* Move starting the robot_state_publisher to an own launch file (`#977 `_) + Co-authored-by: Vincenzo Di Pentima +* Update installation instructions for source build (`#967 `_) +* Fix multi-line strings in DeclareLaunchArgument (`#948 `_) +* Contributors: Christoph Fröhlich, Felix Exner (fexner), Matthijs van der Burgh + +2.4.4 (2024-04-04) +------------------ +* Use ros2 control node from controller_manager and description topic (`#939 `_) +* Move communication setup to on_configure instead of on_activate (`#732 `_) +* [URDF] Fix initial value of speed scaling factor syntax (`#920 `_) +* Reduce number of controller_spawners to 3 (`#919 `_) +* Contributors: Felix Exner + +2.4.3 (2024-02-02) +------------------ +* Add UR30 support (`#899 `_) +* Add control description and ros2_control tag to driver. (`#877 `_) +* Contributors: Felix Exner (fexner) + 2.4.2 (2023-11-23) ------------------ * [README] Move installation instructions to subpage (`#870 `_) diff --git a/ur_robot_driver/CMakeLists.txt b/ur_robot_driver/CMakeLists.txt index a45dd3e61..640667234 100644 --- a/ur_robot_driver/CMakeLists.txt +++ b/ur_robot_driver/CMakeLists.txt @@ -84,17 +84,6 @@ add_executable(dashboard_client target_link_libraries(dashboard_client ${catkin_LIBRARIES} ur_client_library::urcl) ament_target_dependencies(dashboard_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS}) -# -# ur_ros2_control_node -# -add_executable(ur_ros2_control_node - src/ur_ros2_control_node.cpp -) -ament_target_dependencies(ur_ros2_control_node - controller_manager - rclcpp -) - # # controller_stopper_node # @@ -110,7 +99,7 @@ add_executable(urscript_interface ament_target_dependencies(urscript_interface ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS}) install( - TARGETS dashboard_client ur_ros2_control_node controller_stopper_node urscript_interface + TARGETS dashboard_client controller_stopper_node urscript_interface DESTINATION lib/${PROJECT_NAME} ) diff --git a/ur_robot_driver/README.md b/ur_robot_driver/README.md index 4f520eb63..8c7a3582f 100644 --- a/ur_robot_driver/README.md +++ b/ur_robot_driver/README.md @@ -5,7 +5,7 @@ repository and requires other packages from that repository. Also, see the [main README](../README.md) for information on how to install and startup this driver. ## ROS-API -The ROS API is documented in a [standalone document](doc/ROS_INTERFACE.md). +The ROS API is documented in a [standalone document](doc/ROS_INTERFACE.rst). ## Technical details The following image shows a very coarse overview of the driver's architecture. @@ -41,7 +41,7 @@ The robot won't accept script code from a remote source unless the robot is put *remote_control-mode*. However, if put into *remote_control-mode*, the program containing the **External Control** program node can't be started from the panel. For this purpose, please use the **dashboard** services to load, start and stop the main program -running on the robot. See the [ROS-API documentation](doc/ROS_INTERFACE.md) for details on the +running on the robot. See the [ROS-API documentation](doc/ROS_INTERFACE.rst) for details on the dashboard services. For using the **tool communication interface** on e-Series robots, a `socat` script is prepared to @@ -75,12 +75,12 @@ The **remote control mode** is needed for many aspects of this driver such as ### Headless mode Inside this driver, there's the **headless** mode, which can be either enabled or not. When the -[headless mode](./doc/ROS_INTERFACE.md#headless_mode-default-false) is activated, required script +[headless mode](./doc/ROS_INTERFACE.rst#headless_mode-default-false) is activated, required script code for external control will be sent to the robot directly when the driver starts. As soon as other script code is sent to the robot either by sending it directly through this driver or by pressing any motion-related button on the teach pendant, the script will be overwritten by this action and has to be restarted by using the -[resend_robot_program](./doc/ROS_INTERFACE.md#resend_robot_program-std_srvstrigger) service. If this +[resend_robot_program](./doc/ROS_INTERFACE.rst#resend_robot_program-std_srvstrigger) service. If this is necessary, you will see the output `Connection to robot dropped, waiting for new connection.` from the driver. Note that pressing "play" on the TP won't start the external control again, but whatever program is currently loaded on the controller. This mode doesn't require the "External diff --git a/ur_robot_driver/doc/ROS_INTERFACE.md b/ur_robot_driver/doc/ROS_INTERFACE.md deleted file mode 100644 index 829302e0a..000000000 --- a/ur_robot_driver/doc/ROS_INTERFACE.md +++ /dev/null @@ -1,213 +0,0 @@ -**NOTE**: This documentation is obsolete and does not cover in full the current state of driver. - -# ROS interface - -The new driver for Universal Robots UR3, UR5 and UR10 robots with CB3 controllers and the e-series. - -## Nodes - -### ur_ros2_control_node - -This is the actual driver node containing the ROS2-Control stack. Interfaces documented here refer to the robot's hardware interface. Controller-specific API elements might be present for the individual controllers outside of this package. - -#### Parameters -Note that parameters are passed through the ros2_control xacro definition. - -##### headless_mode - -Start robot in headless mode. This does not require the 'External Control' URCap to be running on the robot, but this will send the URScript to the robot directly. On e-Series robots this requires the robot to run in 'remote-control' mode. - -##### input_recipe_filename (Required) - -Path to the file containing the recipe used for requesting RTDE inputs. - -##### kinematics/hash (Required) - -Hash of the calibration reported by the robot. This is used for validating the robot description is using the correct calibration. If the robot's calibration doesn't match this hash, an error will be printed. You can use the robot as usual, however Cartesian poses of the endeffector might be inaccurate. See the "ur_calibration" package on help how to generate your own hash matching your actual robot. - -##### non_blocking_read (default: "false") - -Enables non_blocking_read mode. Disables error generated when read returns without any data, sets -the read timeout to zero, and synchronizes read/write operations. This is a leftover from the ROS1 -driver and we currently see no real application where it would make sense to enable this. - -##### output_recipe_filename (Required) - -Path to the file containing the recipe used for requesting RTDE outputs. - -##### reverse_port (Required) - -Port that will be opened to communicate between the driver and the robot controller. - -##### robot_ip (Required) - -The robot's IP address. - -##### script_filename (Required) - -Path to the urscript code that will be sent to the robot. - -##### script_sender_port (Required) - -The driver will offer an interface to receive the program's URScript on this port. - -##### servoj_gain (Required) - -Specify the gain for the underlying servoj command. This will be used whenever position control is -active. A higher value will lead to sharper motions, but might also introduce -higher jerks and vibrations. - -Range: [100 - 3000] - -##### servoj_lookahead_time (Required) - -Specify lookahead_time parameter of underlying servoj command. This will be used whenever position -control is active. A higher value will result in smoother trajectories, but will also introduce a -higher delay between the commands sent from ROS and the motion being executed on the robot. - -Unit: seconds, range: [0.03 - 0.2] - - - -##### tool_baud_rate (Required) - -Baud rate used for tool communication. Will be set as soon as the UR-Program on the robot is started. See UR documentation for valid baud rates. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_parity (Required) - -Parity used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 0 (None), 1 (odd) and 2 (even). Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_rx_idle_chars (Required) - -Number of idle chars for the RX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=1.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_stop_bits (Required) - -Number of stop bits used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 1 or 2. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_tx_idle_chars (Required) - -Number of idle chars for the TX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=0.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### tool_voltage (Required) - -Tool voltage that will be set as soon as the UR-Program on the robot is started. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. - -##### use_tool_communication (Required) - -Should the tool's RS485 interface be forwarded to the ROS machine? This is only available on e-Series models. Setting this parameter to TRUE requires multiple other parameters to be set,as well. - - -### dashboard_client - -#### Advertised Services - -##### add_to_log ([ur_dashboard_msgs/AddToLog](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/AddToLog.html)) - -Service to add a message to the robot's log - -##### brake_release ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Service to release the brakes. If the robot is currently powered off, it will get powered on on the fly. - -##### clear_operational_mode ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -If this service is called the operational mode can again be changed from PolyScope, and the user password is enabled. - -##### close_popup ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Close a (non-safety) popup on the teach pendant. - -##### close_safety_popup ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Close a safety popup on the teach pendant. - -##### connect ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Service to reconnect to the dashboard server - -##### get_loaded_program ([ur_dashboard_msgs/GetLoadedProgram](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetLoadedProgram.html)) - -Load a robot installation from a file - -##### get_robot_mode ([ur_dashboard_msgs/GetRobotMode](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetRobotMode.html)) - -Service to query the current robot mode - -##### get_safety_mode ([ur_dashboard_msgs/GetSafetyMode](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetSafetyMode.html)) - -Service to query the current safety mode - -##### load_installation ([ur_dashboard_msgs/Load](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Load.html)) - -Load a robot installation from a file - -##### load_program ([ur_dashboard_msgs/Load](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Load.html)) - -Load a robot program from a file - -##### pause ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Pause a running program. - -##### play ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Start execution of a previously loaded program - -##### popup ([ur_dashboard_msgs/Popup](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/Popup.html)) - -Service to show a popup on the UR Teach pendant. - -##### power_off ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Power off the robot motors - -##### power_on ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Power on the robot motors. To fully start the robot, call 'brake_release' afterwards. - -##### program_running ([ur_dashboard_msgs/IsProgramRunning](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/IsProgramRunning.html)) - -Query whether there is currently a program running - -##### program_saved ([ur_dashboard_msgs/IsProgramSaved](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/IsProgramSaved.html)) - -Query whether the current program is saved - -##### program_state ([ur_dashboard_msgs/GetProgramState](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetProgramState.html)) - -Service to query the current program state - -##### quit ([ur_dashboard_msgs/GetLoadedProgram](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/GetLoadedProgram.html)) - -Disconnect from the dashboard service. - -##### raw_request ([ur_dashboard_msgs/RawRequest](http://docs.ros.org/api/ur_dashboard_msgs/html/srv/RawRequest.html)) - -General purpose service to send arbitrary messages to the dashboard server - -##### restart_safety ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Used when robot gets a safety fault or violation to restart the safety. After safety has been rebooted the robot will be in Power Off. NOTE: You should always ensure it is okay to restart the system. It is highly recommended to check the error log before using this command (either via PolyScope or e.g. ssh connection). - -##### shutdown ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Shutdown the robot controller - -##### stop ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Stop program execution on the robot - -##### unlock_protective_stop ([std_srvs/Trigger](http://docs.ros.org/api/std_srvs/html/srv/Trigger.html)) - -Dismiss a protective stop to continue robot movements. NOTE: It is the responsibility of the user to ensure the cause of the protective stop is resolved before calling this service. - -#### Parameters - -##### receive_timeout (Required) - -Timeout after which a call to the dashboard server will be considered failure if no answer has been received. - -##### robot_ip (Required) - -The IP address under which the robot is reachable. diff --git a/ur_robot_driver/doc/ROS_INTERFACE.rst b/ur_robot_driver/doc/ROS_INTERFACE.rst new file mode 100644 index 000000000..0f7a4f373 --- /dev/null +++ b/ur_robot_driver/doc/ROS_INTERFACE.rst @@ -0,0 +1,264 @@ + +**NOTE**\ : This documentation is obsolete and does not cover in full the current state of driver. + +ROS interface +============= + +The new driver for Universal Robots UR3, UR5 and UR10 robots with CB3 controllers and the e-series. + +Nodes +----- + +ur_ros2_control_node +^^^^^^^^^^^^^^^^^^^^ + +This is the actual driver node containing the ROS2-Control stack. Interfaces documented here refer to the robot's hardware interface. Controller-specific API elements might be present for the individual controllers outside of this package. + +Parameters +~~~~~~~~~~ + +Note that parameters are passed through the ros2_control xacro definition. + +headless_mode +""""""""""""" + +Start robot in headless mode. This does not require the 'External Control' URCap to be running on the robot, but this will send the URScript to the robot directly. On e-Series robots this requires the robot to run in 'remote-control' mode. + +input_recipe_filename (Required) +"""""""""""""""""""""""""""""""" + +Path to the file containing the recipe used for requesting RTDE inputs. + +kinematics/hash (Required) +"""""""""""""""""""""""""" + +Hash of the calibration reported by the robot. This is used for validating the robot description is using the correct calibration. If the robot's calibration doesn't match this hash, an error will be printed. You can use the robot as usual, however Cartesian poses of the endeffector might be inaccurate. See the "ur_calibration" package on help how to generate your own hash matching your actual robot. + +non_blocking_read (default: "false") +"""""""""""""""""""""""""""""""""""" + +Enables non_blocking_read mode. Disables error generated when read returns without any data, sets +the read timeout to zero, and synchronizes read/write operations. This is a leftover from the ROS1 +driver and we currently see no real application where it would make sense to enable this. + +output_recipe_filename (Required) +""""""""""""""""""""""""""""""""" + +Path to the file containing the recipe used for requesting RTDE outputs. + +reverse_port (Required) +""""""""""""""""""""""" + +Port that will be opened to communicate between the driver and the robot controller. + +robot_ip (Required) +""""""""""""""""""" + +The robot's IP address. + +script_filename (Required) +"""""""""""""""""""""""""" + +Path to the urscript code that will be sent to the robot. + +script_sender_port (Required) +""""""""""""""""""""""""""""" + +The driver will offer an interface to receive the program's URScript on this port. + +servoj_gain (Required) +"""""""""""""""""""""" + +Specify the gain for the underlying servoj command. This will be used whenever position control is +active. A higher value will lead to sharper motions, but might also introduce +higher jerks and vibrations. + +Range: [100 - 3000] + +servoj_lookahead_time (Required) +"""""""""""""""""""""""""""""""" + +Specify lookahead_time parameter of underlying servoj command. This will be used whenever position +control is active. A higher value will result in smoother trajectories, but will also introduce a +higher delay between the commands sent from ROS and the motion being executed on the robot. + +Unit: seconds, range: [0.03 - 0.2] + +tool_baud_rate (Required) +""""""""""""""""""""""""" + +Baud rate used for tool communication. Will be set as soon as the UR-Program on the robot is started. See UR documentation for valid baud rates. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +tool_parity (Required) +"""""""""""""""""""""" + +Parity used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 0 (None), 1 (odd) and 2 (even). Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +tool_rx_idle_chars (Required) +""""""""""""""""""""""""""""" + +Number of idle chars for the RX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=1.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +tool_stop_bits (Required) +""""""""""""""""""""""""" + +Number of stop bits used for tool communication. Will be set as soon as the UR-Program on the robot is started. Can be 1 or 2. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +tool_tx_idle_chars (Required) +""""""""""""""""""""""""""""" + +Number of idle chars for the TX unit used for tool communication. Will be set as soon as the UR-Program on the robot is started. Valid values: min=0.0, max=40.0 Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +tool_voltage (Required) +""""""""""""""""""""""" + +Tool voltage that will be set as soon as the UR-Program on the robot is started. Note: This parameter is only evaluated, when the parameter "use_tool_communication" is set to TRUE. Then, this parameter is required. + +use_tool_communication (Required) +""""""""""""""""""""""""""""""""" + +Should the tool's RS485 interface be forwarded to the ROS machine? This is only available on e-Series models. Setting this parameter to TRUE requires multiple other parameters to be set as well. + +dashboard_client +^^^^^^^^^^^^^^^^ + +Advertised Services +~~~~~~~~~~~~~~~~~~~ + +add_to_log (\ `ur_dashboard_msgs/AddToLog `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to add a message to the robot's log + +brake_release (\ `std_srvs/Trigger `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to release the brakes. If the robot is currently powered off, it will get powered on on the fly. + +clear_operational_mode (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +If this service is called the operational mode can again be changed from PolyScope, and the user password is enabled. + +close_popup (\ `std_srvs/Trigger `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Close a (non-safety) popup on the teach pendant. + +close_safety_popup (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Close a safety popup on the teach pendant. + +connect (\ `std_srvs/Trigger `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to reconnect to the dashboard server + +get_loaded_program (\ `ur_dashboard_msgs/GetLoadedProgram `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Load a robot installation from a file + +get_robot_mode (\ `ur_dashboard_msgs/GetRobotMode `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to query the current robot mode + +get_safety_mode (\ `ur_dashboard_msgs/GetSafetyMode `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to query the current safety mode + +load_installation (\ `ur_dashboard_msgs/Load `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Load a robot installation from a file + +load_program (\ `ur_dashboard_msgs/Load `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Load a robot program from a file + +pause (\ `std_srvs/Trigger `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Pause a running program. + +play (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Start execution of a previously loaded program + +popup (\ `ur_dashboard_msgs/Popup `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to show a popup on the UR Teach pendant. + +power_off (\ `std_srvs/Trigger `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Power off the robot motors + +power_on (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Power on the robot motors. To fully start the robot, call 'brake_release' afterwards. + +program_running (\ `ur_dashboard_msgs/IsProgramRunning `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Query whether there is currently a program running + +program_saved (\ `ur_dashboard_msgs/IsProgramSaved `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Query whether the current program is saved + +program_state (\ `ur_dashboard_msgs/GetProgramState `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Service to query the current program state + +quit (\ `ur_dashboard_msgs/GetLoadedProgram `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Disconnect from the dashboard service. + +raw_request (\ `ur_dashboard_msgs/RawRequest `_\ ) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +General purpose service to send arbitrary messages to the dashboard server + +restart_safety (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Used when robot gets a safety fault or violation to restart the safety. After safety has been rebooted the robot will be in Power Off. NOTE: You should always ensure it is okay to restart the system. It is highly recommended to check the error log before using this command (either via PolyScope or e.g. ssh connection). + +shutdown (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Shutdown the robot controller + +stop (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Stop program execution on the robot + +unlock_protective_stop (\ `std_srvs/Trigger `_\ ) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Dismiss a protective stop to continue robot movements. NOTE: It is the responsibility of the user to ensure the cause of the protective stop is resolved before calling this service. + +Parameters +~~~~~~~~~~ + +receive_timeout (Required) +"""""""""""""""""""""""""" + +Timeout after which a call to the dashboard server will be considered failure if no answer has been received. + +robot_ip (Required) +""""""""""""""""""" + +The IP address under which the robot is reachable. diff --git a/ur_robot_driver/doc/index.rst b/ur_robot_driver/doc/index.rst index bf536ad81..bef2baf74 100644 --- a/ur_robot_driver/doc/index.rst +++ b/ur_robot_driver/doc/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root ``toctree`` directive. -Welcome to ur_robot_driver's documentation! -=========================================== +ur_robot_driver +=============== .. toctree:: :maxdepth: 2 @@ -15,11 +15,3 @@ Welcome to ur_robot_driver's documentation! usage setup_tool_communication ROS_INTERFACE - generated/index - - - -Indices and tables -================== - -* :ref:`genindex` diff --git a/ur_robot_driver/doc/installation/installation.rst b/ur_robot_driver/doc/installation/installation.rst index eed955798..b6820262a 100644 --- a/ur_robot_driver/doc/installation/installation.rst +++ b/ur_robot_driver/doc/installation/installation.rst @@ -46,12 +46,12 @@ building might fail occasionally. export COLCON_WS=~/workspace/ros_ur_driver mkdir -p $COLCON_WS/src -5. Clone relevant packages, install dependencies, compile, and source the workspace by using: +5. Clone relevant packages (replace ```` with ``humble``, ``iron`` or ``main`` for rolling), install dependencies, compile, and source the workspace by using: .. code-block:: bash cd $COLCON_WS - git clone https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver.git src/Universal_Robots_ROS2_Driver + git clone -b https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver.git src/Universal_Robots_ROS2_Driver vcs import src --skip-existing --input src/Universal_Robots_ROS2_Driver/Universal_Robots_ROS2_Driver-not-released.${ROS_DISTRO}.repos rosdep update rosdep install --ignore-src --from-paths src -y diff --git a/ur_robot_driver/doc/installation/real_time.md b/ur_robot_driver/doc/installation/real_time.md deleted file mode 100644 index e55a9e638..000000000 --- a/ur_robot_driver/doc/installation/real_time.md +++ /dev/null @@ -1,282 +0,0 @@ -# Setting up Ubuntu with a PREEMPT_RT kernel -In order to run the `universal_robot_driver`, we highly recommend to setup a ubuntu system with -real-time capabilities. Especially with a robot from the e-Series the higher control frequency -might lead to non-smooth trajectory execution if not run using a real-time-enabled system. - -You might still be able to control the robot using a non-real-time system. This is, however, not recommended. - -To get real-time support into a ubuntu system, the following steps have to be performed: - 1. Get the sources of a real-time kernel - 2. Compile the real-time kernel - 3. Setup user privileges to execute real-time tasks - -This guide will help you setup your system with a real-time kernel. - -## Preparing -To build the kernel, you will need a couple of tools available on your system. You can install them -using - -``` bash -$ sudo apt-get install build-essential bc ca-certificates gnupg2 libssl-dev wget gawk flex bison -``` - -Before you download the sources of a real-time-enabled kernel, check the kernel version that is currently installed: - -```bash -$ uname -r -4.15.0-62-generic -``` - -To continue with this tutorial, please create a temporary folder and navigate into it. You should -have sufficient space (around 25GB) there, as the extracted kernel sources take much space. After -the new kernel is installed, you can delete this folder again. - -In this example we will use a temporary folder inside our home folder: - -```bash -$ mkdir -p ${HOME}/rt_kernel_build -$ cd ${HOME}/rt_kernel_build -``` - -All future commands are expected to be run inside this folder. If the folder is different, the `$` -sign will be prefixed with a path relative to the above folder. - -## Getting the sources for a real-time kernel -To build a real-time kernel, we first need to get the kernel sources and the real-time patch. - -First, we must decide on the kernel version that we want to use. Above, we -determined that our system has a 4.15 kernel installed. However, real-time -patches exist only for selected kernel versions. Those can be found on the -[linuxfoundation wiki](https://wiki.linuxfoundation.org/realtime/preempt_rt_versions). - -In this example, we will select a 4.14 kernel. Select a kernel version close to the -one installed on your system. - -Go ahead and download the kernel sources, patch sources and their signature files: - -```bash -$ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.xz -$ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.sign -$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.xz -$ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.sign -``` - -To unzip the downloaded files do -```bash -$ xz -dk patch-4.14.139-rt66.patch.xz -$ xz -d linux-4.14.139.tar.xz -``` - -### Verification -Technically, you can skip this section, it is however highly recommended to verify the file -integrity of such a core component of your system! - -To verify file integrity, you must first import public keys by the kernel developers and the patch -author. For the kernel sources use (as suggested on -[kernel.org](https://www.kernel.org/signature.html)) - -```bash -$ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org -``` - -and for the patch search for a key of the author listed on -[linuxfoundation wiki](https://wiki.linuxfoundation.org/realtime/preempt_rt_versions). - -```bash -$ gpg2 --keyserver hkp://keys.gnupg.net --search-keys zanussi -gpg: data source: http://51.38.91.189:11371 -(1) German Daniel Zanussi - 4096 bit RSA key 0x537F98A9D92CEAC8, created: 2019-07-24, expires: 2023-07-24 -(2) Michael Zanussi - 4096 bit RSA key 0x7C7F76A2C1E3D9EB, created: 2019-05-08 -(3) Tom Zanussi - Tom Zanussi - Tom Zanussi - 4096 bit RSA key 0xDE09826778A38521, created: 2017-12-15 -(4) Riccardo Zanussi - 2048 bit RSA key 0xD299A06261D919C3, created: 2014-08-27, expires: 2018-08-27 (expired) -(5) Zanussi Gianni - 1024 bit DSA key 0x78B89CB020D1836C, created: 2004-04-06 -(6) Michael Zanussi - Michael Zanussi - Michael Zanussi - Michael Zanussi - 1024 bit DSA key 0xB3E952DCAC653064, created: 2000-09-05 -(7) Michael Zanussi - 1024 bit DSA key 0xEB10BBD9BA749318, created: 1999-05-31 -(8) Michael B. Zanussi - 1024 bit DSA key 0x39EE4EAD7BBB1E43, created: 1998-07-16 -Keys 1-8 of 8 for "zanussi". Enter number(s), N)ext, or Q)uit > 3 -``` - -Now we can verify the downloaded sources: -```bash -$ gpg2 --verify linux-4.14.139.tar.sign -gpg: assuming signed data in 'linux-4.14.139.tar' -gpg: Signature made Fr 16 Aug 2019 10:15:17 CEST -gpg: using RSA key 647F28654894E3BD457199BE38DBBDC86092693E -gpg: Good signature from "Greg Kroah-Hartman " [unknown] -gpg: WARNING: This key is not certified with a trusted signature! -gpg: There is no indication that the signature belongs to the owner. -Primary key fingerprint: 647F 2865 4894 E3BD 4571 99BE 38DB BDC8 6092 693E - -$ gpg2 --verify patch-4.14.139-rt66.patch.sign -gpg: assuming signed data in 'patch-4.14.139-rt66.patch' -gpg: Signature made Fr 23 Aug 2019 21:09:20 CEST -gpg: using RSA key 0x0129F38552C38DF1 -gpg: Good signature from "Tom Zanussi " [unknown] -gpg: aka "Tom Zanussi " [unknown] -gpg: aka "Tom Zanussi " [unknown] -gpg: WARNING: This key is not certified with a trusted signature! -gpg: There is no indication that the signature belongs to the owner. -Primary key fingerprint: 5BDF C45C 2ECC 5387 D50C E5EF DE09 8267 78A3 8521 - Subkey fingerprint: ACF8 5F98 16A8 D5F0 96AE 1FD2 0129 F385 52C3 8DF1 -``` - -## Compilation -Before we can compile the sources, we have to extract the tar archive and apply the patch - -```bash -$ tar xf linux-4.14.139.tar -$ cd linux-4.14.139 -linux-4.14.139$ xzcat ../patch-4.14.139-rt66.patch.xz | patch -p1 -``` - -Now to configure your kernel, just type -```bash -linux-4.14.139$ make oldconfig -``` - -This will ask for kernel options. For everything else then the `Preemption Model` use the default -value (just press Enter) or adapt to your preferences. For the preemption model select `Fully Preemptible Kernel`: - -```bash -Preemption Model - 1. No Forced Preemption (Server) (PREEMPT_NONE) -> 2. Voluntary Kernel Preemption (Desktop) (PREEMPT_VOLUNTARY) - 3. Preemptible Kernel (Low-Latency Desktop) (PREEMPT__LL) (NEW) - 4. Preemptible Kernel (Basic RT) (PREEMPT_RTB) (NEW) - 5. Fully Preemptible Kernel (RT) (PREEMPT_RT_FULL) (NEW) -choice[1-5]: 5 -``` - -Now you can build the kernel. This will take some time... - -```bash -linux-4.14.139$ make -j `getconf _NPROCESSORS_ONLN` deb-pkg -``` - -After building, install the `linux-headers` and `linux-image` packages in the parent folder (only -the ones without the `-dbg` in the name) - -```bash -linux-4.14.139$ sudo apt install ../linux-headers-4.14.139-rt66_*.deb ../linux-image-4.14.139-rt66_*.deb -``` - -## Setup user privileges to use real-time scheduling -To be able to schedule threads with user privileges (what the driver will do) you'll have to change -the user's limits by changing `/etc/security/limits.conf` (See [the manpage](https://manpages.ubuntu.com/manpages/bionic/man5/limits.conf.5.html) for details) - -We recommend to setup a group for real-time users instead of writing a fixed username into the config -file: - -```bash -$ sudo groupadd realtime -$ sudo usermod -aG realtime $(whoami) -``` - -Then, make sure `/etc/security/limits.conf` contains -``` -@realtime soft rtprio 99 -@realtime soft priority 99 -@realtime soft memlock 102400 -@realtime hard rtprio 99 -@realtime hard priority 99 -@realtime hard memlock 102400 -``` - -Note: You will have to log out and log back in (Not only close your terminal window) for these -changes to take effect. No need to do this now, as we will reboot later on, anyway. - -## Setup GRUB to always boot the real-time kernel -To make the new kernel the default kernel that the system will boot into every time, you'll have to -change the grub config file inside `/etc/default/grub`. - -Note: This works for ubuntu, but might not be working for other linux systems. It might be necessary -to use another menuentry name there. - -But first, let's find out the name of the entry that we will want to make the default. You can list -all available kernels using - -```bash -$ awk -F\' '/menuentry |submenu / {print $1 $2}' /boot/grub/grub.cfg - -menuentry Ubuntu -submenu Advanced options for Ubuntu - menuentry Ubuntu, with Linux 4.15.0-62-generic - menuentry Ubuntu, with Linux 4.15.0-62-generic (recovery mode) - menuentry Ubuntu, with Linux 4.15.0-60-generic - menuentry Ubuntu, with Linux 4.15.0-60-generic (recovery mode) - menuentry Ubuntu, with Linux 4.15.0-58-generic - menuentry Ubuntu, with Linux 4.15.0-58-generic (recovery mode) - menuentry Ubuntu, with Linux 4.14.139-rt66 - menuentry Ubuntu, with Linux 4.14.139-rt66 (recovery mode) -menuentry Memory test (memtest86+) -menuentry Memory test (memtest86+, serial console 115200) -menuentry Windows 7 (on /dev/sdc2) -menuentry Windows 7 (on /dev/sdc3) -``` - -From the output above, we'll need to generate a string with the pattern `"submenu_name>entry_name"`. In our case this would be - -``` -"Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66" -``` -**The double quotes and no spaces around the `>` are important!** - -With this, we can setup the default grub entry and then update the grub menu entries. Don't forget this last step! - -```bash -$ sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66"/' /etc/default/grub -$ sudo update-grub -``` - -## Reboot the PC -After having performed the above mentioned steps, reboot the PC. It should boot into the correct -kernel automatically. - -## Check for preemption capabilities -Make sure that the kernel does indeed support real-time scheduling: - -```bash -$ uname -v | cut -d" " -f1-4 -#1 SMP PREEMPT RT -``` - -## Optional: Disable CPU speed scaling -Many modern CPUs support changing their clock frequency dynamically depending on the currently -requested computation resources. In some cases this can lead to small interruptions in execution. -While the real-time scheduled controller thread should be unaffected by this, any external -components such as a visual servoing system might be interrupted for a short period on scaling -changes. - -To check and modify the power saving mode, install cpufrequtils: -```bash -$ sudo apt install cpufrequtils -``` - -Run `cpufreq-info` to check available "governors" and the current CPU Frequency (`current CPU -frequency is XXX MHZ`). In the following we will set the governor to "performance". - -```bash -$ sudo systemctl disable ondemand -$ sudo systemctl enable cpufrequtils -$ sudo sh -c 'echo "GOVERNOR=performance" > /etc/default/cpufrequtils' -$ sudo systemctl daemon-reload && sudo systemctl restart cpufrequtils -``` - -This disables the `ondemand` CPU scaling daemon, creates a `cpufrequtils` config file and restarts -the `cpufrequtils` service. Check with `cpufreq-info`. - -For further information about governors, please see the [kernel -documentation](https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt). diff --git a/ur_robot_driver/doc/installation/real_time.rst b/ur_robot_driver/doc/installation/real_time.rst new file mode 100644 index 000000000..2bb5b7869 --- /dev/null +++ b/ur_robot_driver/doc/installation/real_time.rst @@ -0,0 +1,311 @@ + +Setting up Ubuntu with a PREEMPT_RT kernel +========================================== + +In order to run the ``universal_robot_driver``\ , we highly recommend to setup a ubuntu system with +real-time capabilities. Especially with a robot from the e-Series the higher control frequency +might lead to non-smooth trajectory execution if not run using a real-time-enabled system. + +You might still be able to control the robot using a non-real-time system. This is, however, not recommended. + +To get real-time support into a ubuntu system, the following steps have to be performed: + + +#. Get the sources of a real-time kernel +#. Compile the real-time kernel +#. Setup user privileges to execute real-time tasks + +This guide will help you setup your system with a real-time kernel. + +Preparing +--------- + +To build the kernel, you will need a couple of tools available on your system. You can install them +using + +.. code-block:: bash + + $ sudo apt-get install build-essential bc ca-certificates gnupg2 libssl-dev wget gawk flex bison + +Before you download the sources of a real-time-enabled kernel, check the kernel version that is currently installed: + +.. code-block:: bash + + $ uname -r + 4.15.0-62-generic + +To continue with this tutorial, please create a temporary folder and navigate into it. You should +have sufficient space (around 25GB) there, as the extracted kernel sources take much space. After +the new kernel is installed, you can delete this folder again. + +In this example we will use a temporary folder inside our home folder: + +.. code-block:: bash + + $ mkdir -p ${HOME}/rt_kernel_build + $ cd ${HOME}/rt_kernel_build + +All future commands are expected to be run inside this folder. If the folder is different, the ``$`` +sign will be prefixed with a path relative to the above folder. + +Getting the sources for a real-time kernel +------------------------------------------ + +To build a real-time kernel, we first need to get the kernel sources and the real-time patch. + +First, we must decide on the kernel version that we want to use. Above, we +determined that our system has a 4.15 kernel installed. However, real-time +patches exist only for selected kernel versions. Those can be found on the +`linuxfoundation wiki `_. + +In this example, we will select a 4.14 kernel. Select a kernel version close to the +one installed on your system. + +Go ahead and download the kernel sources, patch sources and their signature files: + +.. code-block:: bash + + $ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.xz + $ wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/4.14/patch-4.14.139-rt66.patch.sign + $ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.xz + $ wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.14.139.tar.sign + +To unzip the downloaded files do + +.. code-block:: bash + + $ xz -dk patch-4.14.139-rt66.patch.xz + $ xz -d linux-4.14.139.tar.xz + +Verification +^^^^^^^^^^^^ + +Technically, you can skip this section, it is however highly recommended to verify the file +integrity of such a core component of your system! + +To verify file integrity, you must first import public keys by the kernel developers and the patch +author. For the kernel sources use (as suggested on +`kernel.org `_\ ) + +.. code-block:: bash + + $ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org + +and for the patch search for a key of the author listed on +`linuxfoundation wiki `_. + +.. code-block:: bash + + $ gpg2 --keyserver hkp://keys.gnupg.net --search-keys zanussi + gpg: data source: http://51.38.91.189:11371 + (1) German Daniel Zanussi + 4096 bit RSA key 0x537F98A9D92CEAC8, created: 2019-07-24, expires: 2023-07-24 + (2) Michael Zanussi + 4096 bit RSA key 0x7C7F76A2C1E3D9EB, created: 2019-05-08 + (3) Tom Zanussi + Tom Zanussi + Tom Zanussi + 4096 bit RSA key 0xDE09826778A38521, created: 2017-12-15 + (4) Riccardo Zanussi + 2048 bit RSA key 0xD299A06261D919C3, created: 2014-08-27, expires: 2018-08-27 (expired) + (5) Zanussi Gianni + 1024 bit DSA key 0x78B89CB020D1836C, created: 2004-04-06 + (6) Michael Zanussi + Michael Zanussi + Michael Zanussi + Michael Zanussi + 1024 bit DSA key 0xB3E952DCAC653064, created: 2000-09-05 + (7) Michael Zanussi + 1024 bit DSA key 0xEB10BBD9BA749318, created: 1999-05-31 + (8) Michael B. Zanussi + 1024 bit DSA key 0x39EE4EAD7BBB1E43, created: 1998-07-16 + Keys 1-8 of 8 for "zanussi". Enter number(s), N)ext, or Q)uit > 3 + +Now we can verify the downloaded sources: + +.. code-block:: bash + + $ gpg2 --verify linux-4.14.139.tar.sign + gpg: assuming signed data in 'linux-4.14.139.tar' + gpg: Signature made Fr 16 Aug 2019 10:15:17 CEST + gpg: using RSA key 647F28654894E3BD457199BE38DBBDC86092693E + gpg: Good signature from "Greg Kroah-Hartman " [unknown] + gpg: WARNING: This key is not certified with a trusted signature! + gpg: There is no indication that the signature belongs to the owner. + Primary key fingerprint: 647F 2865 4894 E3BD 4571 99BE 38DB BDC8 6092 693E + + $ gpg2 --verify patch-4.14.139-rt66.patch.sign + gpg: assuming signed data in 'patch-4.14.139-rt66.patch' + gpg: Signature made Fr 23 Aug 2019 21:09:20 CEST + gpg: using RSA key 0x0129F38552C38DF1 + gpg: Good signature from "Tom Zanussi " [unknown] + gpg: aka "Tom Zanussi " [unknown] + gpg: aka "Tom Zanussi " [unknown] + gpg: WARNING: This key is not certified with a trusted signature! + gpg: There is no indication that the signature belongs to the owner. + Primary key fingerprint: 5BDF C45C 2ECC 5387 D50C E5EF DE09 8267 78A3 8521 + Subkey fingerprint: ACF8 5F98 16A8 D5F0 96AE 1FD2 0129 F385 52C3 8DF1 + +Compilation +----------- + +Before we can compile the sources, we have to extract the tar archive and apply the patch + +.. code-block:: bash + + $ tar xf linux-4.14.139.tar + $ cd linux-4.14.139 + linux-4.14.139$ xzcat ../patch-4.14.139-rt66.patch.xz | patch -p1 + +Now to configure your kernel, just type + +.. code-block:: bash + + linux-4.14.139$ make oldconfig + +This will ask for kernel options. For everything else then the ``Preemption Model`` use the default +value (just press Enter) or adapt to your preferences. For the preemption model select ``Fully Preemptible Kernel``\ : + +.. code-block:: bash + + Preemption Model + 1. No Forced Preemption (Server) (PREEMPT_NONE) + > 2. Voluntary Kernel Preemption (Desktop) (PREEMPT_VOLUNTARY) + 3. Preemptible Kernel (Low-Latency Desktop) (PREEMPT__LL) (NEW) + 4. Preemptible Kernel (Basic RT) (PREEMPT_RTB) (NEW) + 5. Fully Preemptible Kernel (RT) (PREEMPT_RT_FULL) (NEW) + choice[1-5]: 5 + +Now you can build the kernel. This will take some time... + +.. code-block:: bash + + linux-4.14.139$ make -j $(getconf _NPROCESSORS_ONLN) deb-pkg + +After building, install the ``linux-headers`` and ``linux-image`` packages in the parent folder (only +the ones without the ``-dbg`` in the name) + +.. code-block:: bash + + linux-4.14.139$ sudo apt install ../linux-headers-4.14.139-rt66_*.deb ../linux-image-4.14.139-rt66_*.deb + +Setup user privileges to use real-time scheduling +------------------------------------------------- + +To be able to schedule threads with user privileges (what the driver will do) you'll have to change +the user's limits by changing ``/etc/security/limits.conf`` (See `the manpage `_ for details) + +We recommend to setup a group for real-time users instead of writing a fixed username into the config +file: + +.. code-block:: bash + + $ sudo groupadd realtime + $ sudo usermod -aG realtime $(whoami) + +Then, make sure ``/etc/security/limits.conf`` contains + +.. code-block:: + + @realtime soft rtprio 99 + @realtime soft priority 99 + @realtime soft memlock 102400 + @realtime hard rtprio 99 + @realtime hard priority 99 + @realtime hard memlock 102400 + +Note: You will have to log out and log back in (Not only close your terminal window) for these +changes to take effect. No need to do this now, as we will reboot later on, anyway. + +Setup GRUB to always boot the real-time kernel +---------------------------------------------- + +To make the new kernel the default kernel that the system will boot into every time, you'll have to +change the grub config file inside ``/etc/default/grub``. + +Note: This works for ubuntu, but might not be working for other linux systems. It might be necessary +to use another menuentry name there. + +But first, let's find out the name of the entry that we will want to make the default. You can list +all available kernels using + +.. code-block:: bash + + $ awk -F\' '/menuentry |submenu / {print $1 $2}' /boot/grub/grub.cfg + + menuentry Ubuntu + submenu Advanced options for Ubuntu + menuentry Ubuntu, with Linux 4.15.0-62-generic + menuentry Ubuntu, with Linux 4.15.0-62-generic (recovery mode) + menuentry Ubuntu, with Linux 4.15.0-60-generic + menuentry Ubuntu, with Linux 4.15.0-60-generic (recovery mode) + menuentry Ubuntu, with Linux 4.15.0-58-generic + menuentry Ubuntu, with Linux 4.15.0-58-generic (recovery mode) + menuentry Ubuntu, with Linux 4.14.139-rt66 + menuentry Ubuntu, with Linux 4.14.139-rt66 (recovery mode) + menuentry Memory test (memtest86+) + menuentry Memory test (memtest86+, serial console 115200) + menuentry Windows 7 (on /dev/sdc2) + menuentry Windows 7 (on /dev/sdc3) + +From the output above, we'll need to generate a string with the pattern ``"submenu_name>entry_name"``. In our case this would be + +.. code-block:: + + "Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66" + +**The double quotes and no spaces around the ``>`` are important!** + +With this, we can setup the default grub entry and then update the grub menu entries. Don't forget this last step! + +.. code-block:: bash + + $ sudo sed -i 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 4.14.139-rt66"/' /etc/default/grub + $ sudo update-grub + +Reboot the PC +------------- + +After having performed the above mentioned steps, reboot the PC. It should boot into the correct +kernel automatically. + +Check for preemption capabilities +--------------------------------- + +Make sure that the kernel does indeed support real-time scheduling: + +.. code-block:: bash + + $ uname -v | cut -d" " -f1-4 + #1 SMP PREEMPT RT + +Optional: Disable CPU speed scaling +----------------------------------- + +Many modern CPUs support changing their clock frequency dynamically depending on the currently +requested computation resources. In some cases this can lead to small interruptions in execution. +While the real-time scheduled controller thread should be unaffected by this, any external +components such as a visual servoing system might be interrupted for a short period on scaling +changes. + +To check and modify the power saving mode, install cpufrequtils: + +.. code-block:: bash + + $ sudo apt install cpufrequtils + +Run ``cpufreq-info`` to check available "governors" and the current CPU Frequency (\ ``current CPU +frequency is XXX MHZ``\ ). In the following we will set the governor to "performance". + +.. code-block:: bash + + $ sudo systemctl disable ondemand + $ sudo systemctl enable cpufrequtils + $ sudo sh -c 'echo "GOVERNOR=performance" > /etc/default/cpufrequtils' + $ sudo systemctl daemon-reload && sudo systemctl restart cpufrequtils + +This disables the ``ondemand`` CPU scaling daemon, creates a ``cpufrequtils`` config file and restarts +the ``cpufrequtils`` service. Check with ``cpufreq-info``. + +For further information about governors, please see the `kernel +documentation `_. diff --git a/ur_robot_driver/doc/usage.rst b/ur_robot_driver/doc/usage.rst index 1a3acd329..6e23697ae 100644 --- a/ur_robot_driver/doc/usage.rst +++ b/ur_robot_driver/doc/usage.rst @@ -178,11 +178,8 @@ Using MoveIt `MoveIt! `_ support is built-in into this driver already. -Real robot / URSim -^^^^^^^^^^^^^^^^^^ - To test the driver with the example MoveIt-setup, first start the driver as described -`above <#start-hardware-simulator-or-mockup>`_. +`above <#start-hardware-simulator-or-mockup>`_ and then start the MoveIt! nodes using .. code-block:: @@ -191,17 +188,7 @@ To test the driver with the example MoveIt-setup, first start the driver as desc Now you should be able to use the MoveIt Plugin in rviz2 to plan and execute trajectories with the robot as explained `here `_. -Mock hardware -^^^^^^^^^^^^^ - -Currently, the ``scaled_joint_trajectory_controller`` does not work with ros2_control mock_hardware. There is an -`upstream Merge-Request `_ pending to fix that. Until this is merged and released, you'll have to fallback to the ``joint_trajectory_controller`` by passing ``initial_controller:=joint_trajectory_controller`` to the driver's startup. Also, you'll have to tell MoveIt! that you're using mock_hardware as it then has to map to the other controller: - -.. code-block:: - - ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy use_mock_hardware:=true launch_rviz:=false initial_joint_controller:=joint_trajectory_controller - # and in another shell - ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur5e launch_rviz:=true use_mock_hardware:=true +For more details, please see :ref:`ur_moveit_config`. Robot frames ------------ diff --git a/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp b/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp index 63f762f09..fe1ede6f7 100644 --- a/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp +++ b/ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp @@ -96,8 +96,9 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface std::vector export_command_interfaces() final; + hardware_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) final; hardware_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) final; - hardware_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) final; + hardware_interface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State& previous_state) final; hardware_interface::return_type read(const rclcpp::Time& time, const rclcpp::Duration& period) final; hardware_interface::return_type write(const rclcpp::Time& time, const rclcpp::Duration& period) final; @@ -223,7 +224,7 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface std::unique_ptr ur_driver_; std::shared_ptr async_thread_; - bool rtde_comm_has_been_started_ = false; + std::atomic_bool rtde_comm_has_been_started_ = false; }; } // namespace ur_robot_driver diff --git a/ur_robot_driver/launch/ur10.launch.py b/ur_robot_driver/launch/ur10.launch.py index fe484af5e..8bbc698db 100644 --- a/ur_robot_driver/launch/ur10.launch.py +++ b/ur_robot_driver/launch/ur10.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur10e.launch.py b/ur_robot_driver/launch/ur10e.launch.py index a75b5ad9c..024712440 100644 --- a/ur_robot_driver/launch/ur10e.launch.py +++ b/ur_robot_driver/launch/ur10e.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur16e.launch.py b/ur_robot_driver/launch/ur16e.launch.py index e488b40e7..f10d56c3b 100644 --- a/ur_robot_driver/launch/ur16e.launch.py +++ b/ur_robot_driver/launch/ur16e.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur20.launch.py b/ur_robot_driver/launch/ur20.launch.py index 9c462252c..a5f8afc4d 100644 --- a/ur_robot_driver/launch/ur20.launch.py +++ b/ur_robot_driver/launch/ur20.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur3.launch.py b/ur_robot_driver/launch/ur3.launch.py index c7b823eea..d37e50dd9 100644 --- a/ur_robot_driver/launch/ur3.launch.py +++ b/ur_robot_driver/launch/ur3.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur30.launch.py b/ur_robot_driver/launch/ur30.launch.py index 2ac76eb65..fcbac536b 100644 --- a/ur_robot_driver/launch/ur30.launch.py +++ b/ur_robot_driver/launch/ur30.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur3e.launch.py b/ur_robot_driver/launch/ur3e.launch.py index 0ba3bcbad..87e5a949d 100644 --- a/ur_robot_driver/launch/ur3e.launch.py +++ b/ur_robot_driver/launch/ur3e.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur5.launch.py b/ur_robot_driver/launch/ur5.launch.py index 6a6300e7e..6a1439693 100644 --- a/ur_robot_driver/launch/ur5.launch.py +++ b/ur_robot_driver/launch/ur5.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur5e.launch.py b/ur_robot_driver/launch/ur5e.launch.py index 3a166cce7..df36d7e1d 100644 --- a/ur_robot_driver/launch/ur5e.launch.py +++ b/ur_robot_driver/launch/ur5e.launch.py @@ -55,8 +55,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( diff --git a/ur_robot_driver/launch/ur_control.launch.py b/ur_robot_driver/launch/ur_control.launch.py index 18ad4afa1..923abfd03 100644 --- a/ur_robot_driver/launch/ur_control.launch.py +++ b/ur_robot_driver/launch/ur_control.launch.py @@ -34,208 +34,42 @@ from launch_ros.substitutions import FindPackageShare from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, OpaqueFunction +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription from launch.conditions import IfCondition, UnlessCondition -from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution +from launch.substitutions import ( + LaunchConfiguration, + PathJoinSubstitution, +) +from launch.launch_description_sources import AnyLaunchDescriptionSource -def launch_setup(context, *args, **kwargs): +def launch_setup(): # Initialize Arguments ur_type = LaunchConfiguration("ur_type") robot_ip = LaunchConfiguration("robot_ip") - safety_limits = LaunchConfiguration("safety_limits") - safety_pos_margin = LaunchConfiguration("safety_pos_margin") - safety_k_position = LaunchConfiguration("safety_k_position") # General arguments - runtime_config_package = LaunchConfiguration("runtime_config_package") controllers_file = LaunchConfiguration("controllers_file") - description_package = LaunchConfiguration("description_package") - description_file = LaunchConfiguration("description_file") - tf_prefix = LaunchConfiguration("tf_prefix") + description_launchfile = LaunchConfiguration("description_launchfile") use_mock_hardware = LaunchConfiguration("use_mock_hardware") - mock_sensor_commands = LaunchConfiguration("mock_sensor_commands") controller_spawner_timeout = LaunchConfiguration("controller_spawner_timeout") initial_joint_controller = LaunchConfiguration("initial_joint_controller") activate_joint_controller = LaunchConfiguration("activate_joint_controller") launch_rviz = LaunchConfiguration("launch_rviz") + rviz_config_file = LaunchConfiguration("rviz_config_file") headless_mode = LaunchConfiguration("headless_mode") launch_dashboard_client = LaunchConfiguration("launch_dashboard_client") use_tool_communication = LaunchConfiguration("use_tool_communication") - tool_parity = LaunchConfiguration("tool_parity") - tool_baud_rate = LaunchConfiguration("tool_baud_rate") - tool_stop_bits = LaunchConfiguration("tool_stop_bits") - tool_rx_idle_chars = LaunchConfiguration("tool_rx_idle_chars") - tool_tx_idle_chars = LaunchConfiguration("tool_tx_idle_chars") tool_device_name = LaunchConfiguration("tool_device_name") tool_tcp_port = LaunchConfiguration("tool_tcp_port") - tool_voltage = LaunchConfiguration("tool_voltage") - reverse_ip = LaunchConfiguration("reverse_ip") - script_command_port = LaunchConfiguration("script_command_port") - reverse_port = LaunchConfiguration("reverse_port") - script_sender_port = LaunchConfiguration("script_sender_port") - trajectory_port = LaunchConfiguration("trajectory_port") - - joint_limit_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "joint_limits.yaml"] - ) - kinematics_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "default_kinematics.yaml"] - ) - physical_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "physical_parameters.yaml"] - ) - visual_params = PathJoinSubstitution( - [FindPackageShare(description_package), "config", ur_type, "visual_parameters.yaml"] - ) - script_filename = PathJoinSubstitution( - [FindPackageShare("ur_client_library"), "resources", "external_control.urscript"] - ) - input_recipe_filename = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "resources", "rtde_input_recipe.txt"] - ) - output_recipe_filename = PathJoinSubstitution( - [FindPackageShare("ur_robot_driver"), "resources", "rtde_output_recipe.txt"] - ) - - robot_description_content = Command( - [ - PathJoinSubstitution([FindExecutable(name="xacro")]), - " ", - PathJoinSubstitution([FindPackageShare("ur_robot_driver"), "urdf", description_file]), - " ", - "robot_ip:=", - robot_ip, - " ", - "joint_limit_params:=", - joint_limit_params, - " ", - "kinematics_params:=", - kinematics_params, - " ", - "physical_params:=", - physical_params, - " ", - "visual_params:=", - visual_params, - " ", - "safety_limits:=", - safety_limits, - " ", - "safety_pos_margin:=", - safety_pos_margin, - " ", - "safety_k_position:=", - safety_k_position, - " ", - "name:=", - ur_type, - " ", - "script_filename:=", - script_filename, - " ", - "input_recipe_filename:=", - input_recipe_filename, - " ", - "output_recipe_filename:=", - output_recipe_filename, - " ", - "tf_prefix:=", - tf_prefix, - " ", - "use_mock_hardware:=", - use_mock_hardware, - " ", - "mock_sensor_commands:=", - mock_sensor_commands, - " ", - "headless_mode:=", - headless_mode, - " ", - "use_tool_communication:=", - use_tool_communication, - " ", - "tool_parity:=", - tool_parity, - " ", - "tool_baud_rate:=", - tool_baud_rate, - " ", - "tool_stop_bits:=", - tool_stop_bits, - " ", - "tool_rx_idle_chars:=", - tool_rx_idle_chars, - " ", - "tool_tx_idle_chars:=", - tool_tx_idle_chars, - " ", - "tool_device_name:=", - tool_device_name, - " ", - "tool_tcp_port:=", - tool_tcp_port, - " ", - "tool_voltage:=", - tool_voltage, - " ", - "reverse_ip:=", - reverse_ip, - " ", - "script_command_port:=", - script_command_port, - " ", - "reverse_port:=", - reverse_port, - " ", - "script_sender_port:=", - script_sender_port, - " ", - "trajectory_port:=", - trajectory_port, - " ", - ] - ) - robot_description = {"robot_description": robot_description_content} - - initial_joint_controllers = PathJoinSubstitution( - [FindPackageShare(runtime_config_package), "config", controllers_file] - ) - - rviz_config_file = PathJoinSubstitution( - [FindPackageShare(description_package), "rviz", "view_robot.rviz"] - ) - - # define update rate - update_rate_config_file = PathJoinSubstitution( - [ - FindPackageShare(runtime_config_package), - "config", - ur_type.perform(context) + "_update_rate.yaml", - ] - ) control_node = Node( package="controller_manager", executable="ros2_control_node", parameters=[ - robot_description, - update_rate_config_file, - ParameterFile(initial_joint_controllers, allow_substs=True), + LaunchConfiguration("update_rate_config_file"), + ParameterFile(controllers_file, allow_substs=True), ], output="screen", - condition=IfCondition(use_mock_hardware), - ) - - ur_control_node = Node( - package="ur_robot_driver", - executable="ur_ros2_control_node", - parameters=[ - robot_description, - update_rate_config_file, - ParameterFile(initial_joint_controllers, allow_substs=True), - ], - output="screen", - condition=UnlessCondition(use_mock_hardware), ) dashboard_client_node = Node( @@ -292,13 +126,6 @@ def launch_setup(context, *args, **kwargs): ], ) - robot_state_publisher_node = Node( - package="robot_state_publisher", - executable="robot_state_publisher", - output="both", - parameters=[robot_description], - ) - rviz_node = Node( package="rviz2", condition=IfCondition(launch_rviz), @@ -309,31 +136,31 @@ def launch_setup(context, *args, **kwargs): ) # Spawn controllers - def controller_spawner(name, active=True): + def controller_spawner(controllers, active=True): inactive_flags = ["--inactive"] if not active else [] return Node( package="controller_manager", executable="spawner", arguments=[ - name, "--controller-manager", "/controller_manager", "--controller-manager-timeout", controller_spawner_timeout, ] - + inactive_flags, + + inactive_flags + + controllers, ) - controller_spawner_names = [ + controllers_active = [ "joint_state_broadcaster", "io_and_status_controller", "speed_scaling_state_broadcaster", "force_torque_sensor_broadcaster", ] - controller_spawner_inactive_names = ["forward_position_controller"] + controllers_inactive = ["forward_position_controller"] - controller_spawners = [controller_spawner(name) for name in controller_spawner_names] + [ - controller_spawner(name, active=False) for name in controller_spawner_inactive_names + controller_spawners = [controller_spawner(controllers_active)] + [ + controller_spawner(controllers_inactive, active=False) ] # There may be other controllers of the joints, but this is the initially-started one @@ -363,14 +190,21 @@ def controller_spawner(name, active=True): condition=UnlessCondition(activate_joint_controller), ) + rsp = IncludeLaunchDescription( + AnyLaunchDescriptionSource(description_launchfile), + launch_arguments={ + "robot_ip": robot_ip, + "ur_type": ur_type, + }.items(), + ) + nodes_to_start = [ control_node, - ur_control_node, dashboard_client_node, tool_communication_node, controller_stopper_node, urscript_interface, - robot_state_publisher_node, + rsp, rviz_node, initial_joint_controller_spawner_stopped, initial_joint_controller_spawner_started, @@ -386,7 +220,17 @@ def generate_launch_description(): DeclareLaunchArgument( "ur_type", description="Type/series of used UR robot.", - choices=["ur3", "ur3e", "ur5", "ur5e", "ur10", "ur10e", "ur16e", "ur20", "ur30"], + choices=[ + "ur3", + "ur3e", + "ur5", + "ur5e", + "ur10", + "ur10e", + "ur16e", + "ur20", + "ur30", + ], ) ) declared_arguments.append( @@ -416,43 +260,33 @@ def generate_launch_description(): ) ) # General arguments - declared_arguments.append( - DeclareLaunchArgument( - "runtime_config_package", - default_value="ur_robot_driver", - description='Package with the controller\'s configuration in "config" folder. \ - Usually the argument is not set, it enables use of a custom setup.', - ) - ) declared_arguments.append( DeclareLaunchArgument( "controllers_file", - default_value="ur_controllers.yaml", + default_value=PathJoinSubstitution( + [FindPackageShare("ur_robot_driver"), "config", "ur_controllers.yaml"] + ), description="YAML file with the controllers configuration.", ) ) declared_arguments.append( DeclareLaunchArgument( - "description_package", - default_value="ur_description", - description="Description package with robot URDF/XACRO files. Usually the argument \ - is not set, it enables use of a custom description.", - ) - ) - declared_arguments.append( - DeclareLaunchArgument( - "description_file", - default_value="ur.urdf.xacro", - description="URDF/XACRO description file with the robot.", + "description_launchfile", + default_value=PathJoinSubstitution( + [FindPackageShare("ur_robot_driver"), "launch", "ur_rsp.launch.py"] + ), + description="Launchfile (absolute path) providing the description. " + "The launchfile has to start a robot_state_publisher node that " + "publishes the description topic.", ) ) declared_arguments.append( DeclareLaunchArgument( "tf_prefix", default_value="", - description="tf_prefix of the joint names, useful for \ - multi-robot setup. If changed, also joint names in the controllers' configuration \ - have to be updated.", + description="tf_prefix of the joint names, useful for " + "multi-robot setup. If changed, also joint names in the controllers' configuration " + "have to be updated.", ) ) declared_arguments.append( @@ -466,8 +300,8 @@ def generate_launch_description(): DeclareLaunchArgument( "mock_sensor_commands", default_value="false", - description="Enable mock command interfaces for sensors used for simple simulations. \ - Used only if 'use_mock_hardware' parameter is true.", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", ) ) declared_arguments.append( @@ -503,7 +337,18 @@ def generate_launch_description(): ) declared_arguments.append( DeclareLaunchArgument( - "launch_dashboard_client", default_value="true", description="Launch Dashboard Client?" + "rviz_config_file", + default_value=PathJoinSubstitution( + [FindPackageShare("ur_description"), "rviz", "view_robot.rviz"] + ), + description="RViz config file (absolute path) to use when launching rviz.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "launch_dashboard_client", + default_value="true", + description="Launch Dashboard Client?", ) ) declared_arguments.append( @@ -517,57 +362,57 @@ def generate_launch_description(): DeclareLaunchArgument( "tool_parity", default_value="0", - description="Parity configuration for serial communication. Only effective, if \ - use_tool_communication is set to True.", + description="Parity configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_baud_rate", default_value="115200", - description="Baud rate configuration for serial communication. Only effective, if \ - use_tool_communication is set to True.", + description="Baud rate configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_stop_bits", default_value="1", - description="Stop bits configuration for serial communication. Only effective, if \ - use_tool_communication is set to True.", + description="Stop bits configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_rx_idle_chars", default_value="1.5", - description="RX idle chars configuration for serial communication. Only effective, \ - if use_tool_communication is set to True.", + description="RX idle chars configuration for serial communication. Only effective, " + "if use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_tx_idle_chars", default_value="3.5", - description="TX idle chars configuration for serial communication. Only effective, \ - if use_tool_communication is set to True.", + description="TX idle chars configuration for serial communication. Only effective, " + "if use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_device_name", default_value="/tmp/ttyUR", - description="File descriptor that will be generated for the tool communication device. \ - The user has be be allowed to write to this location. \ - Only effective, if use_tool_communication is set to True.", + description="File descriptor that will be generated for the tool communication device. " + "The user has be be allowed to write to this location. " + "Only effective, if use_tool_communication is set to True.", ) ) declared_arguments.append( DeclareLaunchArgument( "tool_tcp_port", default_value="54321", - description="Remote port that will be used for bridging the tool's serial device. \ - Only effective, if use_tool_communication is set to True.", + description="Remote port that will be used for bridging the tool's serial device. " + "Only effective, if use_tool_communication is set to True.", ) ) declared_arguments.append( @@ -612,4 +457,20 @@ def generate_launch_description(): description="Port that will be opened for trajectory control.", ) ) - return LaunchDescription(declared_arguments + [OpaqueFunction(function=launch_setup)]) + declared_arguments.append( + DeclareLaunchArgument( + name="update_rate_config_file", + default_value=[ + PathJoinSubstitution( + [ + FindPackageShare("ur_robot_driver"), + "config", + ] + ), + "/", + LaunchConfiguration("ur_type"), + "_update_rate.yaml", + ], + ) + ) + return LaunchDescription(declared_arguments + launch_setup()) diff --git a/ur_robot_driver/launch/ur_rsp.launch.py b/ur_robot_driver/launch/ur_rsp.launch.py new file mode 100644 index 000000000..aa2324223 --- /dev/null +++ b/ur_robot_driver/launch/ur_rsp.launch.py @@ -0,0 +1,472 @@ +# Copyright (c) 2024 FZI Forschungszentrum Informatik +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the {copyright_holder} nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# +# Author: Felix Exner + +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import ( + Command, + FindExecutable, + LaunchConfiguration, + PathJoinSubstitution, +) + + +def generate_launch_description(): + ur_type = LaunchConfiguration("ur_type") + robot_ip = LaunchConfiguration("robot_ip") + safety_limits = LaunchConfiguration("safety_limits") + safety_pos_margin = LaunchConfiguration("safety_pos_margin") + safety_k_position = LaunchConfiguration("safety_k_position") + # General arguments + kinematics_params_file = LaunchConfiguration("kinematics_params_file") + physical_params_file = LaunchConfiguration("physical_params_file") + visual_params_file = LaunchConfiguration("visual_params_file") + joint_limit_params_file = LaunchConfiguration("joint_limit_params_file") + description_file = LaunchConfiguration("description_file") + tf_prefix = LaunchConfiguration("tf_prefix") + use_mock_hardware = LaunchConfiguration("use_mock_hardware") + mock_sensor_commands = LaunchConfiguration("mock_sensor_commands") + headless_mode = LaunchConfiguration("headless_mode") + use_tool_communication = LaunchConfiguration("use_tool_communication") + tool_parity = LaunchConfiguration("tool_parity") + tool_baud_rate = LaunchConfiguration("tool_baud_rate") + tool_stop_bits = LaunchConfiguration("tool_stop_bits") + tool_rx_idle_chars = LaunchConfiguration("tool_rx_idle_chars") + tool_tx_idle_chars = LaunchConfiguration("tool_tx_idle_chars") + tool_device_name = LaunchConfiguration("tool_device_name") + tool_tcp_port = LaunchConfiguration("tool_tcp_port") + tool_voltage = LaunchConfiguration("tool_voltage") + reverse_ip = LaunchConfiguration("reverse_ip") + script_command_port = LaunchConfiguration("script_command_port") + reverse_port = LaunchConfiguration("reverse_port") + script_sender_port = LaunchConfiguration("script_sender_port") + trajectory_port = LaunchConfiguration("trajectory_port") + + script_filename = PathJoinSubstitution( + [ + FindPackageShare("ur_client_library"), + "resources", + "external_control.urscript", + ] + ) + input_recipe_filename = PathJoinSubstitution( + [FindPackageShare("ur_robot_driver"), "resources", "rtde_input_recipe.txt"] + ) + output_recipe_filename = PathJoinSubstitution( + [FindPackageShare("ur_robot_driver"), "resources", "rtde_output_recipe.txt"] + ) + + robot_description_content = Command( + [ + PathJoinSubstitution([FindExecutable(name="xacro")]), + " ", + description_file, + " ", + "robot_ip:=", + robot_ip, + " ", + "joint_limit_params:=", + joint_limit_params_file, + " ", + "kinematics_params:=", + kinematics_params_file, + " ", + "physical_params:=", + physical_params_file, + " ", + "visual_params:=", + visual_params_file, + " ", + "safety_limits:=", + safety_limits, + " ", + "safety_pos_margin:=", + safety_pos_margin, + " ", + "safety_k_position:=", + safety_k_position, + " ", + "name:=", + ur_type, + " ", + "script_filename:=", + script_filename, + " ", + "input_recipe_filename:=", + input_recipe_filename, + " ", + "output_recipe_filename:=", + output_recipe_filename, + " ", + "tf_prefix:=", + tf_prefix, + " ", + "use_mock_hardware:=", + use_mock_hardware, + " ", + "mock_sensor_commands:=", + mock_sensor_commands, + " ", + "headless_mode:=", + headless_mode, + " ", + "use_tool_communication:=", + use_tool_communication, + " ", + "tool_parity:=", + tool_parity, + " ", + "tool_baud_rate:=", + tool_baud_rate, + " ", + "tool_stop_bits:=", + tool_stop_bits, + " ", + "tool_rx_idle_chars:=", + tool_rx_idle_chars, + " ", + "tool_tx_idle_chars:=", + tool_tx_idle_chars, + " ", + "tool_device_name:=", + tool_device_name, + " ", + "tool_tcp_port:=", + tool_tcp_port, + " ", + "tool_voltage:=", + tool_voltage, + " ", + "reverse_ip:=", + reverse_ip, + " ", + "script_command_port:=", + script_command_port, + " ", + "reverse_port:=", + reverse_port, + " ", + "script_sender_port:=", + script_sender_port, + " ", + "trajectory_port:=", + trajectory_port, + " ", + ] + ) + robot_description = {"robot_description": robot_description_content} + + declared_arguments = [] + # UR specific arguments + declared_arguments.append( + DeclareLaunchArgument( + "ur_type", + description="Typo/series of used UR robot.", + choices=[ + "ur3", + "ur3e", + "ur5", + "ur5e", + "ur10", + "ur10e", + "ur16e", + "ur20", + "ur30", + ], + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "robot_ip", description="IP address by which the robot can be reached." + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "safety_limits", + default_value="true", + description="Enables the safety limits controller if true.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "safety_pos_margin", + default_value="0.15", + description="The margin to lower and upper limits in the safety controller.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "safety_k_position", + default_value="20", + description="k-position factor in the safety controller.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "joint_limit_params_file", + default_value=PathJoinSubstitution( + [ + FindPackageShare("ur_description"), + "config", + ur_type, + "joint_limits.yaml", + ] + ), + description="Config file containing the joint limits (e.g. velocities, positions) of the robot.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "kinematics_params_file", + default_value=PathJoinSubstitution( + [ + FindPackageShare("ur_description"), + "config", + ur_type, + "default_kinematics.yaml", + ] + ), + description="The calibration configuration of the actual robot used.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "physical_params_file", + default_value=PathJoinSubstitution( + [ + FindPackageShare("ur_description"), + "config", + ur_type, + "physical_parameters.yaml", + ] + ), + description="Config file containing the physical parameters (e.g. masses, inertia) of the robot.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "visual_params_file", + default_value=PathJoinSubstitution( + [ + FindPackageShare("ur_description"), + "config", + ur_type, + "visual_parameters.yaml", + ] + ), + description="Config file containing the visual parameters (e.g. meshes) of the robot.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "description_file", + default_value=PathJoinSubstitution( + [FindPackageShare("ur_robot_driver"), "urdf", "ur.urdf.xacro"] + ), + description="URDF/XACRO description file with the robot.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tf_prefix", + default_value="", + description="tf_prefix of the joint names, useful for " + "multi-robot setup. If changed, also joint names in the controllers' configuration " + "have to be updated.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "use_mock_hardware", + default_value="false", + description="Start robot with mock hardware mirroring command to its states.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "mock_sensor_commands", + default_value="false", + description="Enable mock command interfaces for sensors used for simple simulations. " + "Used only if 'use_mock_hardware' parameter is true.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "headless_mode", + default_value="false", + description="Enable headless mode for robot control", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "initial_joint_controller", + default_value="scaled_joint_trajectory_controller", + description="Initially loaded robot controller.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "activate_joint_controller", + default_value="true", + description="Activate loaded joint controller.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument("launch_rviz", default_value="true", description="Launch RViz?") + ) + declared_arguments.append( + DeclareLaunchArgument( + "launch_dashboard_client", + default_value="true", + description="Launch Dashboard Client?", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "use_tool_communication", + default_value="false", + description="Only available for e series!", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_parity", + default_value="0", + description="Parity configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_baud_rate", + default_value="115200", + description="Baud rate configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_stop_bits", + default_value="1", + description="Stop bits configuration for serial communication. Only effective, if " + "use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_rx_idle_chars", + default_value="1.5", + description="RX idle chars configuration for serial communication. Only effective, " + "if use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_tx_idle_chars", + default_value="3.5", + description="TX idle chars configuration for serial communication. Only effective, " + "if use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_device_name", + default_value="/tmp/ttyUR", + description="File descriptor that will be generated for the tool communication device. " + "The user has be be allowed to write to this location. " + "Only effective, if use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_tcp_port", + default_value="54321", + description="Remote port that will be used for bridging the tool's serial device. " + "Only effective, if use_tool_communication is set to True.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "tool_voltage", + default_value="0", # 0 being a conservative value that won't destroy anything + description="Tool voltage that will be setup.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "reverse_ip", + default_value="0.0.0.0", + description="IP that will be used for the robot controller to communicate back to the driver.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "script_command_port", + default_value="50004", + description="Port that will be opened to forward URScript commands to the robot.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "reverse_port", + default_value="50001", + description="Port that will be opened to send cyclic instructions from the driver to the robot controller.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "script_sender_port", + default_value="50002", + description="The driver will offer an interface to query the external_control URScript on this port.", + ) + ) + declared_arguments.append( + DeclareLaunchArgument( + "trajectory_port", + default_value="50003", + description="Port that will be opened for trajectory control.", + ) + ) + + return LaunchDescription( + declared_arguments + + [ + Node( + package="robot_state_publisher", + executable="robot_state_publisher", + output="both", + parameters=[robot_description], + ), + ] + ) diff --git a/ur_robot_driver/package.xml b/ur_robot_driver/package.xml index c4d06d1d1..c01a02041 100644 --- a/ur_robot_driver/package.xml +++ b/ur_robot_driver/package.xml @@ -2,7 +2,7 @@ ur_robot_driver - 2.4.2 + 2.4.5 The new driver for Universal Robots UR3, UR5 and UR10 robots with CB3 controllers and the e-series. Denis Stogl @@ -59,7 +59,6 @@ velocity_controllers xacro - docker.io launch_testing_ament_cmake diff --git a/ur_robot_driver/src/hardware_interface.cpp b/ur_robot_driver/src/hardware_interface.cpp index 2434e5248..8ff61080e 100644 --- a/ur_robot_driver/src/hardware_interface.cpp +++ b/ur_robot_driver/src/hardware_interface.cpp @@ -59,7 +59,7 @@ URPositionHardwareInterface::~URPositionHardwareInterface() { // If the controller manager is shutdown via Ctrl + C the on_deactivate methods won't be called. // We therefore need to make sure to actually deactivate the communication - on_deactivate(rclcpp_lifecycle::State()); + on_cleanup(rclcpp_lifecycle::State()); } hardware_interface::CallbackReturn @@ -302,7 +302,7 @@ std::vector URPositionHardwareInterface::e } hardware_interface::CallbackReturn -URPositionHardwareInterface::on_activate(const rclcpp_lifecycle::State& previous_state) +URPositionHardwareInterface::on_configure(const rclcpp_lifecycle::State& previous_state) { RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Starting ...please wait..."); @@ -463,13 +463,22 @@ URPositionHardwareInterface::on_activate(const rclcpp_lifecycle::State& previous } hardware_interface::CallbackReturn -URPositionHardwareInterface::on_deactivate(const rclcpp_lifecycle::State& previous_state) +URPositionHardwareInterface::on_activate(const rclcpp_lifecycle::State& previous_state) +{ + RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Activating HW interface"); + return hardware_interface::CallbackReturn::SUCCESS; +} + +hardware_interface::CallbackReturn +URPositionHardwareInterface::on_cleanup(const rclcpp_lifecycle::State& previous_state) { RCLCPP_INFO(rclcpp::get_logger("URPositionHardwareInterface"), "Stopping ...please wait..."); - async_thread_shutdown_ = true; - async_thread_->join(); - async_thread_.reset(); + if (async_thread_) { + async_thread_shutdown_ = true; + async_thread_->join(); + async_thread_.reset(); + } ur_driver_.reset(); diff --git a/ur_robot_driver/src/ur_ros2_control_node.cpp b/ur_robot_driver/src/ur_ros2_control_node.cpp deleted file mode 100644 index 5cce2c044..000000000 --- a/ur_robot_driver/src/ur_ros2_control_node.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2022 Universal Robots A/S -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// * Neither the name of the Universal Robots A/S nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -//---------------------------------------------------------------------- -/*!\file - * - * \author Lovro Ivanov lovro.ivanov@gmail.com - * \date 2022-1-7 - * - */ -//---------------------------------------------------------------------- - -#include -#include - -// ROS includes -#include "controller_manager/controller_manager.hpp" -#include "rclcpp/rclcpp.hpp" -#include "realtime_tools/thread_priority.hpp" - -// code is inspired by -// https://github.com/ros-controls/ros2_control/blob/master/controller_manager/src/ros2_control_node.cpp - -int main(int argc, char** argv) -{ - rclcpp::init(argc, argv); - - // create executor - std::shared_ptr e = std::make_shared(); - // create controller manager instance - auto controller_manager = std::make_shared(e, "controller_manager"); - - // control loop thread - std::thread control_loop([controller_manager]() { - if (!realtime_tools::configure_sched_fifo(50)) { - RCLCPP_WARN(controller_manager->get_logger(), "Could not enable FIFO RT scheduling policy"); - } - - // for calculating sleep time - auto const period = std::chrono::nanoseconds(1'000'000'000 / controller_manager->get_update_rate()); - auto const cm_now = std::chrono::nanoseconds(controller_manager->now().nanoseconds()); - std::chrono::time_point next_iteration_time{ cm_now }; - - // for calculating the measured period of the loop - rclcpp::Time previous_time = controller_manager->now(); - - while (rclcpp::ok()) { - // calculate measured period - auto const current_time = controller_manager->now(); - auto const measured_period = current_time - previous_time; - previous_time = current_time; - - // execute update loop - controller_manager->read(controller_manager->now(), measured_period); - controller_manager->update(controller_manager->now(), measured_period); - controller_manager->write(controller_manager->now(), measured_period); - - // wait until we hit the end of the period - next_iteration_time += period; - std::this_thread::sleep_until(next_iteration_time); - } - }); - - // spin the executor with controller manager node - e->add_node(controller_manager); - e->spin(); - - // wait for control loop to finish - control_loop.join(); - - // shutdown - rclcpp::shutdown(); - - return 0; -} diff --git a/ur_robot_driver/urdf/ur.ros2_control.xacro b/ur_robot_driver/urdf/ur.ros2_control.xacro index 074f94221..308071cc1 100644 --- a/ur_robot_driver/urdf/ur.ros2_control.xacro +++ b/ur_robot_driver/urdf/ur.ros2_control.xacro @@ -84,8 +84,9 @@ - - 1 + + 1.0 +