Skip to content

Commit 289c747

Browse files
authored
Support optimove motions in InstructionExecutor (#354)
Add support for optimovej and optimovel in the InstructionExecutor.
1 parent b4c6c8f commit 289c747

File tree

13 files changed

+374
-9
lines changed

13 files changed

+374
-9
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010

1111
jobs:
1212
build:
13-
timeout-minutes: 30
13+
timeout-minutes: 60
1414
runs-on: ubuntu-latest
1515
name: build (${{matrix.env.URSIM_VERSION}}-${{matrix.env.ROBOT_MODEL}})
1616
strategy:

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ endif()
1717
add_library(urcl
1818
src/comm/tcp_socket.cpp
1919
src/comm/tcp_server.cpp
20+
src/control/motion_primitives.cpp
2021
src/control/reverse_interface.cpp
2122
src/control/script_reader.cpp
2223
src/control/script_sender.cpp

doc/architecture/instruction_executor.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ to point motions easily accessible. Currently, it supports the following instruc
1212
* Execute MoveL point to point motions
1313
* Execute MoveP point to point motions
1414
* Execute MoveC circular motions
15+
* Execute OptimoveJ point to point motions (For PolyScope 5.21 / PolyScope 10.8 and later)
16+
* Execute OptimoveL point to point motions (For PolyScope 5.21 / PolyScope 10.8 and later)
1517
* Execute sequences consisting of the motion primitives above
1618

1719
The Instruction Executor uses the :ref:`trajectory_point_interface` and the

doc/examples/instruction_executor.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ To run a sequence of motions, create an
4444
:end-at: instruction_executor->executeMotion(motion_sequence);
4545

4646
Each element in the motion sequence can be a different motion type. In the example, there are two
47-
``MoveJ`` motions, a ``MoveL`` motion and a ``MoveP`` motion. The primitives' parameters are directly forwarded to
48-
the underlying script functions, so the parameter descriptions for them apply, as well.
49-
Particularly, you may want to choose between either a time-based execution speed or an acceleration
50-
/ velocity parametrization for some move functions. The latter will be ignored if a time > 0 is given.
47+
``MoveJ`` motions, a ``MoveL`` motion, a ``MoveP`` motion, a ``OptimiveJ`` motion and a
48+
``OptimoveL`` motion. The primitives' parameters are directly forwarded to the underlying script
49+
functions, so the parameter descriptions for them apply, as well. Particularly, you may want to
50+
choose between either a time-based execution or an acceleration / velocity parametrization
51+
for some move functions. The latter will be ignored if a time > 0 is given.
5152

5253
Please refer to the script manual for details.
5354

examples/instruction_executor.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ int main(int argc, char* argv[])
9191
std::chrono::seconds(2)),
9292
std::make_shared<urcl::control::MovePPrimitive>(urcl::Pose{ -0.203, 0.463, 0.559, 0.68, -1.083, -2.076 }, 0.1, 0.4,
9393
0.4),
94+
std::make_shared<urcl::control::OptimoveJPrimitive>(urcl::vector6d_t{ -1.57, -1.57, 1.6, -0.5, 0.4, 0.3 }, 0.1, 0.4,
95+
0.7),
96+
std::make_shared<urcl::control::OptimoveLPrimitive>(urcl::Pose(-0.203, 0.263, 0.559, 0.68, -1.083, -2.076), 0.1,
97+
0.4, 0.7),
9498
};
9599
instruction_executor->executeMotion(motion_sequence);
96100

@@ -109,4 +113,4 @@ int main(int argc, char* argv[])
109113

110114
g_my_robot->getUrDriver()->stopControl();
111115
return 0;
112-
}
116+
}

include/ur_client_library/control/motion_primitives.h

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ enum class MotionType : uint8_t
4646
MOVEL = 1,
4747
MOVEP = 2,
4848
MOVEC = 3,
49+
OPTIMOVEJ = 4,
50+
OPTIMOVEL = 5,
4951
SPLINE = 51,
5052
UNKNOWN = 255
5153
};
@@ -61,11 +63,14 @@ enum class TrajectorySplineType : int32_t
6163

