Skip to content

Commit 3122b7e

Browse files
committed
Saving JTC example, but might not be needed
1 parent b52e926 commit 3122b7e

File tree

3 files changed

+132
-21
lines changed

3 files changed

+132
-21
lines changed

ur_robot_driver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ install(PROGRAMS
170170
examples/robot_class.py
171171
examples/passthrough.py
172172
examples/scaled_joint.py
173+
examples/jtc.py
173174
DESTINATION lib/${PROJECT_NAME}
174175
)
175176

ur_robot_driver/examples/jtc.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2024, Universal Robots
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+
30+
import rclpy
31+
from builtin_interfaces.msg import Duration
32+
from rclpy.node import Node
33+
from robot_class import Robot, Actions
34+
from control_msgs.action import FollowJointTrajectory
35+
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
36+
import time
37+
38+
if __name__ == "__main__":
39+
rclpy.init()
40+
node = Node("robot_driver_test")
41+
robot = Robot(node)
42+
43+
robot.switch_motion_controller("scaled_joint_trajectory_controller")
44+
robot.play()
45+
goal = FollowJointTrajectory.Goal()
46+
# The following list are arbitrary joint positions, change according to your own needs
47+
pts = [[robot.HOME[i] + j for i in range(6)] for j in [0, 1, -1]]
48+
vels = [
49+
[
50+
0.0,
51+
0.0,
52+
0.0,
53+
1.0,
54+
0.0,
55+
0.0,
56+
],
57+
[
58+
0.0,
59+
0.0,
60+
0.0,
61+
2.0,
62+
0.0,
63+
0.0,
64+
],
65+
[0.0 for i in range(6)],
66+
]
67+
time_vec = [Duration(sec=4, nanosec=0), Duration(sec=8, nanosec=0), Duration(sec=12, nanosec=0)]
68+
t = JointTrajectory()
69+
t.joint_names = robot.joints
70+
for idx, v in enumerate(vels):
71+
p = JointTrajectoryPoint()
72+
p.positions = pts[idx]
73+
p.velocities = v
74+
p.time_from_start = time_vec[idx]
75+
t.points.append(p)
76+
77+
goal.trajectory = t
78+
# Execute trajectory on robot, make sure that the robot is booted and the control script is running
79+
80+
res = robot.call_action(Actions.FOLLOW_TRAJECTORY, goal)
81+
if not res.accepted:
82+
print("goal not accepted")
83+
quit()
84+
time.sleep(1)
85+
rel = robot.get_result(Actions.FOLLOW_TRAJECTORY, res)
86+
print(rel)

ur_robot_driver/examples/robot_class.py

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,14 @@
5050
TIMEOUT_WAIT_SERVICE_INITIAL = 60
5151
TIMEOUT_WAIT_ACTION = 10
5252

53-
MOTION_CONTROLLERS = ["passthrough_trajectory_controller", "scaled_joint_trajectory_controller"]
53+
# Only one of these can be active at any given time
54+
MOTION_CONTROLLERS = [
55+
"passthrough_trajectory_controller",
56+
"scaled_joint_trajectory_controller",
57+
"joint_trajectory_controller",
58+
"freedrive_mode_controller",
59+
]
60+
5461

5562
ROBOT_JOINTS = [
5663
"shoulder_pan_joint",
@@ -60,10 +67,15 @@
6067
"wrist_2_joint",
6168
"wrist_3_joint",
6269
]
70+
6371
Action_tuple = namedtuple("Actions", ["name", "action_type"])
6472

6573

6674
class Actions(Enum):
75+
JTC = Action_tuple(
76+
"/joint_trajectory_controller/follow_joint_trajectory", FollowJointTrajectory
77+
)
78+
6779
PASSTHROUGH_TRAJECTORY = Action_tuple(
6880
"/passthrough_trajectory_controller/follow_joint_trajectory", FollowJointTrajectory
6981
)
@@ -114,9 +126,10 @@ class Robot:
114126
def __init__(self, node):
115127
self.node = node
116128
self.init_robot()
129+
self.HOME = [0.0, -1.5708, 0.0, -1.5708, 0.0, 0.0]
117130

