Skip to content

Commit 423801a

Browse files
authored
Trajectory point velocities and example (#241)
This PR adds the possibility to pass acceleration&velocity information instead of time constraints to direct robot motions.
1 parent 6bc1553 commit 423801a

File tree

8 files changed

+325
-16
lines changed

8 files changed

+325
-16
lines changed

examples/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,8 @@ add_executable(script_sender_example
5959
script_sender.cpp)
6060
target_compile_options(script_sender_example PUBLIC ${CXX17_FLAG})
6161
target_link_libraries(script_sender_example ur_client_library::urcl)
62+
63+
add_executable(trajectory_point_interface_example
64+
trajectory_point_interface.cpp)
65+
target_compile_options(trajectory_point_interface_example PUBLIC ${CXX17_FLAG})
66+
target_link_libraries(trajectory_point_interface_example ur_client_library::urcl)
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2024 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+
#include <chrono>
32+
#include <string>
33+
#include <thread>
34+
35+
#include "ur_client_library/types.h"
36+
#include "ur_client_library/ur/ur_driver.h"
37+
#include "ur_client_library/log.h"
38+
#include "ur_client_library/control/trajectory_point_interface.h"
39+
#include "ur_client_library/ur/dashboard_client.h"
40+
41+
const std::string DEFAULT_ROBOT_IP = "192.168.56.101";
42+
const std::string SCRIPT_FILE = "resources/external_control.urscript";
43+
const std::string OUTPUT_RECIPE = "examples/resources/rtde_output_recipe.txt";
44+
const std::string INPUT_RECIPE = "examples/resources/rtde_input_recipe.txt";
45+
const std::string CALIBRATION_CHECKSUM = "calib_12788084448423163542";
46+
47+
std::unique_ptr<urcl::DashboardClient> g_my_dashboard;
48+
std::unique_ptr<urcl::UrDriver> g_my_driver;
49+
50+
std::atomic<bool> g_trajectory_done = false;
51+
52+
// We need a callback function to register. See UrDriver's parameters for details.
53+
void handleRobotProgramState(bool program_running)
54+
{
55+
// Print the text in green so we see it better
56+
std::cout << "\033[1;32mProgram running: " << std::boolalpha << program_running << "\033[0m\n" << std::endl;
57+
}
58+
59+
void trajDoneCallback(const urcl::control::TrajectoryResult& result)
60+
{
61+
URCL_LOG_INFO("Trajectory done with result %d", result);
62+
;
63+
g_trajectory_done = true;
64+
}
65+
66+
int main(int argc, char* argv[])
67+
{
68+
urcl::setLogLevel(urcl::LogLevel::INFO);
69+
// Parse the ip arguments if given
70+
std::string robot_ip = DEFAULT_ROBOT_IP;
71+
if (argc > 1)
72+
{
73+
robot_ip = std::string(argv[1]);
74+
}
75+
76+
// --------------- INITIALIZATION BEGIN -------------------
77+
// Making the robot ready for the program by:
78+
// Connect the robot Dashboard
79+
g_my_dashboard.reset(new urcl::DashboardClient(robot_ip));
80+
if (!g_my_dashboard->connect())
81+
{
82+
URCL_LOG_ERROR("Could not connect to dashboard");
83+
return 1;
84+
}
85+
86+
// // Stop program, if there is one running
87+
if (!g_my_dashboard->commandStop())
88+
{
89+
URCL_LOG_ERROR("Could not send stop program command");
90+
return 1;
91+
}
92+
93+
// Power it off
94+
if (!g_my_dashboard->commandPowerOff())
95+
{
96+
URCL_LOG_ERROR("Could not send Power off command");
97+
return 1;
98+
}
99+
100+
// Power it on
101+
if (!g_my_dashboard->commandPowerOn())
102+
{
103+
URCL_LOG_ERROR("Could not send Power on command");
104+
return 1;
105+
}
106+
107+
// Release the brakes
108+
if (!g_my_dashboard->commandBrakeRelease())
109+
{
110+
URCL_LOG_ERROR("Could not send BrakeRelease command");
111+
return 1;
112+
}
113+
114+
std::unique_ptr<urcl::ToolCommSetup> tool_comm_setup;
115+
const bool headless = true;
116+
g_my_driver.reset(new urcl::UrDriver(robot_ip, SCRIPT_FILE, OUTPUT_RECIPE, INPUT_RECIPE, &handleRobotProgramState,
117+
headless, std::move(tool_comm_setup), CALIBRATION_CHECKSUM));
118+
// --------------- INITIALIZATION END -------------------
119+
120+
g_my_driver->registerTrajectoryDoneCallback(&trajDoneCallback);
121+
122+
URCL_LOG_INFO("Running MoveJ motion");
123+
// --------------- MOVEJ TRAJECTORY -------------------
124+
{
125+
g_trajectory_done = false;
126+
// Trajectory definition
127+
std::vector<urcl::vector6d_t> points{ { -1.57, -1.57, 0, 0, 0, 0 }, { -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 } };
128+
std::vector<double> motion_durations{ 5.0, 2.0 };
129+
std::vector<double> blend_radii{ 0.1, 0.1 };
130+
131+
// Trajectory execution
132+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
133+
points.size() * 2);
134+
for (size_t i = 0; i < points.size(); i++)
135+
{
136+
g_my_driver->writeTrajectoryPoint(points[i], false, motion_durations[i], blend_radii[i]);
137+
}
138+
139+
// Same motion, but parametrized with acceleration and velocity
140+
motion_durations = { 0.0, 0.0 };
141+
for (size_t i = 0; i < points.size(); i++)
142+
{
143+
g_my_driver->writeTrajectoryPoint(points[i], 2.0, 2.0, false, motion_durations[i], blend_radii[i]);
144+
}
145+
146+
while (!g_trajectory_done)
147+
{
148+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
149+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
150+
}
151+
}
152+
// --------------- END MOVEJ TRAJECTORY -------------------
153+
154+
URCL_LOG_INFO("Running MoveL motion");
155+
// --------------- MOVEL TRAJECTORY -------------------
156+
{
157+
g_trajectory_done = false;
158+
// Trajectory definition
159+
std::vector<urcl::vector6d_t> points{ { -0.203, 0.263, 0.559, 0.68, -1.083, -2.076 },
160+
{ -0.203, 0.463, 0.559, 0.68, -1.083, -2.076 } };
161+
std::vector<double> motion_durations{ 5.0, 5.0 };
162+
std::vector<double> blend_radii{ 0.0, 0.0 };
163+
164+
// Trajectory execution
165+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
166+
points.size());
167+
for (size_t i = 0; i < points.size(); i++)
168+
{
169+
// setting the cartesian parameter makes it interpret the 6d vector as a pose and use movel
170+
g_my_driver->writeTrajectoryPoint(points[i], true, motion_durations[i], blend_radii[i]);
171+
}
172+
173+
while (!g_trajectory_done)
174+
{
175+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
176+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
177+
}
178+
}
179+
// --------------- END MOVEL TRAJECTORY -------------------
180+
181+
URCL_LOG_INFO("Running a spline motion");
182+
// --------------- SPLINE TRAJECTORY -------------------
183+
{
184+
g_trajectory_done = false;
185+
// Trajectory definition
186+
std::vector<urcl::vector6d_t> positions{ { -1.57, -1.57, 0, 0, 0, 0 },
187+
{ -1.57, -1.57, -1.57, 0, 0, 0 },
188+
{ -1.57, -1.57, 0, 0, 0, 0 },
189+
{ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 } };
190+
std::vector<urcl::vector6d_t> velocities{
191+
{ 0, 0, 0.0, 0, 0, 0 }, { 0, 0, 0.0, 0, 0, 0 }, { 0, 0, 1.5, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }
192+
};
193+
std::vector<double> motion_durations{ 3.0, 3.0, 3.0, 3.0 };
194+
195+
// Trajectory execution
196+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_START,
197+
positions.size());
198+
for (size_t i = 0; i < positions.size(); i++)
199+
{
200+
g_my_driver->writeTrajectorySplinePoint(positions[i], velocities[i], motion_durations[i]);
201+
}
202+
203+
while (!g_trajectory_done)
204+
{
205+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
206+
g_my_driver->writeTrajectoryControlMessage(urcl::control::TrajectoryControlMessage::TRAJECTORY_NOOP);
207+
}
208+
}
209+
// --------------- END MOVEJ TRAJECTORY -------------------
210+
211+
g_my_driver->stopControl();
212+
return 0;
213+
}