6264
struct MotionPrimitive
6365
{
66+
virtual ~MotionPrimitive() = default;
6467
MotionType type = MotionType::UNKNOWN;
6568
std::chrono::duration<double> duration;
6669
double acceleration;
6770
double velocity;
68-
double blend_radius;
71+
double blend_radius = 0.0;
72+
73+
virtual bool validate() const;
6974
};
7075

7176
struct MoveJPrimitive : public MotionPrimitive
@@ -113,6 +118,7 @@ struct MovePPrimitive : public MotionPrimitive
113118
this->velocity = velocity;
114119
this->blend_radius = blend_radius;
115120
}
121+
116122
urcl::Pose target_pose;
117123
};
118124

@@ -129,6 +135,7 @@ struct MoveCPrimitive : public MotionPrimitive
129135
this->blend_radius = blend_radius;
130136
this->mode = mode;
131137
}
138+
132139
urcl::Pose via_point_pose;
133140
urcl::Pose target_pose;
134141
int32_t mode = 0;
@@ -158,10 +165,47 @@ struct SplinePrimitive : public MotionPrimitive
158165
return control::TrajectorySplineType::SPLINE_CUBIC;
159166
}
160167
}
168+
169+
bool validate() const override;
170+
161171
vector6d_t target_positions;
162172
vector6d_t target_velocities;
163173
std::optional<vector6d_t> target_accelerations;
164174
};
175+
176+
struct OptimoveJPrimitive : public MotionPrimitive
177+
{
178+
OptimoveJPrimitive(const urcl::vector6d_t& target, const double blend_radius = 0,
179+
const double acceleration_fraction = 0.3, const double velocity_fraction = 0.3)
180+
{
181+
type = MotionType::OPTIMOVEJ;
182+
target_joint_configuration = target;
183+
this->blend_radius = blend_radius;
184+
this->acceleration = acceleration_fraction;
185+
this->velocity = velocity_fraction;
186+
}
187+
188+
bool validate() const override;
189+
190+
urcl::vector6d_t target_joint_configuration;
191+
};
192+
193+
struct OptimoveLPrimitive : public MotionPrimitive
194+
{
195+
OptimoveLPrimitive(const urcl::Pose& target, const double blend_radius = 0, const double acceleration_fraction = 0.3,
196+
const double velocity_fraction = 0.3)
197+
{
198+
type = MotionType::OPTIMOVEL;
199+
target_pose = target;
200+
this->blend_radius = blend_radius;
201+
this->acceleration = acceleration_fraction;
202+
this->velocity = velocity_fraction;
203+
}
204+
205+
bool validate() const override;
206+
207+
urcl::Pose target_pose;
208+
};
165209
} // namespace control
166210
} // namespace urcl
167-
#endif // UR_CLIENT_LIBRARY_MOTION_PRIMITIVES_H_INCLUDED
211+
#endif // UR_CLIENT_LIBRARY_MOTION_PRIMITIVES_H_INCLUDED

include/ur_client_library/ur/instruction_executor.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,42 @@ class InstructionExecutor
132132
bool moveC(const urcl::Pose& via, const urcl::Pose& target, const double acceleration = 1.4,
133133
const double velocity = 1.04, const double blend_radius = 0.0, const int32_t mode = 0);
134134

