Skip to content

Commit c3bead7

Browse files
authored
Added watchdog configuration for the reverse socket (#178)
This enables the possibility to configure the read timeout for the reverse socket running in the external control script Separated control modes into realtime and non realtime to have different possible configurations for the watchdog Added test to verify the changes Updated test and examples to coincide with the new changes
1 parent 868120b commit c3bead7

18 files changed

+1076
-151
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ add_library(urcl SHARED
4343
src/ur/calibration_checker.cpp
4444
src/ur/dashboard_client.cpp
4545
src/ur/tool_communication.cpp
46+
src/ur/robot_receive_timeout.cpp
4647
src/ur/version_information.cpp
4748
src/rtde/rtde_writer.cpp
4849
src/default_log_handler.cpp

examples/full_driver.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,6 @@ int main(int argc, char* argv[])
110110
const bool HEADLESS = true;
111111
g_my_driver.reset(new UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, HEADLESS,
112112
std::move(tool_comm_setup), CALIBRATION_CHECKSUM));
113-
g_my_driver->setKeepaliveCount(5); // This is for example purposes only. This will make the example running more
114-
// reliable on non-realtime systems. Do not use this in productive applications.
115113

116114
// Once RTDE communication is started, we have to make sure to read from the interface buffer, as
117115
// otherwise we will get pipeline overflows. Therefor, do this directly before starting your main
@@ -153,7 +151,10 @@ int main(int argc, char* argv[])
153151
increment = 0.02;
154152
}
155153
g_joint_positions[5] += increment;
156-
bool ret = g_my_driver->writeJointCommand(g_joint_positions, comm::ControlMode::MODE_SERVOJ);
154+
// Setting the RobotReceiveTimeout time is for example purposes only. This will make the example running more
155+
// reliable on non-realtime systems. Use with caution in productive applications.
156+
bool ret = g_my_driver->writeJointCommand(g_joint_positions, comm::ControlMode::MODE_SERVOJ,
157+
RobotReceiveTimeout::millisec(100));
157158
if (!ret)
158159
{
159160
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?");
@@ -166,6 +167,7 @@ int main(int argc, char* argv[])
166167
URCL_LOG_WARN("Could not get fresh data package from robot");
167168
}
168169
}
170+
g_my_driver->stopControl();
169171
URCL_LOG_INFO("Movement done");
170172
return 0;
171173
}

examples/spline_example.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,6 @@ int main(int argc, char* argv[])
177177
const bool HEADLESS = true;
178178
g_my_driver.reset(new UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, HEADLESS,
179179
std::move(tool_comm_setup), CALIBRATION_CHECKSUM));
180-
g_my_driver->setKeepaliveCount(5); // This is for example purposes only. This will make the example running more
181-
// reliable on non-realtime systems. Do not use this in productive applications.
182180

183181
g_my_driver->registerTrajectoryDoneCallback(&handleTrajectoryState);
184182

@@ -211,8 +209,7 @@ int main(int argc, char* argv[])
211209
4.00000000e+00 };
212210

213211
bool ret = false;
214-
URCL_LOG_INFO("Switch to Forward mode");
215-
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD);
212+
ret = g_my_driver->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_NOOP);
216213
if (!ret)
217214
{
218215
std::stringstream lastq;
@@ -232,11 +229,11 @@ int main(int argc, char* argv[])
232229
p_i[joint_to_control] = s_pos[i];
233230
p.push_back(p_i);
234231

235-
vector6d_t v_i;
232+
vector6d_t v_i = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
236233
v_i[joint_to_control] = s_vel[i];
237234
v.push_back(v_i);
238235

239-
vector6d_t a_i;
236+
vector6d_t a_i = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
240237
a_i[joint_to_control] = s_acc[i];
241238
a.push_back(a_i);
242239

@@ -259,7 +256,7 @@ int main(int argc, char* argv[])
259256
std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!";
260257
throw std::runtime_error(error_msg);
261258
}
262-
bool ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD);
259+
ret = g_my_driver->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_NOOP);
263260

264261
if (!ret)
265262
{
@@ -276,7 +273,6 @@ int main(int argc, char* argv[])
276273

277274
// QUINTIC
278275
SendTrajectory(p, v, a, time, true);
279-
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD);
280276

