agenticros 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,223 @@
1
+ """sim_arm.launch.py
2
+
3
+ Bring up the AgenticROS 6-DOF arm in Gazebo (Fortress / ign-gazebo 6 on
4
+ Humble Jetson, or Harmonic / gz-sim 8 on Jazzy) with full ROS-side
5
+ plumbing:
6
+ * gz sim agenticros_indoor.sdf (the world)
7
+ * ros_gz_sim create (spawn the arm)
8
+ * ros_gz_bridge parameter_bridge (gz <-> ROS topics)
9
+ * robot_state_publisher (URDF mirror) (for RViz RobotModel)
10
+ * Optional RViz (--rviz launch arg)
11
+
12
+ Joint commands are exposed as std_msgs/Float64 (radians) on:
13
+ /arm/shoulder_pan/cmd_pos
14
+ /arm/shoulder_lift/cmd_pos
15
+ /arm/elbow/cmd_pos
16
+ /arm/wrist_1/cmd_pos
17
+ /arm/wrist_2/cmd_pos
18
+ /arm/wrist_3/cmd_pos
19
+
20
+ Examples:
21
+ ros2 launch agenticros_sim sim_arm.launch.py
22
+ ros2 launch agenticros_sim sim_arm.launch.py use_rviz:=true
23
+ ros2 launch agenticros_sim sim_arm.launch.py gui:=false # headless
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import os
29
+
30
+ from ament_index_python.packages import get_package_share_directory
31
+ from launch import LaunchContext, LaunchDescription
32
+ from launch.actions import (
33
+ DeclareLaunchArgument,
34
+ ExecuteProcess,
35
+ IncludeLaunchDescription,
36
+ OpaqueFunction,
37
+ TimerAction,
38
+ )
39
+ from launch.conditions import IfCondition
40
+ from launch.launch_description_sources import PythonLaunchDescriptionSource
41
+ from launch.substitutions import Command, LaunchConfiguration, PathJoinSubstitution
42
+ from launch_ros.actions import Node
43
+ from launch_ros.parameter_descriptions import ParameterValue
44
+ from launch_ros.substitutions import FindPackageShare
45
+
46
+
47
+ PKG_NAME = "agenticros_sim"
48
+
49
+ # Default "home" pose, published once after spawn so the arm settles in a
50
+ # stable known configuration instead of whatever gravity happens to find:
51
+ # shoulder_pan = 0 (forward)
52
+ # shoulder_lift = -pi/2 (upper arm up)
53
+ # elbow = pi/2 (forearm horizontal, forming an L)
54
+ # wrist_* = 0
55
+ HOME_POSE = {
56
+ "shoulder_pan": 0.0,
57
+ "shoulder_lift": -1.5707963,
58
+ "elbow": 1.5707963,
59
+ "wrist_1": 0.0,
60
+ "wrist_2": 0.0,
61
+ "wrist_3": 0.0,
62
+ }
63
+
64
+
65
+ def generate_launch_description() -> LaunchDescription:
66
+ pkg_share = get_package_share_directory(PKG_NAME)
67
+ default_world = os.path.join(pkg_share, "worlds", "agenticros_indoor.sdf")
68
+ default_bridge = os.path.join(pkg_share, "config", "arm_bridge.yaml")
69
+ default_rviz = os.path.join(pkg_share, "config", "arm_view.rviz")
70
+ model_sdf = os.path.join(pkg_share, "models", "agenticros_arm", "model.sdf")
71
+ urdf_xacro = os.path.join(pkg_share, "urdf", "agenticros_arm.urdf.xacro")
72
+
73
+ world_arg = DeclareLaunchArgument(
74
+ "world", default_value=default_world,
75
+ description="SDF world file to load (absolute path).",
76
+ )
77
+ use_rviz_arg = DeclareLaunchArgument(
78
+ "use_rviz", default_value="false",
79
+ description="Open RViz with the arm view config.",
80
+ )
81
+ use_sim_time_arg = DeclareLaunchArgument(
82
+ "use_sim_time", default_value="true",
83
+ description="Tell ROS nodes to use /clock published by gz.",
84
+ )
85
+ # Spawn the arm well away from the AMR spawn point (which is 0,0) so users
86
+ # running the indoor world can see both side by side if curious.
87
+ x_arg = DeclareLaunchArgument("x", default_value="3.0", description="Arm spawn X (m)")
88
+ y_arg = DeclareLaunchArgument("y", default_value="0.0", description="Arm spawn Y (m)")
89
+ z_arg = DeclareLaunchArgument("z", default_value="0.0", description="Arm spawn Z (m)")
90
+ yaw_arg = DeclareLaunchArgument("yaw", default_value="0.0", description="Arm yaw (rad)")
91
+ bridge_arg = DeclareLaunchArgument(
92
+ "bridge_config_file", default_value=default_bridge,
93
+ description="ros_gz_bridge parameter_bridge config YAML.",
94
+ )
95
+ gui_arg = DeclareLaunchArgument(
96
+ "gui", default_value="true",
97
+ description="If 'false', run gz-sim headless (-s -r).",
98
+ )
99
+ rviz_config_arg = DeclareLaunchArgument(
100
+ "rviz_config", default_value=default_rviz,
101
+ description="RViz config path (used when use_rviz is true).",
102
+ )
103
+
104
+ # ---------- Spawn the arm ----------
105
+ spawn_arm = Node(
106
+ package="ros_gz_sim",
107
+ executable="create",
108
+ name="spawn_agenticros_arm",
109
+ output="screen",
110
+ arguments=[
111
+ "-name", "agenticros_arm",
112
+ "-file", model_sdf,
113
+ "-x", LaunchConfiguration("x"),
114
+ "-y", LaunchConfiguration("y"),
115
+ "-z", LaunchConfiguration("z"),
116
+ "-Y", LaunchConfiguration("yaw"),
117
+ ],
118
+ )
119
+
120
+ # ---------- gz <-> ROS bridge ----------
121
+ bridge = Node(
122
+ package="ros_gz_bridge",
123
+ executable="parameter_bridge",
124
+ name="agenticros_arm_bridge",
125
+ output="screen",
126
+ parameters=[{
127
+ "config_file": LaunchConfiguration("bridge_config_file"),
128
+ "qos_overrides./tf_static.publisher.durability": "transient_local",
129
+ "use_sim_time": LaunchConfiguration("use_sim_time"),
130
+ }],
131
+ )
132
+
133
+ # ---------- robot_state_publisher (URDF mirror of the SDF) ----------
134
+ # Without this, RViz only has TF axes and won't draw the arm. The URDF is
135
+ # purely for visualization; physics, joint controllers, and gravity all
136
+ # live in the SDF.
137
+ # NOTE: `Command(... on_stderr='ignore')` keeps xacro's "redefining global
138
+ # symbol: pi" warnings from aborting the launch.
139
+ robot_description = ParameterValue(
140
+ Command(["xacro ", urdf_xacro], on_stderr="ignore"), value_type=str,
141
+ )
142
+ rsp = Node(
143
+ package="robot_state_publisher",
144
+ executable="robot_state_publisher",
145
+ name="robot_state_publisher",
146
+ output="screen",
147
+ parameters=[{
148
+ "robot_description": robot_description,
149
+ "use_sim_time": LaunchConfiguration("use_sim_time"),
150
+ }],
151
+ )
152
+
153
+ # ---------- Redundant home-pose publisher ----------
154
+ # The SDF already sets <initial_position> on the joints and the
155
+ # JointPositionController plugins so the arm spawns at home pose and
156
+ # the controllers hold it there immediately. This timer is a safety
157
+ # net: if a user `gz service`-spawned a fresh arm from a controller
158
+ # config that didn't include initial_position, this still gets it
159
+ # into a sane pose.
160
+ #
161
+ # `ros2 topic pub --once` is unreliable: the publisher exits before
162
+ # discovery completes, so the bridge often drops the message.
163
+ # `--times 5 --rate 5` publishes 5 copies over 1 s, which is enough
164
+ # for the bridge subscription to come up and consume at least one.
165
+ home_publishers = [
166
+ ExecuteProcess(
167
+ cmd=[
168
+ "ros2", "topic", "pub", "--times", "5", "--rate", "5",
169
+ f"/arm/{joint}/cmd_pos", "std_msgs/msg/Float64",
170
+ f"{{data: {angle}}}",
171
+ ],
172
+ output="log",
173
+ )
174
+ for joint, angle in HOME_POSE.items()
175
+ ]
176
+ home_pose_action = TimerAction(period=8.0, actions=home_publishers)
177
+
178
+ # ---------- Optional RViz ----------
179
+ rviz = Node(
180
+ package="rviz2",
181
+ executable="rviz2",
182
+ name="rviz2",
183
+ output="screen",
184
+ condition=IfCondition(LaunchConfiguration("use_rviz")),
185
+ arguments=["-d", LaunchConfiguration("rviz_config")],
186
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
187
+ )
188
+
189
+ return LaunchDescription([
190
+ world_arg,
191
+ use_rviz_arg,
192
+ use_sim_time_arg,
193
+ x_arg, y_arg, z_arg, yaw_arg,
194
+ bridge_arg,
195
+ gui_arg,
196
+ rviz_config_arg,
197
+ OpaqueFunction(function=_launch_gz_sim),
198
+ spawn_arm,
199
+ bridge,
200
+ rsp,
201
+ home_pose_action,
202
+ rviz,
203
+ ])
204
+
205
+
206
+ def _launch_gz_sim(context: LaunchContext, *_, **__):
207
+ """Resolve launch args and emit the gz_sim IncludeLaunchDescription action."""
208
+ gui = context.launch_configurations.get("gui", "true").lower() == "true"
209
+ world = context.launch_configurations.get(
210
+ "world",
211
+ os.path.join(get_package_share_directory(PKG_NAME), "worlds", "agenticros_indoor.sdf"),
212
+ )
213
+ gz_args = f"-r {world}" if gui else f"-r -s --headless-rendering {world}"
214
+
215
+ gz_launch = IncludeLaunchDescription(
216
+ PythonLaunchDescriptionSource(
217
+ PathJoinSubstitution(
218
+ [FindPackageShare("ros_gz_sim"), "launch", "gz_sim.launch.py"]
219
+ )
220
+ ),
221
+ launch_arguments=[("gz_args", gz_args)],
222
+ )
223
+ return [gz_launch]
@@ -20,7 +20,7 @@
20
20
  lidar scan → /scan
21
21
  imu → /imu/data
22
22
  -->
23
- <sdf version="1.10">
23
+ <sdf version="1.9">
24
24
  <model name="agenticros_amr">
25
25
 
26
26
  <!-- ===================== Chassis ===================== -->
@@ -0,0 +1,26 @@
1
+ <?xml version="1.0"?>
2
+ <model>
3
+ <name>agenticros_arm</name>
4
+ <version>1.0</version>
5
+ <sdf version="1.10">model.sdf</sdf>
6
+ <author>
7
+ <name>AgenticROS</name>
8
+ <email>hello@agenticros.dev</email>
9
+ </author>
10
+ <description>
11
+ Six-DOF UR5e-shaped robotic arm for the AgenticROS Phase 3 simulation.
12
+ Approximates the UR5e joint topology (shoulder_pan, shoulder_lift, elbow,
13
+ wrist_1, wrist_2, wrist_3 + tool0 end-effector frame) using simple
14
+ cylindrical link geometry, gz-sim JointPositionController plugins per
15
+ joint, and PD gains tuned to hold the arm against gravity without
16
+ ros2_control or MoveIt2 in the loop.
17
+
18
+ Topic surface (Gazebo side; ros_gz_bridge re-emits as ROS topics):
19
+ /arm/JOINT/cmd_pos type=gz.msgs.Double ROS_TO_GZ
20
+ /joint_states type=gz.msgs.Model GZ_TO_ROS
21
+ /model/agenticros_arm/pose type=gz.msgs.Pose_V GZ_TO_ROS (to /tf)
22
+
23
+ Send a single joint angle as ROS std_msgs/Float64 (radians) to any
24
+ /arm/JOINT/cmd_pos topic and the controller drives the joint there.
25
+ </description>
26
+ </model>