Skip to content

Commit be86c25

Browse files
committed
Add example documentation for tool contact
1 parent 60c10c9 commit be86c25

File tree

3 files changed

+107
-52
lines changed

3 files changed

+107
-52
lines changed

doc/examples.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ may be running forever until manually stopped.
2222
examples/primary_pipeline_calibration
2323
examples/rtde_client
2424
examples/script_sender
25+
examples/tool_contact_example
2526
examples/ur_driver
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
:github_url: https://github.com/UniversalRobots/Universal_Robots_Client_Library/blob/master/doc/examples/tool_contact_example.rst
2+
3+
.. _tool_contact_example:
4+
5+
Tool Contact example
6+
====================
7+
8+
Univeral Robots' **tool contact mode** allows detecting collisions between the robot's tool and the
9+
environment and interrupting motions when that happens. This example demonstrates how to use the
10+
tool contact mode to detect collisions and stop the robot.
11+
12+
As a basic concept, we will move the robot linearly in the negative z axis until the tool hits
13+
something or the program's timeout is hit.
14+
15+
At first, we create a ``UrDriver`` object as usual:
16+
17+
.. literalinclude:: ../../examples/tool_contact_example.cpp
18+
:language: c++
19+
:caption: examples/tool_contact_example.cpp
20+
:linenos:
21+
:lineno-match:
22+
:start-at: std::unique_ptr<ToolCommSetup> tool_comm_setup;
23+
:end-before: g_my_driver->registerToolContactResultCallback
24+
25+
We use a small helper function to make sure that the reverse interface is active and connected
26+
before proceeding.
27+
28+
When a tool contact is detected, a callback will be triggered. For that we define a function to
29+
handle this event:
30+
31+
.. literalinclude:: ../../examples/tool_contact_example.cpp
32+
:language: c++
33+
:caption: examples/tool_contact_example.cpp
34+
:linenos:
35+
:lineno-match:
36+
:start-at: void handleToolContactResult(control::ToolContactResult result)
37+
:end-at: }
38+
39+
40+
41+
This function is registered as a callback to the driver and then tool_contact mode is enabled:
42+
43+
.. literalinclude:: ../../examples/tool_contact_example.cpp
44+
:language: c++
45+
:caption: examples/tool_contact_example.cpp
46+
:linenos:
47+
:lineno-match:
48+
:start-at: g_my_driver->registerToolContactResultCallback(&handleToolContactResult);
49+
:end-at: g_my_driver->startToolContact()
50+
51+
Once everything is initialized, we can start a control loop commanding the robot to move in the
52+
negative z direction until the program timeout is hit or a tool contact is detected:
53+
54+
.. literalinclude:: ../../examples/tool_contact_example.cpp
55+
:language: c++
56+
:caption: examples/tool_contact_example.cpp
57+
:linenos:
58+
:lineno-match:
59+
:start-at: // This will move the robot
60+
:end-at: return 0;

examples/tool_contact_example.cpp

Lines changed: 46 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,26 @@ const std::string DEFAULT_ROBOT_IP = "192.168.56.101";
4646
const std::string SCRIPT_FILE = "resources/external_control.urscript";
4747
const std::string OUTPUT_RECIPE = "examples/resources/rtde_output_recipe.txt";
4848
const std::string INPUT_RECIPE = "examples/resources/rtde_input_recipe.txt";
49-
const std::string CALIBRATION_CHECKSUM = "calib_12788084448423163542";
5049

5150
std::unique_ptr<UrDriver> g_my_driver;
5251
std::unique_ptr<DashboardClient> g_my_dashboard;
53-
bool g_tool_contact_result_triggered;
52+
std::atomic<bool> g_tool_contact_result_triggered;
5453
control::ToolContactResult g_tool_contact_result;
54+
std::atomic<bool> g_program_running;
55+
std::condition_variable g_program_running_cv;
56+
std::mutex g_program_running_mutex;
5557

5658
// We need a callback function to register. See UrDriver's parameters for details.
5759
void handleRobotProgramState(bool program_running)
5860
{
5961
// Print the text in green so we see it better
6062
std::cout << "\033[1;32mProgram running: " << std::boolalpha << program_running << "\033[0m\n" << std::endl;
63+
g_program_running = program_running;
64+
if (program_running)
65+
{
66+
std::lock_guard<std::mutex> lk(g_program_running_mutex);
67+
g_program_running_cv.notify_one();
68+
}
6169
}
6270

6371
void handleToolContactResult(control::ToolContactResult result)
@@ -68,6 +76,17 @@ void handleToolContactResult(control::ToolContactResult result)
6876
g_tool_contact_result_triggered = true;
6977
}
7078

