Skip to content

Commit 9196723

Browse files
authored
Merge branch 'master' into gh-550-streaming-starvation-recovery-tests
2 parents 4aaffdf + 01292bd commit 9196723

37 files changed

Lines changed: 726 additions & 158 deletions

.github/workflows/check_links.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ jobs:
2929
--cache
3030
--cache-exclude-status '429, 403, 404, 502'
3131
--max-cache-age 2d
32+
--exclude '^https?://(www\.)?stackoverflow\.com'
3233
--exclude '^http://192\.168\.56\.101'
3334
--exclude 'kernel\.org\/pub\/linux\/kernel'
3435
--exclude 'releases/download/v\$%7BURCAP_VERSION%7D/externalcontrol-\$%7BURCAP_VERSION%7D\.jar'
3536
--exclude '^http://rosin-project\.eu'
36-
--exclude '2013181\/gdb-causes-sem-wait-to-fail-with-eintr-error'
3737
--exclude 'https:\/\/lists\.apple\.com\/archives\/darwin-kernel\/2009\/Apr\/msg00010\.html'
3838
--exclude 'https:\/\/wiki\.linuxfoundation\.org\/realtime\/preempt_rt_versions'
3939
--exclude 'https:\/\/en\.cppreference\.com\/w\/cpp\/string\/basic_string\/to_string'

.github/workflows/ci.yml

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,25 @@ jobs:
7575
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
7676
- ROBOT_MODEL: 'ur7e'
7777
URSIM_VERSION: '10.13.0'
78-
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
78+
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex/10.13.0/ur7e'
79+
POLYSCOPE_X_WITH_REMOTE_CONTROL: 'true'
80+
CITADEL_DB_FOLDER: 'tests/resources/dockerursim/citadelDB'
7981

