Skip to content

Commit b5be8bd

Browse files
authored
Fix instruction executor idle (UniversalRobots#424)
When using the InstructionExecutor, sending the first trajectory puts the external_control program in `MODE_FORWARD`. We then send keepalive signals every 100ms with a timeout of 200 ms. However, when the trajectory has finished, nothing is sent anymore, hence reading the keepalive signal times out and program execution is stopped. This is most likely the cause of UniversalRobots#423. This PR explicitly sets the external_control program into `MODE_IDLE` once a trajectory has finished. That allows waiting for a new package on the reverse interface indefinitely. Thus, users can spend as much time between trajectory calls as they like without having to manually send keepalive signals of some sort.
1 parent d8bec80 commit b5be8bd

2 files changed

Lines changed: 24 additions & 1 deletion

File tree

src/ur/instruction_executor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ bool urcl::InstructionExecutor::executeMotion(
9191
if (!cancel_requested_)
9292
{
9393
std::unique_lock<std::mutex> lock(trajectory_result_mutex_);
94+
driver_->writeJointCommand({ 0, 0, 0, 0, 0, 0 }, comm::ControlMode::MODE_IDLE, RobotReceiveTimeout::millisec(0));
9495
URCL_LOG_INFO("Trajectory done with result %s", control::trajectoryResultToString(trajectory_result_).c_str());
9596
return trajectory_result_ == urcl::control::TrajectoryResult::TRAJECTORY_RESULT_SUCCESS;
9697
}

tests/test_instruction_executor.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include <gtest/gtest.h>
3232
#include <algorithm>
33+
#include <future>
3334
#include <iostream>
3435
#include <thread>
3536
#include "ur_client_library/ur/instruction_executor.h"
@@ -455,6 +456,27 @@ TEST_F(InstructionExecutorTest, optimovel_with_illegal_parameters_fails)
455456
ASSERT_FALSE(executor_->optimoveL({ -0.203, 0.263, 0.559, 0.68, -1.083, -2.076 }, 0.4, 0.7, -0.1));
456457
}
457458

459+
TEST_F(InstructionExecutorTest, no_new_trajectory_doesnt_stop_execution)
460+
{
461+
// move to a feasible starting pose
462+
ASSERT_TRUE(executor_->moveJ({ -1.57, -1.6, 1.6, -0.7, 0.7, 0.2 }));
463+
464+
// The internal keepalive of the instruction executor is 200 ms
465+
// The program should keep running.
466+
ASSERT_FALSE(g_my_robot->waitForProgramNotRunning(500));
467+
468+
auto op = [this]() mutable { return executor_->moveJ({ -1.57, -1.6, 1.6, -0.7, 0.7, -2.2 }); };
469+
auto result = std::async(std::launch::async, op);
470+
471+
// Give motion some time to start, then cancel
472+
std::this_thread::sleep_for(std::chrono::milliseconds(200));
473+
executor_->cancelMotion();
474+
475+
result.wait();
476+
ASSERT_FALSE(result.get());
477+
ASSERT_FALSE(g_my_robot->waitForProgramNotRunning(500));
478+
}
479+
458480
int main(int argc, char* argv[])
459481
{
460482
::testing::InitGoogleTest(&argc, argv);
@@ -475,4 +497,4 @@ int main(int argc, char* argv[])
475497
}
476498

477499
return RUN_ALL_TESTS();
478-
}
500+
}

0 commit comments

Comments
 (0)