Skip to content

Commit bd7d050

Browse files
committed
Wrote more tests and a little refactoring
1 parent 85b7c01 commit bd7d050

File tree

5 files changed

+83
-68
lines changed

5 files changed

+83
-68
lines changed

ur_controllers/include/ur_controllers/tool_contact_controller.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ class ToolContactController : public controller_interface::ControllerInterface
9090

9191
RealtimeGoalHandleBuffer rt_active_goal_; ///< Currently active action goal, if any.
9292
rclcpp::TimerBase::SharedPtr goal_handle_timer_; ///< Timer to frequently check on the running goal
93-
rclcpp::duration<std::chrono::nanoseconds> action_monitor_period_;
93+
rclcpp::Duration action_monitor_period_ = rclcpp::Duration::from_seconds(0.05);
9494

95-
void action_handler(RealtimeGoalHandlePtr rt_goal);
95+
void action_handler();
9696

9797
rclcpp_action::GoalResponse goal_received_callback(const rclcpp_action::GoalUUID& /*uuid*/,
9898
std::shared_ptr<const ur_msgs::action::ToolContact::Goal> goal);
@@ -110,6 +110,7 @@ class ToolContactController : public controller_interface::ControllerInterface
110110
std::atomic<bool> tool_contact_abort_ = false;
111111
std::atomic<bool> change_requested_ = false;
112112
std::atomic<bool> logged_once_ = false;
113+
std::atomic<bool> should_reset_goal = false;
113114

114115
std::optional<std::reference_wrapper<hardware_interface::LoanedStateInterface>> tool_contact_result_interface_;
115116
std::optional<std::reference_wrapper<hardware_interface::LoanedStateInterface>> major_version_state_interface_;

ur_controllers/src/tool_contact_controller.cpp

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ controller_interface::InterfaceConfiguration ToolContactController::command_inte
7474
const std::string tf_prefix = tool_contact_params_.tf_prefix;
7575

7676
// Start or stop tool contact functionality
77-
config.names.emplace_back(tf_prefix + "tool_contact/tool_contact_status");
77+
config.names.emplace_back(tf_prefix + "tool_contact/tool_contact_set_state");
7878
return config;
7979
}
8080

@@ -84,28 +84,29 @@ controller_interface::InterfaceConfiguration ToolContactController::state_interf
8484
config.type = controller_interface::interface_configuration_type::INDIVIDUAL;
8585

8686
const std::string tf_prefix = tool_contact_params_.tf_prefix;
87-
config.names.push_back(tf_prefix + "tool_contact/tool_contact_result");
88-
config.names.push_back(tf_prefix + "get_robot_software_version/get_version_major");
87+
config.names.emplace_back(tf_prefix + "tool_contact/tool_contact_result");
88+
config.names.emplace_back(tf_prefix + "get_robot_software_version/get_version_major");
89+
config.names.emplace_back(tf_prefix + "tool_contact/tool_contact_state");
8990
return config;
9091
}
9192