include/ur_client_library/control/trajectory_point_interface.h

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

32+
#include <optional>
33+
3234
#include "ur_client_library/control/reverse_interface.h"
3335
#include "ur_client_library/comm/control_mode.h"
3436
#include "ur_client_library/types.h"
@@ -44,6 +46,7 @@ namespace control
4446
enum class TrajectoryResult : int32_t
4547
{
4648

49+
TRAJECTORY_RESULT_UNKNOWN = -1, ///< No result received, yet
4750
TRAJECTORY_RESULT_SUCCESS = 0, ///< Successful execution
4851
TRAJECTORY_RESULT_CANCELED = 1, ///< Canceled by user
4952
TRAJECTORY_RESULT_FAILURE = 2 ///< Aborted due to error during execution
@@ -76,6 +79,7 @@ class TrajectoryPointInterface : public ReverseInterface
7679
{
7780
public:
7881
static const int32_t MULT_TIME = 1000;
82+
static const int MESSAGE_LENGTH = 21;
7983

8084
TrajectoryPointInterface() = delete;
8185
/*!
@@ -103,6 +107,23 @@ class TrajectoryPointInterface : public ReverseInterface
103107
bool writeTrajectoryPoint(const vector6d_t* positions, const float goal_time, const float blend_radius,
104108
const bool cartesian);
105109

110+
/*!
111+
* \brief Writes information for a robot motion to the robot to be read by the URScript program.
112+
*
113+
* \param positions A vector of joint or cartesian targets for the robot
114+
* \param acceleration Joint acceleration of leading axis [rad/s^2] / tool acceleration [m/s^2]
115+
* for Cartesian motions
116+
* \param velocity Joint speed of leading axis [rad/s] / tool speed [m/s] for Cartesian motions
117+
* \param goal_time The goal time to reach the target. When a non-zero goal time is specified,
118+
* this has priority over speed and acceleration settings.
119+
* \param blend_radius The radius to be used for blending between control points
120+
* \param cartesian True, if the written point is specified in Cartesian space, false if in joint space
121+
*
122+
* \returns True, if the write was performed successfully, false otherwise.
123+
*/
124+
bool writeTrajectoryPoint(const vector6d_t* positions, const float acceleration = 1.4, const float velocity = 1.05,
125+
const float goal_time = 0, const float blend_radius = 0, const bool cartesian = false);
126+
106127
/*!
107128
* \brief Writes needed information to the robot to be read by the URScript program including
108129
* velocity and acceleration information. Depending on the information given the robot will do quadratic (positions
@@ -131,7 +152,6 @@ class TrajectoryPointInterface : public ReverseInterface
131152
virtual void messageCallback(const int filedescriptor, char* buffer, int nbytesrecv) override;
132153

133154
private:
134-
static const int MESSAGE_LENGTH = 21;
135155
std::function<void(TrajectoryResult)> handle_trajectory_end_;
136156
};
137157

include/ur_client_library/ur/ur_driver.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,23 @@ class UrDriver
272272
bool writeTrajectoryPoint(const vector6d_t& positions, const bool cartesian, const float goal_time = 0.0,
273273
const float blend_radius = 0.052);
274274

275+
/*!
276+
* \brief Writes a trajectory point onto the dedicated socket.
277+
*
278+
* \param positions Desired joint or cartesian positions
279+
* \param cartesian True, if the point sent is cartesian, false if joint-based
280+
* \param acceleration Joint acceleration of leading axis [rad/s^2] / tool acceleration [m/s^2]
281+
* for Cartesian motions
282+
* \param velocity Joint speed of leading axis [rad/s] / tool speed [m/s] for Cartesian motions
283+
* \param goal_time Time for the robot to reach this point. When a non-zero goal time is specified,
284+
* this has priority over speed and acceleration settings.
285+
* \param blend_radius The radius to be used for blending between control points
286+
*
287+
* \returns True on successful write.
288+
*/
289+
bool writeTrajectoryPoint(const vector6d_t& positions, float acceleration, float velocity, const bool cartesian,
290+
const float goal_time = 0.0, const float blend_radius = 0.052);
291+
275292
/*!
276293
* \brief Writes a trajectory spline point for quintic spline interpolation onto the dedicated socket.
277294
*

resources/external_control.urscript

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,19 @@ thread trajectoryThread():
479479
end
480480
# MoveJ point
481481
if raw_point[INDEX_POINT_TYPE] == TRAJECTORY_POINT_JOINT:
482-
movej(q, t = tmptime, r = blend_radius)
482+
acceleration = raw_point[7] / MULT_jointstate
483+
velocity = raw_point[13] / MULT_jointstate
484+
movej(q, a = acceleration, v = velocity, t = tmptime, r = blend_radius)
483485

484486
# reset old acceleration
485487
spline_qdd = [0, 0, 0, 0, 0, 0]
486488
spline_qd = [0, 0, 0, 0, 0, 0]
487489

488490
# Movel point
489491
elif raw_point[INDEX_POINT_TYPE] == TRAJECTORY_POINT_CARTESIAN:
490-
movel(p[q[0], q[1], q[2], q[3], q[4], q[5]], t = tmptime, r = blend_radius)
492+
acceleration = raw_point[7] / MULT_jointstate
493+
velocity = raw_point[13] / MULT_jointstate
494+
movel(p[q[0], q[1], q[2], q[3], q[4], q[5]], a = acceleration, v = velocity, t = tmptime, r = blend_radius)
491495

492496
# reset old acceleration
493497
spline_qdd = [0, 0, 0, 0, 0, 0]

src/control/trajectory_point_interface.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ TrajectoryPointInterface::TrajectoryPointInterface(uint32_t port) : ReverseInter
3838
{
3939
}
4040

41-
bool TrajectoryPointInterface::writeTrajectoryPoint(const vector6d_t* positions, const float goal_time,
41+
bool TrajectoryPointInterface::writeTrajectoryPoint(const vector6d_t* positions, const float acceleration,
42+
const float velocity, const float goal_time,
4243
const float blend_radius, const bool cartesian)
4344
{
4445
if (client_fd_ == -1)
@@ -56,16 +57,24 @@ bool TrajectoryPointInterface::writeTrajectoryPoint(const vector6d_t* positions,
5657
val = htobe32(val);
5758
b_pos += append(b_pos, val);
5859
}
60+
for (size_t i = 0; i < positions->size(); ++i)
61+
{
62+
int32_t val = static_cast<int32_t>(round(velocity * MULT_JOINTSTATE));
63+
val = htobe32(val);
64+
b_pos += append(b_pos, val);
65+
}
66+
for (size_t i = 0; i < positions->size(); ++i)
67+
{
68+
int32_t val = static_cast<int32_t>(round(acceleration * MULT_JOINTSTATE));
69+
val = htobe32(val);
70+
b_pos += append(b_pos, val);
71+
}
5972
}
6073
else
6174
{
6275
b_pos += 6 * sizeof(int32_t);
6376
}
6477

65-
// Fill in velocity and acceleration, not used for this point type
66-
b_pos += 6 * sizeof(int32_t);
67-
b_pos += 6 * sizeof(int32_t);
68-
6978
int32_t val = static_cast<int32_t>(round(goal_time * MULT_TIME));
7079
val = htobe32(val);
7180
b_pos += append(b_pos, val);
@@ -91,6 +100,12 @@ bool TrajectoryPointInterface::writeTrajectoryPoint(const vector6d_t* positions,
91100
return server_.write(client_fd_, buffer, sizeof(buffer), written);
92101
}
93102

103+
bool TrajectoryPointInterface::writeTrajectoryPoint(const vector6d_t* positions, const float goal_time,
104+
const float blend_radius, const bool cartesian)
105+
{
106+
return writeTrajectoryPoint(positions, 1.4, 1.05, goal_time, blend_radius, cartesian);
107+
}
108+
94109
bool TrajectoryPointInterface::writeTrajectorySplinePoint(const vector6d_t* positions, const vector6d_t* velocities,
95110
const vector6d_t* accelerations, const float goal_time)
96111
{

src/ur/ur_driver.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,13 @@ bool UrDriver::writeJointCommand(const vector6d_t& values, const comm::ControlMo
232232
return reverse_interface_->write(&values, control_mode, robot_receive_timeout);
233233
}
234234

235+
bool UrDriver::writeTrajectoryPoint(const vector6d_t& positions, const float acceleration, const float velocity,
236+
const bool cartesian, const float goal_time, const float blend_radius)
237+
{
238+
return trajectory_interface_->writeTrajectoryPoint(&positions, acceleration, velocity, goal_time, blend_radius,
239+
cartesian);
240+
}
241+
235242
bool UrDriver::writeTrajectoryPoint(const vector6d_t& positions, const bool cartesian, const float goal_time,
236243
const float blend_radius)
237244
{

0 commit comments

Comments
 (0)