281277
g_trajectory_running = true;
282278
while (g_trajectory_running)
@@ -291,7 +287,7 @@ int main(int argc, char* argv[])
291287
std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!";
292288
throw std::runtime_error(error_msg);
293289
}
294-
bool ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD);
290+
ret = g_my_driver->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_NOOP);
295291

296292
if (!ret)
297293
{
@@ -306,7 +302,7 @@ int main(int argc, char* argv[])
306302

307303
URCL_LOG_INFO("QUINTIC Movement done");
308304

309-
ret = g_my_driver->writeJointCommand(vector6d_t(), comm::ControlMode::MODE_FORWARD);
305+
ret = g_my_driver->writeTrajectoryControlMessage(control::TrajectoryControlMessage::TRAJECTORY_NOOP);
310306
if (!ret)
311307
{
312308
std::stringstream lastq;
@@ -315,5 +311,6 @@ int main(int argc, char* argv[])
315311
lastq.str().c_str());
316312
return 1;
317313
}
314+
g_my_driver->stopControl();
318315
return 0;
319316
}

examples/tool_contact_example.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,6 @@ int main(int argc, char* argv[])
127127
const bool HEADLESS = true;
128128
g_my_driver.reset(new UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState, HEADLESS,
129129
std::move(tool_comm_setup), CALIBRATION_CHECKSUM));
130-
g_my_driver->setKeepaliveCount(5); // This is for example purposes only. This will make the example running more
131-
// reliable on non-realtime systems. Do not use this in productive applications.
132130

133131
g_my_driver->registerToolContactResultCallback(&handleToolContactResult);
134132

@@ -155,7 +153,10 @@ int main(int argc, char* argv[])
155153
std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_driver->getDataPackage();
156154
if (data_pkg)
157155
{
158-
bool ret = g_my_driver->writeJointCommand(tcp_speed, comm::ControlMode::MODE_SPEEDL);
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));
159160
if (!ret)
160161
{
161162
URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?");

include/ur_client_library/comm/control_mode.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#ifndef UR_CLIENT_LIBRARY_CONTROL_MODE_H_INCLUDED
3030
#define UR_CLIENT_LIBRARY_CONTROL_MODE_H_INCLUDED
3131

32+
#include <algorithm>
33+
#include <vector>
34+
3235
namespace urcl
3336
{
3437
namespace comm
@@ -48,8 +51,59 @@ enum class ControlMode : int32_t
4851
MODE_POSE = 5, ///< Set when cartesian pose control is active.
4952
MODE_FREEDRIVE = 6, ///< Set when freedrive mode is active.
5053
MODE_TOOL_IN_CONTACT =
51-
7 ///< Used only internally in the script, when robot is in tool contact, clear by endToolContact()
54+
7, ///< Used only internally in the script, when robot is in tool contact, clear by endToolContact()
55+
END ///< This is not an actual control mode, but used internally to get the number of control modes
5256
};
57+
58+
/*!
59+
* \brief Class used to separate the control modes into realtime and non realtime.
60+
*/
61+
class ControlModeTypes
62+
{
63+
public:
64+
// Control modes that require realtime communication
65+
static const inline std::vector<ControlMode> REALTIME_CONTROL_MODES = {
66+
ControlMode::MODE_SERVOJ, ControlMode::MODE_SPEEDJ, ControlMode::MODE_SPEEDL, ControlMode::MODE_POSE
67+
};
68+
69+
// Control modes that doesn't require realtime communication
70+
static const inline std::vector<ControlMode> NON_REALTIME_CONTROL_MODES = { ControlMode::MODE_IDLE,
71+
ControlMode::MODE_FORWARD,
72+
ControlMode::MODE_FREEDRIVE };
73+
74+
// Control modes which doesn't move the robot, meaning they are neither realtime or non realtime
75+
static const inline std::vector<ControlMode> STATIONARY_CONTROL_MODES = { ControlMode::MODE_STOPPED,
76+
ControlMode::MODE_UNINITIALIZED,
77+
ControlMode::MODE_TOOL_IN_CONTACT };
78+
79+
/*!
80+
* \brief Check if the control mode is realtime
81+
*
82+
* \param control_mode Current control mode
83+
*
84+
* \returns true if the control mode is realtime, false otherwise
85+
*/
86+
static bool is_control_mode_realtime(ControlMode control_mode)
87+
{
88+
return (std::find(ControlModeTypes::REALTIME_CONTROL_MODES.begin(), ControlModeTypes::REALTIME_CONTROL_MODES.end(),
89+
control_mode) != ControlModeTypes::REALTIME_CONTROL_MODES.end());
90+
}
91+
92+
/*!
93+
* \brief Check if the control mode is non realtime
94+
*
95+
* \param control_mode Current control mode
96+
*
97+
* \returns true if the control mode is non realtime, false otherwise
98+
*/
99+
static bool is_control_mode_non_realtime(ControlMode control_mode)
100+
{
101+
return (std::find(ControlModeTypes::NON_REALTIME_CONTROL_MODES.begin(),
102+
ControlModeTypes::NON_REALTIME_CONTROL_MODES.end(),
103+
control_mode) != ControlModeTypes::NON_REALTIME_CONTROL_MODES.end());
104+
}
105+
};
106+
53107
} // namespace comm
54108
} // namespace urcl
55109

