Skip to content

Commit e7d0517

Browse files
destogllivanov93
andauthored
Move test nodes from the ros2_control_demos repository. (#294)
Co-authored-by: Lovro Ivanov <[email protected]>
1 parent 427e86b commit e7d0517

File tree

9 files changed

+327
-0
lines changed

9 files changed

+327
-0
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ jobs:
4848
joint_trajectory_controller
4949
gripper_controllers
5050
position_controllers
51+
ros2_controllers_test_nodes
5152
velocity_controllers
5253
vcs-repo-file-url: |
5354
https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/.github/workspace.repos

.github/workflows/lint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
gripper_controllers
2828
position_controllers
2929
ros2_controllers
30+
ros2_controllers_test_nodes
3031
velocity_controllers
3132

3233
ament_lint_cpplint:
@@ -52,4 +53,5 @@ jobs:
5253
gripper_controllers
5354
position_controllers
5455
ros2_controllers
56+
ros2_controllers_test_nodes
5557
velocity_controllers
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>ros2_controllers_test_nodes</name>
5+
<version>0.0.0</version>
6+
<description>Demo nodes for showing and testing functionalities of the ros2_control framework.</description>
7+
8+
<maintainer email="[email protected]">Denis Štogl</maintainer>
9+
<maintainer email="[email protected]">Bence Magyar</maintainer>
10+
11+
<license>Apache-2.0</license>
12+
13+
<depend>rclpy</depend>
14+
<depend>std_msgs</depend>
15+
<depend>trajectory_msgs</depend>
16+
17+
<test_depend>python3-pytest</test_depend>
18+
19+
<export>
20+
<build_type>ament_python</build_type>
21+
</export>
22+
</package>

ros2_controllers_test_nodes/resource/ros2_controllers_test_nodes

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2022 Stogl Robotics Consulting UG (haftungsbeschränkt)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright 2022 Stogl Robotics Consulting UG (haftungsbeschränkt)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# Authors: Denis Štogl, Lovro Ivanov
16+
#
17+
18+
import rclpy
19+
from rclpy.node import Node
20+
21+
from std_msgs.msg import Float64MultiArray
22+
23+
24+
class PublisherForwardPosition(Node):
25+
def __init__(self):
26+
super().__init__("publisher_forward_position_controller")
27+
# Declare all parameters
28+
self.declare_parameter("controller_name", "forward_position_controller")
29+
self.declare_parameter("wait_sec_between_publish", 5)
30+
self.declare_parameter("goal_names", ["pos1", "pos2"])
31+
32+
# Read parameters
33+
controller_name = self.get_parameter("controller_name").value
34+
wait_sec_between_publish = self.get_parameter("wait_sec_between_publish").value
35+
goal_names = self.get_parameter("goal_names").value
36+
37+
# Read all positions from parameters
38+
self.goals = []
39+
for name in goal_names:
40+
self.declare_parameter(name)
41+
goal = self.get_parameter(name).value
42+
if goal is None or len(goal) == 0:
43+
raise Exception(f'Values for goal "{name}" not set!')
44+
45+
float_goal = [float(value) for value in goal]
46+
self.goals.append(float_goal)
47+
48+
publish_topic = "/" + controller_name + "/" + "commands"
49+
50+
self.get_logger().info(
51+
f'Publishing {len(goal_names)} goals on topic "{publish_topic}"\
52+
every {wait_sec_between_publish} s'
53+
)
54+
55+
self.publisher_ = self.create_publisher(Float64MultiArray, publish_topic, 1)
56+
self.timer = self.create_timer(wait_sec_between_publish, self.timer_callback)
57+
self.i = 0
58+
59+
def timer_callback(self):
60+
msg = Float64MultiArray()
61+
msg.data = self.goals[self.i]
62+
self.get_logger().info(f'Publishing: "{msg.data}"')
63+
self.publisher_.publish(msg)
64+
self.i += 1
65+
self.i %= len(self.goals)
66+
67+
68+
def main(args=None):
69+
rclpy.init(args=args)
70+
71+
publisher_forward_position = PublisherForwardPosition()
72+
73+
rclpy.spin(publisher_forward_position)
74+
publisher_forward_position.destroy_node()
75+
rclpy.shutdown()
76+
77+
78+
if __name__ == "__main__":
79+
main()
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Copyright 2022 Stogl Robotics Consulting UG (haftungsbeschränkt)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# Authors: Denis Štogl, Lovro Ivanov
16+
#
17+
18+
import rclpy
19+
from rclpy.node import Node
20+
from builtin_interfaces.msg import Duration
21+
22+
from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
23+
from sensor_msgs.msg import JointState
24+
25+
26+
class PublisherJointTrajectory(Node):
27+
def __init__(self):
28+
super().__init__("publisher_position_trajectory_controller")
29+
# Declare all parameters
30+
self.declare_parameter("controller_name", "position_trajectory_controller")
31+
self.declare_parameter("wait_sec_between_publish", 6)
32+
self.declare_parameter("goal_names", ["pos1", "pos2"])
33+
self.declare_parameter("joints")
34+
self.declare_parameter("check_starting_point", False)
35+
self.declare_parameter("starting_point_limits")
36+
37+
# Read parameters
38+
controller_name = self.get_parameter("controller_name").value
39+
wait_sec_between_publish = self.get_parameter("wait_sec_between_publish").value
40+
goal_names = self.get_parameter("goal_names").value
41+
self.joints = self.get_parameter("joints").value
42+
self.check_starting_point = self.get_parameter("check_starting_point").value
43+
self.starting_point = {}
44+
45+
if self.joints is None or len(self.joints) == 0:
46+
raise Exception('"joints" parameter is not set!')
47+
48+
# starting point stuff
49+
if self.check_starting_point:
50+
# declare nested params
51+
for name in self.joints:
52+
param_name_tmp = "starting_point_limits" + "." + name
53+
self.declare_parameter(param_name_tmp, [-2 * 3.14159, 2 * 3.14159])
54+
self.starting_point[name] = self.get_parameter(param_name_tmp).value
55+
56+
for name in self.joints:
57+
if len(self.starting_point[name]) != 2:
58+
raise Exception('"starting_point" parameter is not set correctly!')
59+
self.joint_state_sub = self.create_subscription(
60+
JointState, "joint_states", self.joint_state_callback, 10
61+
)
62+
# initialize starting point status
63+
self.starting_point_ok = not self.check_starting_point
64+
65+
self.joint_state_msg_received = False
66+
67+
# Read all positions from parameters
68+
self.goals = []
69+
for name in goal_names:
70+
self.declare_parameter(name)
71+
goal = self.get_parameter(name).value
72+
if goal is None or len(goal) == 0:
73+
raise Exception(f'Values for goal "{name}" not set!')
74+
75+
float_goal = []
76+
for value in goal:
77+
float_goal.append(float(value))
78+
self.goals.append(float_goal)
79+
80+
publish_topic = "/" + controller_name + "/" + "joint_trajectory"
81+
82+
self.get_logger().info(
83+
'Publishing {} goals on topic "{}" every {} s'.format(
84+
len(goal_names), publish_topic, wait_sec_between_publish
85+
)
86+
)
87+
88+
self.publisher_ = self.create_publisher(JointTrajectory, publish_topic, 1)
89+
self.timer = self.create_timer(wait_sec_between_publish, self.timer_callback)
90+
self.i = 0
91+
92+
def timer_callback(self):
93+
94+
if self.starting_point_ok:
95+
96+
traj = JointTrajectory()
97+
traj.joint_names = self.joints
98+
point = JointTrajectoryPoint()
99+
point.positions = self.goals[self.i]
100+
point.time_from_start = Duration(sec=4)
101+
102+
traj.points.append(point)
103+
self.publisher_.publish(traj)
104+
105+
self.i += 1
106+
self.i %= len(self.goals)
107+
108+
elif self.check_starting_point and not self.joint_state_msg_received:
109+
self.get_logger().warn(
110+
'Start configuration could not be checked! Check "joint_state" topic!'
111+
)
112+
else:
113+
self.get_logger().warn("Start configuration is not within configured limits!")
114+
115+
def joint_state_callback(self, msg):
116+
117+
if not self.joint_state_msg_received:
118+
119+
# check start state
120+
limit_exceeded = [False] * len(msg.name)
121+
for idx, enum in enumerate(msg.name):
122+
if (msg.position[idx] < self.starting_point[enum][0]) or (
123+
msg.position[idx] > self.starting_point[enum][1]
124+
):
125+
self.get_logger().warn(f"Starting point limits exceeded for joint {enum} !")
126+
limit_exceeded[idx] = True
127+
128+
if any(limit_exceeded):
129+
self.starting_point_ok = False
130+
else:
131+
self.starting_point_ok = True
132+
133+
self.joint_state_msg_received = True
134+
else:
135+
return
136+
137+
138+
def main(args=None):
139+
rclpy.init(args=args)
140+
141+
publisher_joint_trajectory = PublisherJointTrajectory()
142+
143+
rclpy.spin(publisher_joint_trajectory)
144+
publisher_joint_trajectory.destroy_node()
145+
rclpy.shutdown()
146+
147+
148+
if __name__ == "__main__":
149+
main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[develop]
2+
script-dir=$base/lib/ros2_controllers_test_nodes
3+
[install]
4+
install-scripts=$base/lib/ros2_controllers_test_nodes
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright 2022 Stogl Robotics Consulting UG (haftungsbeschränkt)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from glob import glob
16+
17+
from setuptools import setup
18+
19+
package_name = "ros2_controllers_test_nodes"
20+
21+
setup(
22+
name=package_name,
23+
version="0.0.1",
24+
packages=[package_name],
25+
data_files=[
26+
("share/ament_index/resource_index/packages", ["resource/" + package_name]),
27+
("share/" + package_name, ["package.xml"]),
28+
("share/" + package_name, glob("launch/*.launch.py")),
29+
("share/" + package_name + "/configs", glob("configs/*.*")),
30+
],
31+
install_requires=["setuptools"],
32+
zip_safe=True,
33+
author="Denis Štogl",
34+
author_email="[email protected]",
35+
maintainer="Denis Štogl",
36+
maintainer_email="[email protected]",
37+
keywords=["ROS"],
38+
classifiers=[
39+
"Intended Audience :: Developers",
40+
"License :: OSI Approved :: Apache Software License",
41+
"Programming Language :: Python",
42+
"Topic :: Software Development",
43+
],
44+
description="Demo nodes for showing and testing functionalities of ros2_control framework.",
45+
long_description="""\
46+
Demo nodes for showing and testing functionalities of the ros2_control framework.""",
47+
license="Apache License, Version 2.0",
48+
tests_require=["pytest"],
49+
entry_points={
50+
"console_scripts": [
51+
"publisher_forward_position_controller = \
52+
ros2_controllers_test_nodes.publisher_forward_position_controller:main",
53+
"publisher_joint_trajectory_controller = \
54+
ros2_controllers_test_nodes.publisher_joint_trajectory_controller:main",
55+
],
56+
},
57+
)

0 commit comments

Comments
 (0)