135+
/**
136+
* \brief Move the robot to a joint target using optimoveJ.
137+
*
138+
* This function will move the robot to the given joint target using the optimoveJ motion
139+
* primitive. The robot will move with the given acceleration and velocity fractions.
140+
*
141+
* \param target The joint target to move to.
142+
* \param acceleration_fraction The fraction of the maximum acceleration to use for the motion
143+
* (0.0 < fraction <= 1.0).
144+
* \param velocity_fraction The fraction of the maximum velocity to use for the motion_sequence
145+
* (0.0 < fraction <= 1.0).
146+
* \param blend_radius The blend radius to use for the motion.
147+
*
148+
* \return True if the robot has reached the target, false otherwise.
149+
*/
150+
bool optimoveJ(const urcl::vector6d_t& target, const double acceleration_fraction = 0.3,
151+
const double velocity_fraction = 0.3, const double blend_radius = 0);
152+
153+
/**
154+
* \brief Move the robot to a pose target using optimoveL.
155+
*
156+
* This function will move the robot to the given pose target using the optimoveL motion
157+
* primitive. The robot will move with the given acceleration and velocity fractions.
158+
*
159+
* \param target The pose target to move to.
160+
* \param acceleration_fraction The fraction of the maximum acceleration to use for the motion
161+
* (0.0 < fraction <= 1.0).
162+
* \param velocity_fraction The fraction of the maximum velocity to use for the motion_sequence
163+
* (0.0 < fraction <= 1.0).
164+
* \param blend_radius The blend radius to use for the motion.
165+
*
166+
* \return True if the robot has reached the target, false otherwise.
167+
*/
168+
bool optimoveL(const urcl::Pose& target, const double acceleration_fraction = 0.3,
169+
const double velocity_fraction = 0.3, const double blend_radius = 0);
170+
135171
/**
136172
* \brief Cancel the current motion.
137173
*
@@ -154,6 +190,7 @@ class InstructionExecutor
154190
private:
155191
void trajDoneCallback(const urcl::control::TrajectoryResult& result);
156192
void trajDisconnectCallback(const int filedescriptor);
193+
157194
std::shared_ptr<urcl::UrDriver> driver_;
158195
std::atomic<bool> trajectory_running_ = false;
159196
std::atomic<bool> cancel_requested_ = false;

resources/external_control.urscript

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ textmsg("ExternalControl: steptime=", steptime)
88
MULT_jointstate = {{JOINT_STATE_REPLACE}}
99
MULT_time = {{TIME_REPLACE}}
1010

11+
DEBUG = False
12+
1113
STOPJ_ACCELERATION = 4.0
1214

1315
#Constants
@@ -35,6 +37,8 @@ MOTION_TYPE_MOVEJ = 0
3537
MOTION_TYPE_MOVEL = 1
3638
MOTION_TYPE_MOVEP = 2
3739
MOTION_TYPE_MOVEC = 3
40+
MOTION_TYPE_OPTIMOVEJ = 4
41+
MOTION_TYPE_OPTIMOVEL = 5
3842
MOTION_TYPE_SPLINE = 51
3943
TRAJECTORY_DATA_DIMENSION = 3 * 6 + 1
4044

@@ -551,6 +555,63 @@ thread trajectoryThread():
551555
clear_remaining_trajectory_points()
552556
trajectory_result = TRAJECTORY_RESULT_FAILURE
553557
end
558+
559+
# OptimoveJ point
560+
elif raw_point[INDEX_POINT_TYPE] == MOTION_TYPE_OPTIMOVEJ:
561+
acceleration = raw_point[13] / MULT_jointstate
562+
velocity = raw_point[7] / MULT_jointstate
563+
{% if ROBOT_SOFTWARE_VERSION >= v5.21.0 %}
564+
{% if ROBOT_SOFTWARE_VERSION < v6.0.0 %}
565+
optimovej(q, a = acceleration, v = velocity, r = blend_radius)
566+
{% elif ROBOT_SOFTWARE_VERSION >= v10.8.0 %}
567+
optimovej(q, a = acceleration, v = velocity, r = blend_radius)
568+
{% else %}
569+
popup("Optimovej is only supported from software 10.8.0 and upwards.", error=True, blocking=False)
570+
{% endif %}
571+
{% else %}
572+
popup("Optimovej is only supported from software 5.21.0 and upwards.", error=True, blocking=False)
573+
{% endif %}
574+
575+
if DEBUG:
576+
textmsg(str_cat("optimovej(", str_cat(
577+
str_cat("q=", q), str_cat(
578+
str_cat(", a=", acceleration), str_cat(
579+
str_cat(", v=", velocity), str_cat(
580+
str_cat(", r=", blend_radius), ")"))))))
581+
end
582+
583+
# reset old acceleration
584+
spline_qdd = [0, 0, 0, 0, 0, 0]
585+
spline_qd = [0, 0, 0, 0, 0, 0]
586+
587+
# OptimoveL point
588+
elif raw_point[INDEX_POINT_TYPE] == MOTION_TYPE_OPTIMOVEL:
589+
acceleration = raw_point[13] / MULT_jointstate
590+
velocity = raw_point[7] / MULT_jointstate
591+
592+
{% if ROBOT_SOFTWARE_VERSION >= v5.21.0 %}
593+
{% if ROBOT_SOFTWARE_VERSION < v6.0.0 %}
594+
optimovel(p[q[0], q[1], q[2], q[3], q[4], q[5]], a = acceleration, v = velocity, r = blend_radius)
595+
{% elif ROBOT_SOFTWARE_VERSION >= v10.8.0 %}
596+
optimovel(p[q[0], q[1], q[2], q[3], q[4], q[5]], a = acceleration, v = velocity, r = blend_radius)
597+
{% else %}
598+
popup("Optimovel is only supported from software 10.8.0 and upwards.", error=True, blocking=False)
599+
{% endif %}
600+
{% else %}
601+
popup("Optimovel is only supported from software 5.21.0 and upwards.", error=True, blocking=False)
602+
{% endif %}
603+
604+
if DEBUG:
605+
textmsg(str_cat("optimovel(", str_cat(
606+
str_cat("q=", p[q[0], q[1], q[2], q[3], q[4], q[5]]), str_cat(
607+
str_cat(", a=", acceleration), str_cat(
608+
str_cat(", v=", velocity), str_cat(
609+
str_cat(", r=", blend_radius), ")"))))))
610+
end
611+
612+
# reset old acceleration
613+
spline_qdd = [0, 0, 0, 0, 0, 0]
614+
spline_qd = [0, 0, 0, 0, 0, 0]
554615
end
555616
else:
556617
textmsg("Receiving trajectory point failed!")
@@ -815,4 +876,4 @@ socket_close("reverse_socket")
815876
socket_close("trajectory_socket")
816877
socket_close("script_command_socket")
817878

818-
# NODE_CONTROL_LOOP_ENDS
879+
# NODE_CONTROL_LOOP_ENDS

src/control/motion_primitives.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
// Copyright 2025 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 <ur_client_library/control/motion_primitives.h>
32+
#include <ur_client_library/log.h>
33+
34+
namespace urcl::control
35+
{
36+
37+
bool MotionPrimitive::validate() const
38+
{
39+
if (blend_radius < 0)
40+
{
41+
URCL_LOG_ERROR("Negative blend radius passed to motion primitive. This is not allowed.");
42+
return false;
43+
}
44+
if (acceleration < 0)
45+
{
46+
URCL_LOG_ERROR("Negative acceleration passed to motion primitive. This is not allowed.");
47+
return false;
48+
}
49+
if (velocity < 0)
50+
{
51+
URCL_LOG_ERROR("Negative velocity passed to motion primitive. This is not allowed.");
52+
return false;
53+
}
54+
return true;
55+
}
56+
57+
bool SplinePrimitive::validate() const
58+
{
59+
// Spline primitives don't have the same restriction as others do. Whether the primitives are valid or not
60+
// is checked in the URScript program.
61+
return true;
62+
}
63+
bool OptimoveJPrimitive::validate() const
64+
{
65+
if (!MotionPrimitive::validate())
66+
{
67+
return false;
68+
}
69+
if (acceleration <= 0 || acceleration > 1.0)
70+
{
71+
URCL_LOG_ERROR("Acceleration fraction must be in range (0, 1].");
72+
return false;
73+
}
74+
if (velocity <= 0 || velocity > 1.0)
75+
{
76+
URCL_LOG_ERROR("Velocity fraction must be in range (0, 1].");
77+
return false;
78+
}
79+
return true;
80+
}
81+
82+
bool OptimoveLPrimitive::validate() const
83+
{
84+
if (!MotionPrimitive::validate())
85+
{
86+
return false;
87+
}
88+
if (acceleration <= 0 || acceleration > 1.0)
89+
{
90+
URCL_LOG_ERROR("Acceleration fraction must be in range (0, 1].");
91+
return false;
92+
}
93+
if (velocity <= 0 || velocity > 1.0)
94+
{
95+
URCL_LOG_ERROR("Velocity fraction must be in range (0, 1].");
96+
return false;
97+
}
98+
return true;
99+
}
100+
} // namespace urcl::control

0 commit comments

Comments
 (0)