Skip to content

Commit cac074d

Browse files
committed
Urscript interface (#721)
* Add a urscript interface node * Add urscript_interface to standard launchfile * Added documentation for urscript_interface * Add a notice about incorrect script code * Add test for urscript interface * Move tests to one single tests This should avoid that different tests run in parallel * Wait for IO controller before checking IOs * Write an initial textmessage when connecting the urscript_interface * Wait for controller_manager services longer * Make sure we have a clean robot state without any program running once we enter our test similar to how we did it on the robot_driver test * Remove unneeded Destructor definition (cherry picked from commit 7ee47bd)
1 parent 79932af commit cac074d

File tree

5 files changed

+461
-1
lines changed

5 files changed

+461
-1
lines changed

ur_robot_driver/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,13 @@ add_executable(controller_stopper_node
103103
)
104104
ament_target_dependencies(controller_stopper_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS})
105105

106+
add_executable(urscript_interface
107+
src/urscript_interface.cpp
108+
)
109+
ament_target_dependencies(urscript_interface ${${PROJECT_NAME}_EXPORTED_TARGETS} ${THIS_PACKAGE_INCLUDE_DEPENDS})
110+
106111
install(
107-
TARGETS dashboard_client ur_ros2_control_node controller_stopper_node
112+
TARGETS dashboard_client ur_ros2_control_node controller_stopper_node urscript_interface
108113
DESTINATION lib/${PROJECT_NAME}
109114
)
110115

@@ -182,5 +187,9 @@ if(BUILD_TESTING)
182187
TIMEOUT
183188
500
184189
)
190+
add_launch_test(test/urscript_interface.py
191+
TIMEOUT
192+
500
193+
)
185194
endif()
186195
endif()

ur_robot_driver/doc/usage.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,61 @@ Then start
240240
ros2 launch ur_robot_driver ur_control.launch.py ur_type:=ur5e robot_ip:=yyy.yyy.yyy.yyy use_fake_hardware:=true launch_rviz:=false initial_joint_controller:=joint_trajectory_controller
241241
# and in another shell
242242
ros2 launch ur_moveit_config ur_moveit.launch.py ur_type:=ur5e launch_rviz:=true
243+
244+
Custom URScript commands
245+
------------------------
246+
247+
The driver's package contains a ``urscript_interface`` node that allows sending URScript snippets
248+
directly to the robot. It gets started in the driver's launchfiles by default. To use it, simply
249+
publish a message to its interface:
250+
251+
.. code-block:: bash
252+
253+
# simple popup
254+
ros2 topic pub /urscript_interface/script_command std_msgs/msg/String '{data: popup("hello")}' --once
255+
256+
Be aware, that running a program on this interface (meaning publishing script code to that interface) stops any running program on the robot.
257+
Thus, the motion-interpreting program that is started by the driver gets stopped and has to be
258+
restarted again. Depending whether you use headless mode or not, you'll have to call the
259+
``resend_program`` service or press the ``play`` button on the teach panel to start the
260+
external_control program again.
261+
262+
.. note::
263+
Currently, there is no feedback on the code's correctness. If the code sent to the
264+
robot is incorrect, it will silently not get executed. Make sure that you send valid URScript code!
265+
266+
Multi-line programs
267+
^^^^^^^^^^^^^^^^^^^
268+
269+
When you want to define multi-line programs, make sure to check that newlines are correctly
270+
interpreted from your message. For this purpose the driver prints the program as it is being sent to
271+
the robot. When sending a multi-line program from the command line, you can use an empty line
272+
between each statement:
273+
274+
.. code-block:: bash
275+
276+
ros2 topic pub --once /urscript_interface/script_command std_msgs/msg/String '{data:
277+
"def my_prog():
278+
279+
set_digital_out(1, True)
280+
281+
movej(p[0.2, 0.3, 0.8, 0, 0, 3.14], a=1.2, v=0.25, r=0)
282+
283+
textmsg(\"motion finished\")
284+
285+
end"}'
286+
287+
Non-interrupting programs
288+
^^^^^^^^^^^^^^^^^^^^^^^^^
289+
290+
To prevent interrupting the main program, you can send certain commands as `secondary programs
291+
<https://www.universal-robots.com/articles/ur/programming/secondary-program/>`_.
292+
293+
.. code-block:: bash
294+
295+
ros2 topic pub --once /urscript_interface/script_command std_msgs/msg/String '{data:
296+
"sec my_prog():
297+
298+
textmsg(\"This is a log message\")
299+
300+
end"}'

ur_robot_driver/launch/ur_control.launch.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,13 @@ def launch_setup(context, *args, **kwargs):
243243
],
244244
)
245245