118131
def init_robot(self):
119-
132+
self.joints = ROBOT_JOINTS
120133
self.service_clients = {
121134
service.name: waitForService(self.node, service.value.name, service.value.service_type)
122135
for (service) in Services
@@ -151,14 +164,7 @@ def set_io(self, pin, value):
151164

152165
def follow_trajectory(self, waypts: list[list[float]], time_vec: list[float]):
153166
# No other motion controllers can be active at the same time as the scaled joint controller
154-
self.switch_controllers(
155-
["scaled_joint_trajectory_controller"],
156-
[
157-
"passthrough_trajectory_controller",
158-
"force_mode_controller",
159-
"freedrive_mode_controller",
160-
],
161-
)
167+
self.switch_motion_controller("scaled_joint_trajectory_controller")
162168
"""Send robot trajectory."""
163169
if len(waypts) != len(time_vec):
164170
raise Exception("waypoints vector and time vec should be same length")
@@ -193,9 +199,7 @@ def passthrough_trajectory(
193199
goal_time_tolerance=Duration(sec=1),
194200
):
195201
# The scaled joint controller can't be active at the same time as the passthrough controller
196-
self.switch_controllers(
197-
["passthrough_trajectory_controller"], ["scaled_joint_trajectory_controller"]
198-
)
202+
self.switch_motion_controller("passthrough_trajectory_controller")
199203
"""Send trajectory through the passthrough controller."""
200204
if len(waypts) != len(time_vec):
201205
raise Exception("waypoints vector and time vec should be same length.")
@@ -231,8 +235,8 @@ def call_service(self, Service: Services, request):
231235
else:
232236
raise Exception(f"Exception while calling service: {future.exception()}")
233237

234-
def call_action(self, Action: Actions, g):
235-
future = self.action_clients[Action.name].send_goal_async(g)
238+
def call_action(self, Action: Actions, goal):
239+
future = self.action_clients[Action.name].send_goal_async(goal)
236240
rclpy.spin_until_future_complete(self.node, future)
237241

238242
if future.result() is not None:
@@ -248,10 +252,14 @@ def get_result(self, Action: Actions, goal_response):
248252
else:
249253
raise Exception(f"Exception while calling action: {future_res.exception()}")
250254

251-
def load_controller(self, controller_name: str):
255+
def list_controllers(self):
252256
list_response = self.call_service(Services.List_Controllers, ListControllers.Request())
253-
names = []
257+
return list_response
258+
259+
def load_controller(self, controller_name: str):
260+
list_response = self.list_controllers()
254261
# Find loaded controllers
262+
names = []
255263
for controller in list_response.controller:
256264
names.append(controller.name)
257265
# Check whether the controller is already loaded
@@ -263,7 +271,7 @@ def load_controller(self, controller_name: str):
263271
self.call_service(Services.Load_Controller, load_request)
264272
configure_request = ConfigureController.Request(name=controller_name)
265273
self.call_service(Services.Configure_Controller, configure_request)
266-
list_response = self.call_service(Services.List_Controllers, ListControllers.Request())
274+
list_response = self.list_controllers()
267275
names.clear()
268276
# Update the list of controller names.
269277
for controller in list_response.controller:
@@ -273,17 +281,33 @@ def load_controller(self, controller_name: str):
273281
finally:
274282
print(f"Currently loaded controllers: {names}")
275283

276-
def switch_controllers(self, active: list[str] = [], inactive: list[str] = []) -> bool:
284+
def switch_controllers(self, activate: list[str] = [], deactivate: list[str] = []) -> bool:
285+
controllers = self.list_controllers()
286+
activate_ = []
287+
deactivate_ = []
288+
for controller in controllers.controller:
289+
if controller.name in activate and controller.state == "inactive":
290+
activate_.append(controller.name)
291+
elif controller.name in deactivate and controller.state == "active":
292+
deactivate_.append(controller.name)
293+
277294
switch_request = SwitchController.Request()
278-
switch_request.activate_controllers = active
279-
switch_request.deactivate_controllers = inactive
295+
switch_request.activate_controllers = activate_
296+
switch_request.deactivate_controllers = deactivate_
280297
switch_request.strictness = (
281298
SwitchController.Request.BEST_EFFORT
282299
) # Best effort switching, will not terminate program if controller is already running
283300
switch_request.activate_asap = False
284301
switch_request.timeout = Duration(sec=2, nanosec=0)
285302
return self.call_service(Services.Switch_Controller, switch_request)
286303

304+
def switch_motion_controller(self, motion_controller: str):
305+
if motion_controller not in MOTION_CONTROLLERS:
306+
print("Requested motion controller does not exist.")
307+
return self.switch_controllers(
308+
[motion_controller], [i for i in MOTION_CONTROLLERS if motion_controller not in i]
309+
)
310+
287311
def start_force_mode(self, req: SetForceMode.Request):
288312
return self.call_service(Services.start_force_mode, req)
289313

0 commit comments

Comments
 (0)