8082
steps:
8183
- uses: actions/checkout@v7
8284
- name: start ursim
8385
run: |
84-
scripts/start_ursim.sh -m $ROBOT_MODEL -v $URSIM_VERSION -p $PROGRAM_FOLDER -d -f DISABLED
86+
CITADEL_DB_ARG=""
87+
if [ -n "$CITADEL_DB_FOLDER" ]; then
88+
CITADEL_DB_ARG="-c $CITADEL_DB_FOLDER"
89+
fi
90+
scripts/start_ursim.sh -m $ROBOT_MODEL -v $URSIM_VERSION -p $PROGRAM_FOLDER $CITADEL_DB_ARG -d -f DISABLED
8591
env:
8692
DOCKER_RUN_OPTS: --network ursim_net
8793
ROBOT_MODEL: ${{matrix.env.ROBOT_MODEL}}
8894
URSIM_VERSION: ${{matrix.env.URSIM_VERSION}}
8995
PROGRAM_FOLDER: ${{matrix.env.PROGRAM_FOLDER}}
96+
CITADEL_DB_FOLDER: ${{matrix.env.CITADEL_DB_FOLDER || ''}}
9097
- id: check_polyscopex
9198
run: |
9299
if [[ "${{matrix.env.URSIM_VERSION}}" == "10."* ]]; then
@@ -103,18 +110,27 @@ jobs:
103110
- name: Extract CMake build directory
104111
run: tar -xzf build.tar.gz
105112
- name: Create folder for test artifacts
106-
run: mkdir -p test_artifacts
113+
run: mkdir -p test_artifacts/screenshots
107114
- name: Access PolyScope
108115
if: ${{ steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
109116
run: chrome --no-sandbox --disable-settuid-sandbox --headless=new 192.168.56.101 &
117+
- name: Setup Python
118+
uses: actions/setup-python@v5
119+
with:
120+
python-version: '3.x'
110121
- name: Install Python dependencies
111-
run: sudo apt-get update && sudo apt-get install -y python3-pandas python3-lxml
122+
run: pip install pandas lxml
123+
- name: Install Playwright for PolyScope X screenshots
124+
if: ${{ steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
125+
run: pip install playwright && playwright install chromium
112126
- name: Generate rtde outputs lists
113127
run: python3 tests/resources/generate_rtde_outputs.py
114128
- name: test
115129
run: cd build && ctest --output-on-failure --output-junit junit.xml
116130
env:
117131
URSIM_VERSION: ${{matrix.env.URSIM_VERSION}}
132+
POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL: ${{matrix.env.POLYSCOPE_X_WITH_REMOTE_CONTROL || ''}}
133+
POLYSCOPE_X_SCREENSHOT_DIR: ${{github.workspace}}/test_artifacts/screenshots
118134
- name: Upload test results to Codecov
119135
uses: codecov/codecov-action@v7
120136
with:
@@ -168,6 +184,25 @@ jobs:
168184
path: test_artifacts
169185
if-no-files-found: error
170186
retention-days: 10
187+
- name: Capture PolyScope X URSim logs
188+
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
189+
run: docker logs ursim > test_artifacts/ursim_docker.log 2>&1
190+
- name: Upload PolyScope X URSim logs
191+
uses: actions/upload-artifact@v7
192+
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
193+
with:
194+
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_URSim_Logs
195+
path: test_artifacts/ursim_docker.log
196+
if-no-files-found: error
197+
retention-days: 10
198+
- name: Upload PolyScope X screenshots
199+
uses: actions/upload-artifact@v7
200+
if: ${{ failure() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
201+
with:
202+
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_screenshots
203+
path: test_artifacts/screenshots
204+
if-no-files-found: ignore
205+
retention-days: 10
171206
- name: Upload generated scripts
172207
uses: actions/upload-artifact@v6
173208
if: ${{ always() }}

.github/workflows/industrial-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- NAME: kilted
2424
DOWNSTREAM_WORKSPACE: "github:UniversalRobots/Universal_Robots_ROS2_Driver#kilted"
2525
- NAME: lyrical
26-
DOWNSTREAM_WORKSPACE: "github:UniversalRobots/Universal_Robots_ROS2_Driver#main"
26+
DOWNSTREAM_WORKSPACE: "github:UniversalRobots/Universal_Robots_ROS2_Driver#lyrical"
2727
- NAME: rolling
2828
DOWNSTREAM_WORKSPACE: "github:UniversalRobots/Universal_Robots_ROS2_Driver#main"
2929
CLANG_TIDY: pedantic
@@ -58,7 +58,7 @@ jobs:
5858
env:
5959
IMMEDIATE_TEST_OUTPUT: true
6060
DOWNSTREAM_CMAKE_ARGS: -DUR_ROBOT_DRIVER_BUILD_INTEGRATION_TESTS=ON
61-
ADDITIONAL_DEBS: docker.io netcat-openbsd # Needed for integration tests
61+
ADDITIONAL_DEBS: docker.io netcat-openbsd curl # Needed for integration tests
6262
DOWNSTREAM_WORKSPACE: ${{matrix.ROS_DISTRO.DOWNSTREAM_WORKSPACE}}
6363
ROS_DISTRO: ${{matrix.ROS_DISTRO.NAME}}
6464
ROS_REPO: ${{matrix.ROS_REPO}}

doc/architecture/trajectory_point_interface.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ representations in 22 datafields. The data fields have the following meaning:
5757
- For all MOVEC variants this field contains the via point (same
5858
joint-vs-pose interpretation as the target at indices 0-5, see the motion type at
5959
index 20).
60-
- trajectory point velocities (multiplied by ``MULT_JOINTSTATE``) for spline joint types
60+
- trajectory point velocities (multiplied by ``MULT_VEL_ACC``) for spline joint types
6161

6262
12-17 Depending on the motion type, this represents either
6363

64-
- trajectory point accelerations (multiplied by ``MULT_JOINTSTATE``) for spline joint
64+
- trajectory point accelerations (multiplied by ``MULT_VEL_ACC``) for spline joint
6565
types.
6666

6767
- for all other motion types
@@ -121,12 +121,23 @@ where
121121

122122
- ``MULT_JOINTSTATE``: 1000000
123123
- ``MULT_TIME``: 1000000
124+
- ``MULT_VEL_ACC``: 100000000
124125

125126
.. note::
126127
With ``MULT_TIME`` being 1000000, the maximum duration that can be sent is 2147 seconds, while
127128
precision is cut off at 1 microsecond. (The same applies to the blend radius, respectively being
128129
max 2147 m and 1 μm precision.)
129130

131+
.. note::
132+
Spline point velocities and accelerations use the finer ``MULT_VEL_ACC`` scaling, giving 1e-8
133+
resolution with a maximum magnitude of ~21.47 rad/s (rad/s²). The coarser ``MULT_JOINTSTATE``
134+
scaling quantizes near-zero accelerations, causing reconstructed acceleration
135+
profile to become jagged, which can trigger controller faults. Values exceeding the maximum magnitude are rejected by the library.
136+
137+
Finer scaling is only used when the control script defines the matching multiplier through
138+
the ``{{VEL_ACC_REPLACE}}`` placeholder. For user-supplied scripts without that placeholder the
139+
library falls back to encoding spline velocities and accelerations with ``MULT_JOINTSTATE``.
140+
130141
.. note::
131142
The ``*_POSE`` / ``*_JOINT`` motion-type variants let callers mix joint-space and Cartesian
132143
targets freely through the high-level APIs (see :ref:`instruction_executor` and the

include/ur_client_library/control/script_reader.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ class ScriptReader
8787
*/
8888
static bool evaluateExpression(const std::string& expression, const DataDict& data);
8989

90+
/*!
91+
* \brief Checks whether a variable placeholder was substituted during the last script read.
92+
* This allows detecting which features a user-supplied script supports.
93+
* \param key The variable name to check.
94+
* \return True if the last read script, including its includes, contained the placeholder.
95+
*/
96+
bool isVariableRegistered(const std::string& key) const;
97+
9098
private:
9199
enum BlockType
92100
{
@@ -108,11 +116,13 @@ class ScriptReader
108116
std::filesystem::path current_dir_;
109117
std::unordered_set<std::string> include_stack_;
110118
std::size_t include_depth_ = 0;
119+
std::unordered_set<std::string> variable_registry_;
111120

112121
std::string readScriptFileImpl(const std::filesystem::path& canonical_path, const DataDict& data);
113122
static std::string readFileContent(const std::string& file_path);
114123
void replaceIncludes(std::string& script_code, const DataDict& data);
115-
static void replaceVariables(std::string& script_code, const DataDict& data);
124+
static void replaceVariables(std::string& script_code, const DataDict& data,
125+
std::unordered_set<std::string>& variable_registry);
116126
static void replaceConditionals(std::string& script_code, const DataDict& data);
117127
};
118128

include/ur_client_library/control/trajectory_point_interface.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class TrajectoryPointInterface : public ReverseInterface
6565
{
6666
public:
6767
static const int MESSAGE_LENGTH = 22;
68+
// Spline vel/acc need finer resolution: quantising near-zero values at 1e-6 causes controller faults.
69+
static constexpr int32_t MULT_VEL_ACC = 100000000;
70+
6871

6972
TrajectoryPointInterface() = delete;
7073
/*!
@@ -134,6 +137,28 @@ class TrajectoryPointInterface : public ReverseInterface
134137
*/
135138
bool writeMotionPrimitive(const std::shared_ptr<control::MotionPrimitive> primitive);
136139

140+
/*!
141+
* \brief Sets the multiplier used to encode spline point velocities and accelerations.
142+
*
143+
* Spline velocities and accelerations in legacy scripts have a coarser multiplier
144+
* so the encoding has to match the running script. Defaults to the modern
145+
* multiplier.
146+
*
147+
* \param multiplier The multiplier the running control script uses to decode spline velocities
148+
* and accelerations.
149+
*
150+
* \throws std::invalid_argument if the multiplier is not positive.
151+
*/
152+
void setVelAccMultiplier(const int32_t multiplier);
153+
154+
/*!
155+
* \brief Returns the multiplier used to encode spline point velocities and accelerations.
156+
*/
157+
int32_t getVelAccMultiplier() const
158+
{
159+
return mult_vel_acc_;
160+
}
161+
137162
void setTrajectoryEndCallback(std::function<void(TrajectoryResult)> callback);
138163

139164
uint32_t addTrajectoryEndCallback(const std::function<void(TrajectoryResult)>& callback);
@@ -165,6 +190,8 @@ class TrajectoryPointInterface : public ReverseInterface
165190
private:
166191
const double MAX_GOAL_TIME_ = static_cast<double>(std::numeric_limits<int32_t>::max()) / MULT_TIME;
167192

193+
int32_t mult_vel_acc_ = MULT_VEL_ACC;
194+
168195
std::list<HandlerFunction<void(TrajectoryResult)>> trajectory_end_callbacks_;
169196
uint32_t next_done_callback_id_ = 0;
170197

include/ur_client_library/helpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,5 +186,7 @@ RobotSeries robotSeriesFromTypeAndVersion(const RobotType type, const VersionInf
186186
*/
187187
RobotType robotTypeFromString(const std::string& robot_type_str);
188188

189+
std::string stringFromMotionTarget(const MotionTarget& target);
190+
189191
} // namespace urcl
190192
#endif // ifndef UR_CLIENT_LIBRARY_HELPERS_H_INCLUDED

include/ur_client_library/types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <optional>
3030
#include <variant>
3131
#include <vector>
32+
#include <string>
3233

3334
namespace urcl
3435
{
@@ -59,6 +60,8 @@ class Q
5960
void setValues(const vector6d_t& values);
6061
void setValues(const std::vector<double>& values);
6162

63+
std::string toString() const;
64+
6265
private:
6366
std::vector<double> values_;
6467
};
@@ -89,6 +92,8 @@ class Pose
8992

9093
void setPose(const double x, const double y, const double z, const double rx, const double ry, const double rz);
9194

95+
std::string toString() const;
96+
9297
double x;
9398
double y;
9499
double z;

include/ur_client_library/ur/ur_driver.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,14 @@ class UrDriver
976976
trajectory_interface_->removeTrajectoryEndCallback(handler_id);
977977
}
978978

979+
/*!
980+
* \brief Returns the multiplier used to encode spline point velocities and accelerations.
981+
*/
982+
int32_t getVelAccMultiplier() const
983+
{
984+
return trajectory_interface_->getVelAccMultiplier();
985+
}
986+
979987
/*!
980988
* \brief Register a callback for the robot-based tool contact execution completion.
981989
*

resources/external_control.urscript

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ steptime = get_steptime()
77
textmsg("ExternalControl: steptime=", steptime)
88
MULT_jointstate = {{JOINT_STATE_REPLACE}}
99
MULT_time = {{TIME_REPLACE}}
10+
MULT_velacc = {{VEL_ACC_REPLACE}}
1011

1112
DEBUG = False
1213

@@ -698,16 +699,16 @@ thread trajectoryThread():
698699

699700
# Cubic spline
700701
if raw_point[INDEX_SPLINE_TYPE] == SPLINE_CUBIC:
701-
qd = [ raw_point[7] / MULT_jointstate, raw_point[8] / MULT_jointstate, raw_point[9] / MULT_jointstate, raw_point[10] / MULT_jointstate, raw_point[11] / MULT_jointstate, raw_point[12] / MULT_jointstate]
702+
qd = [ raw_point[7] / MULT_velacc, raw_point[8] / MULT_velacc, raw_point[9] / MULT_velacc, raw_point[10] / MULT_velacc, raw_point[11] / MULT_velacc, raw_point[12] / MULT_velacc]
702703
is_robot_moving = cubicSplineRun(q, qd, tmptime, is_last_point, is_first_point)
703704

704705
# reset old acceleration
705706
spline_qdd = [0, 0, 0, 0, 0, 0]
706707

707708
# Quintic spline
708709
elif raw_point[INDEX_SPLINE_TYPE] == SPLINE_QUINTIC:
709-
qd = [ raw_point[7] / MULT_jointstate, raw_point[8] / MULT_jointstate, raw_point[9] / MULT_jointstate, raw_point[10] / MULT_jointstate, raw_point[11] / MULT_jointstate, raw_point[12] / MULT_jointstate]
710-
qdd = [ raw_point[13]/ MULT_jointstate, raw_point[14]/ MULT_jointstate, raw_point[15]/ MULT_jointstate, raw_point[16]/ MULT_jointstate, raw_point[17]/ MULT_jointstate, raw_point[18]/ MULT_jointstate]
710+
qd = [ raw_point[7] / MULT_velacc, raw_point[8] / MULT_velacc, raw_point[9] / MULT_velacc, raw_point[10] / MULT_velacc, raw_point[11] / MULT_velacc, raw_point[12] / MULT_velacc]
711+
qdd = [ raw_point[13]/ MULT_velacc, raw_point[14]/ MULT_velacc, raw_point[15]/ MULT_velacc, raw_point[16]/ MULT_velacc, raw_point[17]/ MULT_velacc, raw_point[18]/ MULT_velacc]
711712
is_robot_moving = quinticSplineRun(q, qd, qdd, tmptime, is_last_point, is_first_point)
712713
else:
713714
textmsg("Unknown spline type given:", raw_point[INDEX_POINT_TYPE])

0 commit comments

Comments
 (0)