include/ur_client_library/control/reverse_interface.h

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "ur_client_library/comm/control_mode.h"
3434
#include "ur_client_library/types.h"
3535
#include "ur_client_library/log.h"
36+
#include "ur_client_library/ur/robot_receive_timeout.h"
3637
#include <cstring>
3738
#include <endian.h>
3839
#include <condition_variable>
@@ -76,8 +77,10 @@ class ReverseInterface
7677
*
7778
* \param port Port the Server is started on
7879
* \param handle_program_state Function handle to a callback on program state changes.
80+
* \param step_time The robots step time
7981
*/
80-
ReverseInterface(uint32_t port, std::function<void(bool)> handle_program_state);
82+
ReverseInterface(uint32_t port, std::function<void(bool)> handle_program_state,
83+
std::chrono::milliseconds step_time = std::chrono::milliseconds(8));
8184

8285
/*!
8386
* \brief Disconnects possible clients so the reverse interface object can be safely destroyed.
@@ -90,39 +93,53 @@ class ReverseInterface
9093
* \param positions A vector of joint targets for the robot
9194
* \param control_mode Control mode assigned to this command. See documentation of comm::ControlMode
9295
* for details on possible values.
96+
* \param robot_receive_timeout The read timeout configuration for the reverse socket running in the external
97+
* control script on the robot. Use with caution when dealing with realtime commands as the robot
98+
* expects to get a new control signal each control cycle. Note the timeout cannot be higher than 1 second for
99+
* realtime commands.
93100
*
94101
* \returns True, if the write was performed successfully, false otherwise.
95102
*/
96-
virtual bool write(const vector6d_t* positions, const comm::ControlMode control_mode = comm::ControlMode::MODE_IDLE);
103+
virtual bool write(const vector6d_t* positions, const comm::ControlMode control_mode = comm::ControlMode::MODE_IDLE,
104+
const RobotReceiveTimeout& robot_receive_timeout = RobotReceiveTimeout::millisec(20));
97105

98106
/*!
99107
* \brief Writes needed information to the robot to be read by the URScript program.
100108
*
101109
* \param trajectory_action 1 if a trajectory is to be started, -1 if it should be stopped
102110
* \param point_number The number of points of the trajectory to be executed
111+
* \param robot_receive_timeout The read timeout configuration for the reverse socket running in the external
112+
* control script on the robot. If you want to make the read function blocking then use RobotReceiveTimeout::off()
113+
* function to create the RobotReceiveTimeout object
103114
*
104115
* \returns True, if the write was performed successfully, false otherwise.
105116
*/
106-
bool writeTrajectoryControlMessage(const TrajectoryControlMessage trajectory_action, const int point_number = 0);
117+
bool
118+
writeTrajectoryControlMessage(const TrajectoryControlMessage trajectory_action, const int point_number = 0,
119+
const RobotReceiveTimeout& robot_receive_timeout = RobotReceiveTimeout::millisec(200));
107120

108121
/*!
109122
* \brief Writes needed information to the robot to be read by the URScript program.
110123
*
111124
* \param freedrive_action 1 if freedrive mode is to be started, -1 if it should be stopped and 0 to keep it running
125+
* \param robot_receive_timeout The read timeout configuration for the reverse socket running in the external
126+
* control script on the robot. If you want to make the read function blocking then use RobotReceiveTimeout::off()
127+
* function to create the RobotReceiveTimeout object
112128
*
113129
* \returns True, if the write was performed successfully, false otherwise.
114130
*/
115-
bool writeFreedriveControlMessage(const FreedriveControlMessage freedrive_action);
131+
bool
132+
writeFreedriveControlMessage(const FreedriveControlMessage freedrive_action,
133+
const RobotReceiveTimeout& robot_receive_timeout = RobotReceiveTimeout::millisec(200));
116134