9293
controller_interface::CallbackReturn
9394
ToolContactController::on_activate(const rclcpp_lifecycle::State& /* previous_state */)
9495
{
96+
double val;
9597
{
9698
const std::string interface_name = tool_contact_params_.tf_prefix + "tool_contact/tool_contact_state";
9799
auto it = std::find_if(state_interfaces_.begin(), state_interfaces_.end(),
98100
[&](auto& interface) { return (interface.get_name() == interface_name); });
99101
if (it != state_interfaces_.end()) {
100102
tool_contact_state_interface_ = *it;
101-
if (!tool_contact_state_interface_->get().get_value()) {
103+
if (!tool_contact_state_interface_->get().get_value(val)) {
102104
RCLCPP_ERROR(get_node()->get_logger(),
103-
"Failed to set '%s' command interface, aborting activation of controller.",
104-
interface_name.c_str());
105+
"Failed to read '%s' state interface, aborting activation of controller.", interface_name.c_str());
105106
return controller_interface::CallbackReturn::ERROR;
106107
}
107108
} else {
108-
RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in command interfaces.", interface_name.c_str());
109+
RCLCPP_ERROR(get_node()->get_logger(), "Did not find '%s' in state interfaces.", interface_name.c_str());
109110
return controller_interface::CallbackReturn::ERROR;
110111
}
111112
}
@@ -132,7 +133,7 @@ ToolContactController::on_activate(const rclcpp_lifecycle::State& /* previous_st
132133
[&](auto& interface) { return (interface.get_name() == interface_name); });
133134
if (it != state_interfaces_.end()) {
134135
tool_contact_result_interface_ = *it;
135-
if (!tool_contact_result_interface_->get().get_value()) {
136+
if (!tool_contact_result_interface_->get().get_value(val)) {
136137
RCLCPP_ERROR(get_node()->get_logger(),
137138
"Failed to read '%s' state interface, aborting activation of controller.", interface_name.c_str());
138139
return controller_interface::CallbackReturn::ERROR;
@@ -165,8 +166,7 @@ ToolContactController::on_activate(const rclcpp_lifecycle::State& /* previous_st
165166
}
166167
}
167168

168-
action_monitor_period_ =
169-
rclcpp::duration<std::chrono::nanoseconds>(rclcpp::Rate(tool_contact_params_.action_monitor_rate).period());
169+
action_monitor_period_ = rclcpp::Duration(rclcpp::Rate(tool_contact_params_.action_monitor_rate).period());
170170
return controller_interface::CallbackReturn::SUCCESS;
171171
}
172172

@@ -179,9 +179,9 @@ ToolContactController::on_deactivate(const rclcpp_lifecycle::State& /* previous_
179179
RCLCPP_INFO(get_node()->get_logger(), "Aborting tool contact, as controller has been deactivated.");
180180
// Mark the current goal as abort
181181
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
182-
result->result = ur_msgs::action::ToolContact::Result::ABORTED_BY_CONTROLLER;
182+
result->result = ur_msgs::action::ToolContact::Result::ABORTED;
183183
active_goal->setAborted(result);
184-
rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr());
184+
should_reset_goal = true;
185185
}
186186
if (tool_contact_active_) {
187187
tool_contact_active_ = false;
@@ -212,7 +212,8 @@ rclcpp_action::GoalResponse ToolContactController::goal_received_callback(
212212
return rclcpp_action::GoalResponse::REJECT;
213213
}
214214

215-
if (tool_contact_active_) {
215+
const auto active_goal = *rt_active_goal_.readFromNonRT();
216+
if (active_goal) {
216217
RCLCPP_ERROR(get_node()->get_logger(), "Tool contact already active, rejecting goal.");
217218
return rclcpp_action::GoalResponse::REJECT;
218219
}
@@ -229,22 +230,22 @@ void ToolContactController::goal_accepted_callback(
229230
rt_goal->execute();
230231
rt_active_goal_.writeFromNonRT(rt_goal);
231232
goal_handle_timer_.reset();
232-
goal_handle_timer_ =
233-
get_node()->create_wall_timer(action_monitor_period_, std::bind(&ToolContactController::action_handler, rt_goal));
233+
auto period = std::chrono::duration_cast<std::chrono::nanoseconds>(
234+
std::chrono::nanoseconds(action_monitor_period_.nanoseconds()));
235+
goal_handle_timer_ = get_node()->create_wall_timer(period, std::bind(&ToolContactController::action_handler, this));
234236
return;
235237
}
236238

237-
void ToolContactController::action_handler(RealtimeGoalHandlePtr rt_goal)
239+
void ToolContactController::action_handler()
238240
{
239241
const auto active_goal = *rt_active_goal_.readFromNonRT();
240242
if (active_goal) {
241-
// Save the value if any of the goal ending conditions have been activated
242-
static bool reset_goal = active_goal->req_abort_ | active_goal->req_cancel_ | active_goal->req_succeed_;
243243
// Allow the goal to handle any actions it needs to perform
244-
rt_goal->runNonRealtime();
244+
active_goal->runNonRealtime();
245245
// If one of the goal ending conditions were met, reset our active goal pointer
246-
if (reset_goal) {
246+
if (should_reset_goal) {
247247
rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr());
248+
should_reset_goal = false;
248249
}
249250
}
250251
}
@@ -259,7 +260,7 @@ rclcpp_action::CancelResponse ToolContactController::goal_cancelled_callback(
259260

260261
// Mark the current goal as canceled
261262
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
262-
result->result = ur_msgs::action::ToolContact::Result::CANCELLED_BY_USER;
263+
result->result = ur_msgs::action::ToolContact::Result::PREEMPTED;
263264
active_goal->setCanceled(result);
264265
rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr());
265266
tool_contact_abort_ = true;
@@ -305,6 +306,7 @@ controller_interface::return_type ToolContactController::update(const rclcpp::Ti
305306
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
306307
result->result = ur_msgs::action::ToolContact::Result::SUCCESS;
307308
active_goal->setSucceeded(result);
309+
should_reset_goal = true;
308310
}
309311
} else if (result == 1.0) {
310312
tool_contact_active_ = false;
@@ -313,8 +315,9 @@ controller_interface::return_type ToolContactController::update(const rclcpp::Ti
313315
write_success &= tool_contact_set_state_interface_->get().set_value(TOOL_CONTACT_STANDBY);
314316
if (active_goal) {
315317
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
316-
result->result = ur_msgs::action::ToolContact::Result::ABORTED_BY_HARDWARE;
318+
result->result = ur_msgs::action::ToolContact::Result::ABORTED;
317319
active_goal->setAborted(result);
320+
should_reset_goal = true;
318321
}
319322
}
320323
} break;
@@ -327,8 +330,9 @@ controller_interface::return_type ToolContactController::update(const rclcpp::Ti
327330

328331
if (active_goal) {
329332
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
330-
result->result = ur_msgs::action::ToolContact::Result::ABORTED_BY_HARDWARE;
333+
result->result = ur_msgs::action::ToolContact::Result::ABORTED;
331334
active_goal->setAborted(result);
335+
should_reset_goal = true;
332336
}
333337
} break;
334338

@@ -348,8 +352,9 @@ controller_interface::return_type ToolContactController::update(const rclcpp::Ti
348352

349353
if (active_goal) {
350354
auto result = std::make_shared<ur_msgs::action::ToolContact::Result>();
351-
result->result = ur_msgs::action::ToolContact::Result::ABORTED_BY_HARDWARE;
355+
result->result = ur_msgs::action::ToolContact::Result::ABORTED;
352356
active_goal->setAborted(result);
357+
should_reset_goal = true;
353358
}
354359
} break;
355360
case static_cast<int>(TOOL_CONTACT_STANDBY):

ur_robot_driver/src/hardware_interface.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,6 @@ hardware_interface::return_type URPositionHardwareInterface::read(const rclcpp::
745745
force_mode_disable_cmd_ = NO_NEW_CMD_;
746746
freedrive_mode_abort_ = NO_NEW_CMD_;
747747
freedrive_mode_enable_ = NO_NEW_CMD_;
748-
tool_contact_result_ = NO_NEW_CMD_;
749-
tool_contact_status_ = NO_NEW_CMD_;
750748
initialized_ = true;
751749
}
752750

ur_robot_driver/test/integration_test_tool_contact.py

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@
3939
from rclpy.node import Node
4040

4141
from controller_manager_msgs.srv import SwitchController
42-
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
43-
from builtin_interfaces.msg import Duration
44-
from ur_msgs.action import ToolContact, TrajectoryUntil
42+
from ur_msgs.action import ToolContact
4543

4644
sys.path.append(os.path.dirname(__file__))
4745
from test_common import ( # noqa: E402
@@ -50,21 +48,16 @@
5048
IoStatusInterface,
5149
ActionInterface,
5250
generate_driver_test_description,
53-
ROBOT_JOINTS,
5451
)
5552

56-
TIMEOUT_EXECUTE_TRAJECTORY = 30
57-
5853

5954
@pytest.mark.launch_test
6055
@launch_testing.parametrize(
61-
"tf_prefix, initial_joint_controller",
62-
[("", "scaled_joint_trajectory_controller"), ("my_ur_", "passthrough_trajectory_controller")],
56+
"tf_prefix",
57+
[(""), ("my_ur_")],
6358
)
64-
def generate_test_description(tf_prefix, initial_joint_controller):
65-
return generate_driver_test_description(
66-
tf_prefix=tf_prefix, initial_joint_controller=initial_joint_controller
67-
)
59+
def generate_test_description(tf_prefix):
60+
return generate_driver_test_description(tf_prefix=tf_prefix)
6861

6962

7063
class RobotDriverTest(unittest.TestCase):
@@ -87,10 +80,7 @@ def init_robot(self):
8780
self._controller_manager_interface = ControllerManagerInterface(self.node)
8881
self._io_status_controller_interface = IoStatusInterface(self.node)
8982
self._tool_contact_interface = ActionInterface(
90-
self.node, "/tool_contact_controller/enable_tool_contact", ToolContact
91-
)
92-
self._trajectory_until_interface = ActionInterface(
93-
self.node, "/trajectory_until_node/execute", TrajectoryUntil
83+
self.node, "/tool_contact_controller/detect_tool_contact", ToolContact
9484
)
9585

9686
def setUp(self):
@@ -110,37 +100,61 @@ def test_start_tool_contact_controller(self):
110100
).ok
111101
)
112102

113-
def test_tool_contact(self):
103+
def test_goal_can_be_cancelled(self):
104+
self.assertTrue(
105+
self._controller_manager_interface.switch_controller(
106+
strictness=SwitchController.Request.BEST_EFFORT,
107+
activate_controllers=["tool_contact_controller"],
108+
).ok
109+
)
114110
goal_handle = self._tool_contact_interface.send_goal()
115111
self.assertTrue(goal_handle.accepted)
116112

117113
cancel_res = self._tool_contact_interface.cancel_goal(goal_handle)
118114
self.assertEqual(cancel_res.return_code, 0)
119115

120-
def test_trajectory_until(self, tf_prefix):
121-
L_pose_to_down = {
122-
"waypts": [[1.5, -1.5, -0.45, -1.5, -1.5, -1.5], [2.1, -1.2, -0.45, -2.4, -1.5, -1.5]],
123-
"time_vec": [Duration(sec=3, nanosec=0), Duration(sec=6, nanosec=0)],
124-
}
116+
def test_deactivate_controller_aborts_action(self):
117+
self.assertTrue(
118+
self._controller_manager_interface.switch_controller(
119+
strictness=SwitchController.Request.BEST_EFFORT,
120+
activate_controllers=["tool_contact_controller"],
121+
).ok
122+
)
125123

126-
trajectory = JointTrajectory()
127-
trajectory.joint_names = [tf_prefix + joint for joint in ROBOT_JOINTS]
124+
goal_handle = self._tool_contact_interface.send_goal()
125+
self.assertTrue(goal_handle.accepted)
126+
127+
self.assertTrue(
128+
self._controller_manager_interface.switch_controller(
129+
strictness=SwitchController.Request.BEST_EFFORT,
130+
deactivate_controllers=["tool_contact_controller"],
131+
).ok
132+
)
128133

129-
trajectory.points = [
130-
JointTrajectoryPoint(
131-
positions=L_pose_to_down["waypts"][i], time_from_start=L_pose_to_down["time_vec"][i]
132-
)
133-
for i in range(len(L_pose_to_down["waypts"]))
134-
]
134+
result = self._tool_contact_interface.get_result(goal_handle, 5)
135+
self.assertEqual(result.result, ToolContact.Result.ABORTED)
135136

136-
goal_handle = self._trajectory_until_interface.send_goal(
137-
trajectory=trajectory, until_type=TrajectoryUntil.Goal.TOOL_CONTACT
137+
def test_inactive_controller_rejects_actions(self):
138+
self.assertTrue(
139+
self._controller_manager_interface.switch_controller(
140+
strictness=SwitchController.Request.BEST_EFFORT,
141+
deactivate_controllers=["tool_contact_controller"],
142+
).ok
138143
)
139-
self.assertTrue(goal_handle.accepted)
140144

141-
result = self._trajectory_until_interface.get_result(
142-
goal_handle=goal_handle, timeout=TIMEOUT_EXECUTE_TRAJECTORY
145+
goal_handle = self._tool_contact_interface.send_goal()
146+
self.assertFalse(goal_handle.accepted)
147+
148+
def test_busy_controller_rejects_actions(self):
149+
self.assertTrue(
150+
self._controller_manager_interface.switch_controller(
151+
strictness=SwitchController.Request.BEST_EFFORT,
152+
activate_controllers=["tool_contact_controller"],
153+
).ok
143154
)
144155

145-
self.assertEqual(result.error_code, TrajectoryUntil.Result.SUCCESSFUL)
146-
self.assertGreaterEqual(result.until_condition_result, TrajectoryUntil.Result.NOT_TRIGGERED)
156+
goal_handle = self._tool_contact_interface.send_goal()
157+
self.assertTrue(goal_handle.accepted)
158+
159+
goal_handle = self._tool_contact_interface.send_goal()
160+
self.assertFalse(goal_handle.accepted)

ur_robot_driver/urdf/ur.ros2_control.xacro

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,6 @@
233233

234234
<gpio name="${tf_prefix}tool_contact">
235235
<command_interface name="tool_contact_set_state"/>
236-
</gpio>
237-
238-
<gpio name="${tf_prefix}tool_contact">
239236
<state_interface name="tool_contact_result"/>
240237
<state_interface name="tool_contact_state"/>
241238
</gpio>

0 commit comments

Comments
 (0)