Skip to content

Commit c34c7d1

Browse files
committed
Make sure (and test) that the robot stops when force_mode controller is stopped
1 parent c4f050e commit c34c7d1

File tree

3 files changed

+147
-20
lines changed

3 files changed

+147
-20
lines changed

ur_controllers/src/force_mode_controller.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
*/
3535
//----------------------------------------------------------------------
3636

37-
#include <future>
3837
#include <limits>
38+
#include <lifecycle_msgs/msg/state.hpp>
3939
#include <rclcpp/logging.hpp>
4040
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
4141

@@ -232,6 +232,13 @@ controller_interface::return_type ur_controllers::ForceModeController::update(co
232232
bool ForceModeController::setForceMode(const ur_msgs::srv::SetForceMode::Request::SharedPtr req,
233233
ur_msgs::srv::SetForceMode::Response::SharedPtr resp)
234234
{
235+
// Reject if controller is not active
236+
if (get_lifecycle_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) {
237+
RCLCPP_ERROR(get_node()->get_logger(), "Can't accept new action goals. Controller is not running.");
238+
resp->success = false;
239+
return false;
240+
}
241+
235242
ForceModeParameters force_mode_parameters;
236243

237244
// transform task frame into base

ur_robot_driver/src/hardware_interface.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ hardware_interface::return_type URPositionHardwareInterface::perform_command_mod
11101110
} else if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(),
11111111
StoppingInterface::STOP_FORCE_MODE) != stop_modes_[0].end()) {
11121112
force_mode_controller_running_ = false;
1113+
stop_force_mode();
11131114
} else if (stop_modes_[0].size() != 0 && std::find(stop_modes_[0].begin(), stop_modes_[0].end(),
11141115
StoppingInterface::STOP_PASSTHROUGH) != stop_modes_[0].end()) {
11151116
passthrough_trajectory_controller_running_ = false;

ur_robot_driver/test/integration_test_force_mode.py

Lines changed: 138 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ def setUp(self):
104104
self.tf_buffer = Buffer()
105105
self.tf_listener = TransformListener(self.tf_buffer, self.node)
106106

107+
def lookup_tcp_in_base(self, tf_prefix, timepoint):
108+
trans = None
109+
while not trans:
110+
rclpy.spin_once(self.node)
111+
try:
112+
trans = self.tf_buffer.lookup_transform(
113+
tf_prefix + "base", tf_prefix + "tool0", timepoint
114+
)
115+
except TransformException:
116+
pass
117+
return trans
118+
107119
def test_force_mode_controller(self, tf_prefix):
108120
self.assertTrue(
109121
self._controller_manager_interface.switch_controller(
@@ -153,15 +165,7 @@ def test_force_mode_controller(self, tf_prefix):
153165
damping_factor = 0.1
154166
gain_scale = 0.8
155167

156-
trans_before = None
157-
while not trans_before:
158-
rclpy.spin_once(self.node)
159-
try:
160-
trans_before = self.tf_buffer.lookup_transform(
161-
tf_prefix + "base", tf_prefix + "tool0", rclpy.time.Time()
162-
)
163-
except TransformException:
164-
pass
168+
trans_before = self.lookup_tcp_in_base(tf_prefix, rclpy.time.Time())
165169

166170
# Send request to controller
167171
res = self._force_mode_controller_interface.start_force_mode(
@@ -183,16 +187,7 @@ def test_force_mode_controller(self, tf_prefix):
183187

184188
time.sleep(5.0)
185189

186-
trans_after = None
187-
timepoint = self.node.get_clock().now()
188-
while not trans_after:
189-
rclpy.spin_once(self.node)
190-
try:
191-
trans_after = self.tf_buffer.lookup_transform(
192-
tf_prefix + "base", tf_prefix + "tool0", timepoint
193-
)
194-
except TransformException:
195-
pass
190+
trans_after = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now())
196191

197192
# task frame and wrench determines the expected motion
198193
# In the example we used
@@ -326,3 +321,127 @@ def test_illegal_task_frame(self, tf_prefix):
326321
task_frame=frame_stamp,
327322
)
328323
self.assertFalse(res.success)
324+
325+
def test_start_force_mode_on_inactive_controller_fails(self, tf_prefix):
326+
self.assertTrue(
327+
self._controller_manager_interface.switch_controller(
328+
strictness=SwitchController.Request.BEST_EFFORT,
329+
activate_controllers=[],
330+
deactivate_controllers=[
331+
"force_mode_controller",
332+
"scaled_joint_trajectory_controller",
333+
"joint_trajectory_controller",
334+
],
335+
).ok
336+
)
337+
self._force_mode_controller_interface = ForceModeInterface(self.node)
338+
339+
# Create task frame for force mode
340+
point = Point(x=0.0, y=0.0, z=0.0)
341+
orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0)
342+
task_frame_pose = Pose()
343+
task_frame_pose.position = point
344+
task_frame_pose.orientation = orientation
345+
header = std_msgs.msg.Header(seq=1, frame_id=tf_prefix + "base")
346+
header.stamp.sec = int(time.time()) + 1
347+
header.stamp.nanosec = 0
348+
frame_stamp = PoseStamped()
349+
frame_stamp.header = header
350+
frame_stamp.pose = task_frame_pose
351+
352+
res = self._force_mode_controller_interface.start_force_mode(
353+
task_frame=frame_stamp,
354+
)
355+
self.assertFalse(res.success)
356+
357+
def test_deactivating_controller_stops_force_mode(self, tf_prefix):
358+
self.assertTrue(
359+
self._controller_manager_interface.switch_controller(
360+
strictness=SwitchController.Request.BEST_EFFORT,
361+
activate_controllers=[
362+
"force_mode_controller",
363+
],
364+
deactivate_controllers=[
365+
"scaled_joint_trajectory_controller",
366+
"joint_trajectory_controller",
367+
],
368+
).ok
369+
)
370+
self._force_mode_controller_interface = ForceModeInterface(self.node)
371+
372+
# Create task frame for force mode
373+
point = Point(x=0.0, y=0.0, z=0.0)
374+
orientation = Quaternion(x=0.0, y=0.0, z=0.0, w=1.0)
375+
task_frame_pose = Pose()
376+
task_frame_pose.position = point
377+
task_frame_pose.orientation = orientation
378+
header = std_msgs.msg.Header(seq=1, frame_id=tf_prefix + "base")
379+
header.stamp.sec = int(time.time()) + 1
380+
header.stamp.nanosec = 0
381+
frame_stamp = PoseStamped()
382+
frame_stamp.header = header
383+
frame_stamp.pose = task_frame_pose
384+
385+
# Create compliance vector (which axes should be force controlled)
386+
compliance = [False, False, True, False, False, False]
387+
388+
# Create Wrench message for force mode
389+
wrench = Wrench()
390+
wrench.force = Vector3(x=0.0, y=0.0, z=5.0)
391+
wrench.torque = Vector3(x=0.0, y=0.0, z=0.0)
392+
393+
# Specify interpretation of task frame (no transform)
394+
type_spec = 2
395+
396+
# Specify max speeds and deviations of force mode
397+
speed_limits = Twist()
398+
speed_limits.linear = Vector3(x=0.0, y=0.0, z=1.0)
399+
speed_limits.angular = Vector3(x=0.0, y=0.0, z=1.0)
400+
deviation_limits = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
401+
402+
# specify damping and gain scaling
403+
damping_factor = 0.1
404+
gain_scale = 0.8
405+
406+
res = self._force_mode_controller_interface.start_force_mode(
407+
task_frame=frame_stamp,
408+
selection_vector_x=compliance[0],
409+
selection_vector_y=compliance[1],
410+
selection_vector_z=compliance[2],
411+
selection_vector_rx=compliance[3],
412+
selection_vector_ry=compliance[4],
413+
selection_vector_rz=compliance[5],
414+
wrench=wrench,
415+
type=type_spec,
416+
speed_limits=speed_limits,
417+
deviation_limits=deviation_limits,
418+
damping_factor=damping_factor,
419+
gain_scaling=gain_scale,
420+
)
421+
self.assertTrue(res.success)
422+
423+
time.sleep(0.5)
424+
425+
self.assertTrue(
426+
self._controller_manager_interface.switch_controller(
427+
strictness=SwitchController.Request.BEST_EFFORT,
428+
activate_controllers=[],
429+
deactivate_controllers=[
430+
"force_mode_controller",
431+
"scaled_joint_trajectory_controller",
432+
"joint_trajectory_controller",
433+
],
434+
).ok
435+
)
436+
self._force_mode_controller_interface = ForceModeInterface(self.node)
437+
438+
time.sleep(0.5)
439+
trans_before_wait = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now())
440+
441+
# Make sure the robot didn't move anymore
442+
time.sleep(0.5)
443+
trans_after_wait = self.lookup_tcp_in_base(tf_prefix, self.node.get_clock().now())
444+
445+
self.assertAlmostEqual(
446+
trans_before_wait.transform.translation.z, trans_after_wait.transform.translation.z
447+
)

0 commit comments

Comments
 (0)