117135
/*!
118136
* \brief Set the Keepalive count. This will set the number of allowed timeout reads on the robot.
119137
*
120138
* \param count Number of allowed timeout reads on the robot.
121139
*/
122-
virtual void setKeepaliveCount(const uint32_t& count)
123-
{
124-
keepalive_count_ = count;
125-
}
140+
[[deprecated("Set keepaliveCount is deprecated, instead use the robot receive timeout directly in the write "
141+
"commands.")]] virtual void
142+
setKeepaliveCount(const uint32_t count);
126143

127144
protected:
128145
virtual void connectionCallback(const int filedescriptor);
@@ -145,7 +162,10 @@ class ReverseInterface
145162
static const int MAX_MESSAGE_LENGTH = 8;
146163

147164
std::function<void(bool)> handle_program_state_;
165+
std::chrono::milliseconds step_time_;
166+
148167
uint32_t keepalive_count_;
168+
bool keep_alive_count_modified_deprecated_;
149169
};
150170

151171
} // namespace control
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2023 Universal Robots A/S
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
//
10+
// * Redistributions in binary form must reproduce the above copyright
11+
// notice, this list of conditions and the following disclaimer in the
12+
// documentation and/or other materials provided with the distribution.
13+
//
14+
// * Neither the name of the {copyright_holder} nor the names of its
15+
// contributors may be used to endorse or promote products derived from
16+
// this software without specific prior written permission.
17+
//
18+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
// POSSIBILITY OF SUCH DAMAGE.
29+
// -- END LICENSE BLOCK ------------------------------------------------
30+
31+
#pragma once
32+
33+
#include "ur_client_library/comm/control_mode.h"
34+
#include <chrono>
35+
36+
namespace urcl
37+
{
38+
/*!
39+
* \brief RobotReceiveTimeout class containing a timeout configuration
40+
*
41+
* This robot receive timeout is used to configure the read timeout for the reverse socket running in the external
42+
* control script. The read timeout is the number of milliseconds until the read action times out. A timeout of 0 or
43+
* negative number indicates that the function should not return until a read is completed, this will make the read
44+
* function on the robot blocking. This can be set using the function off().
45+
*
46+
*/
47+
class RobotReceiveTimeout
48+
{
49+
public:
50+
static constexpr std::chrono::milliseconds MAX_RT_RECEIVE_TIMEOUT_MS = std::chrono::milliseconds(200);
51+
RobotReceiveTimeout() = delete;
52+
~RobotReceiveTimeout() = default;
53+
54+
/*!
55+
* \brief Create a RobotReceiveTimeout object with a specific timeout given in milliseconds
56+
*
57+
* \param milliseconds robot receive timeout
58+
*
59+
* \returns RobotReceiveTimeout object
60+
*/
61+
static RobotReceiveTimeout millisec(const unsigned int milliseconds = 20);
62+
63+
/*!
64+
* \brief Create a RobotReceiveTimeout object with a specific timeout given in seconds
65+
*
66+
* \param seconds robot receive timeout
67+
*
68+
* \returns RobotReceiveTimeout object
69+
*/
70+
static RobotReceiveTimeout sec(const float seconds = 0.02);
71+
72+
/*!
73+
* \brief Creates a RobotReceiveTimeout object with no timeout, this will make the read function on the robot blocking
74+
*
75+
* \returns RobotReceiveTimeout object
76+
*/
77+
static RobotReceiveTimeout off();
78+
79+
/*!
80+
* \brief Helper function to verify that the robot receive timeout is configured appropriately given the current
81+
* control mode
82+
*
83+
* \param control_mode current control mode
84+
* \param step_time The robots step time
85+
*
86+
* \returns receive timeout in milliseconds
87+
*/
88+
int verifyRobotReceiveTimeout(const comm::ControlMode control_mode, const std::chrono::milliseconds step_time) const;
89+
90+
std::chrono::milliseconds timeout_;
91+
92+
private:
93+
RobotReceiveTimeout(std::chrono::milliseconds timeout);
94+
};
95+
96+
} // namespace urcl

0 commit comments

Comments
 (0)