79+
bool waitForProgramRunning(int milliseconds = 100)
80+
{
81+
std::unique_lock<std::mutex> lk(g_program_running_mutex);
82+
if (g_program_running_cv.wait_for(lk, std::chrono::milliseconds(milliseconds)) == std::cv_status::no_timeout ||
83+
g_program_running == true)
84+
{
85+
return true;
86+
}
87+
return false;
88+
}
89+
7190
int main(int argc, char* argv[])
7291
{
7392
urcl::setLogLevel(urcl::LogLevel::INFO);
@@ -121,68 +140,43 @@ int main(int argc, char* argv[])
121140
URCL_LOG_ERROR("Could not send BrakeRelease command");
122141
return 1;
123142
}
124-
125143
// Now the robot is ready to receive a program
144+
126145
std::unique_ptr<ToolCommSetup> tool_comm_setup;
127146
const bool headless = true;
128147
g_my_driver.reset(new UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, headless,
129-
std::move(tool_comm_setup), CALIBRATION_CHECKSUM));
130-
148+
std::move(tool_comm_setup)));
149+
if (!waitForProgramRunning(1000))
150+
{
151+
std::cout << "Program did not start running. Is the robot in remote control?" << std::endl;
152+
return 1;
153+
}
131154
g_my_driver->registerToolContactResultCallback(&handleToolContactResult);
132-
133-
// Once RTDE communication is started, we have to make sure to read from the interface buffer, as
134-
// otherwise we will get pipeline overflows. Therefor, do this directly before starting your main
135-
// loop.
136-
g_my_driver->startRTDECommunication();
155+
g_my_driver->startToolContact();
137156

138157
// This will move the robot downward in the z direction of the base until a tool contact is detected or seconds_to_run
139158
// is reached
140-
std::chrono::duration<double> time_done(0);
141-
std::chrono::duration<double> timeout(second_to_run);
142-
vector6d_t tcp_speed = { 0.0, 0.0, -0.02, 0.0, 0.0, 0.0 };
143-
auto stopwatch_last = std::chrono::steady_clock::now();
144-
auto stopwatch_now = stopwatch_last;
145-
g_my_driver->startToolContact();
146-
147-
while (true)
159+
const vector6d_t tcp_speed = { 0.0, 0.0, -0.02, 0.0, 0.0, 0.0 };
160+
auto start_time = std::chrono::system_clock::now();
161+
while (second_to_run.count() < 0 || (std::chrono::system_clock::now() - start_time) < second_to_run)
148162
{
149-
// Read latest RTDE package. This will block for a hard-coded timeout (see UrDriver), so the
150-
// robot will effectively be in charge of setting the frequency of this loop.
151-
// In a real-world application this thread should be scheduled with real-time priority in order
152-
// to ensure that this is called in time.
153-
std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_driver->getDataPackage();
154-
if (data_pkg)
163+
// Setting the RobotReceiveTimeout time is for example purposes only. This will make the example running more
164+
// reliable on non-realtime systems. Use with caution in productive applications.
165+
bool ret =
166+
g_my_driver->writeJointCommand(tcp_speed, comm::ControlMode::MODE_SPEEDL, RobotReceiveTimeout::millisec(100));
167+
if (!ret)
155168
{
156-
// Setting the RobotReceiveTimeout time is for example purposes only. This will make the example running more
157-
// reliable on non-realtime systems. Use with caution in productive applications.
158-
bool ret =
159-
g_my_driver->writeJointCommand(tcp_speed, comm::ControlMode::MODE_SPEEDL, RobotReceiveTimeout::millisec(100));
160-
if (!ret)
161-
{
162-
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?");
163-
return 1;
164-
}
165-
166-
if (g_tool_contact_result_triggered)
167-
{
168-
URCL_LOG_INFO("Tool contact result triggered. Received tool contact result %i.",
169-
toUnderlying(g_tool_contact_result));
170-
break;
171-
}
172-
173-
if (time_done > timeout && second_to_run.count() != 0)
174-
{
175-
URCL_LOG_INFO("Timed out before reaching tool contact.");
176-
break;
177-
}
169+
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?");
170+
return 1;
178171
}
179-
else
172+
173+
if (g_tool_contact_result_triggered)
180174
{
181-
URCL_LOG_WARN("Could not get fresh data package from robot");
175+
URCL_LOG_INFO("Tool contact detected");
176+
break;
182177
}
183-
184-
stopwatch_now = std::chrono::steady_clock::now();
185-
time_done += stopwatch_now - stopwatch_last;
186-
stopwatch_last = stopwatch_now;
187178
}
179+
URCL_LOG_INFO("Timed out before reaching tool contact.");
180+
g_my_driver->stopControl();
181+
return 0;
188182
}

0 commit comments

Comments
 (0)