246+
urscript_interface = Node(
247+
package="ur_robot_driver",
248+
executable="urscript_interface",
249+
parameters=[{"robot_ip": robot_ip}],
250+
output="screen",
251+
)
252+
246253
controller_stopper_node = Node(
247254
package="ur_robot_driver",
248255
executable="controller_stopper_node",
@@ -341,6 +348,7 @@ def controller_spawner(name, active=True):
341348
dashboard_client_node,
342349
tool_communication_node,
343350
controller_stopper_node,
351+
urscript_interface,
344352
robot_state_publisher_node,
345353
rviz_node,
346354
initial_joint_controller_spawner_stopped,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2023, FZI Forschungszentrum Informatik, Created on behalf of Universal Robots A/S
2+
//
3+
// Redistribution and use in source and binary forms, with or without
4+
// modification, are permitted provided that the following conditions are met:
5+
//
6+
// * Redistributions of source code must retain the above copyright
7+
// notice, this list of conditions and the following disclaimer.
8+
//
9+
// * Redistributions in binary form must reproduce the above copyright
10+
// notice, this list of conditions and the following disclaimer in the
11+
// documentation and/or other materials provided with the distribution.
12+
//
13+
// * Neither the name of the {copyright_holder} nor the names of its
14+
// contributors may be used to endorse or promote products derived from
15+
// this software without specific prior written permission.
16+
//
17+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
// POSSIBILITY OF SUCH DAMAGE.
28+
29+
//----------------------------------------------------------------------
30+
/*!\file
31+
*
32+
* \author Felix Exner [email protected]
33+
* \date 2023-06-20
34+
*
35+
*/
36+
//----------------------------------------------------------------------
37+
38+
#include <ur_client_library/comm/stream.h>
39+
#include <ur_client_library/primary/primary_package.h>
40+
41+
#include <memory>
42+
43+
#include <rclcpp/rclcpp.hpp>
44+
#include <std_msgs/msg/string.hpp>
45+
46+
class URScriptInterface : public rclcpp::Node
47+
{
48+
public:
49+
URScriptInterface()
50+
: Node("urscript_interface")
51+
, m_script_sub(this->create_subscription<std_msgs::msg::String>(
52+
"~/script_command", 1, [this](const std_msgs::msg::String::SharedPtr msg) {
53+
auto program_with_newline = msg->data + '\n';
54+
55+
RCLCPP_INFO_STREAM(this->get_logger(), program_with_newline);
56+
57+
size_t len = program_with_newline.size();
58+
const auto* data = reinterpret_cast<const uint8_t*>(program_with_newline.c_str());
59+
size_t written;
60+
61+
if (m_secondary_stream->write(data, len, written)) {
62+
URCL_LOG_INFO("Sent program to robot:\n%s", program_with_newline.c_str());
63+
return true;
64+
}
65+
URCL_LOG_ERROR("Could not send program to robot");
66+
return false;
67+
}))
68+
{
69+
this->declare_parameter("robot_ip", rclcpp::PARAMETER_STRING);
70+
m_secondary_stream = std::make_unique<urcl::comm::URStream<urcl::primary_interface::PrimaryPackage>>(
71+
this->get_parameter("robot_ip").as_string(), urcl::primary_interface::UR_SECONDARY_PORT);
72+
m_secondary_stream->connect();
73+
74+
auto program_with_newline = std::string("textmsg(\"urscript_interface connected\")\n");
75+
size_t len = program_with_newline.size();
76+
const auto* data = reinterpret_cast<const uint8_t*>(program_with_newline.c_str());
77+
size_t written;
78+
m_secondary_stream->write(data, len, written);
79+
}
80+
81+
private:
82+
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr m_script_sub;
83+
std::unique_ptr<urcl::comm::URStream<urcl::primary_interface::PrimaryPackage>> m_secondary_stream;
84+
};
85+
86+
int main(int argc, char** argv)
87+
{
88+
rclcpp::init(argc, argv);
89+
rclcpp::spin(std::make_unique<URScriptInterface>());
90+
rclcpp::shutdown();
91+
return 0;
92+
}

0 commit comments

Comments
 (0)