Skip to content

Commit 3c7eaea

Browse files
XuRoboticsclaude
andcommitted
state_machine + action_planner: port 9 scripts/ Python files from rospy to rclpy
The per-package subagents focused on the C++ side and missed the 9 Python scripts living under action_planner/scripts/ and state_machine_core/scripts/ that still imported rospy and actionlib. These are dev tools (trajectory evaluators, a goal publisher) and the main state-machine orchestrator, none of which are installed by CMakeLists. Port them to rclpy under a separate subagent pass. Scripts ported (9 files, ~1886 source lines): action_planner/scripts/: - publish_plantwopointaction.py: rclpy.create_node + rclpy.action. ActionClient(PlanTwoPoint) replacing actionlib.SimpleActionClient and the PlanTwoPointActionGoal publisher. send_goal_async + spin_until_future_complete flow. - evaluate_traj.py: Evaluater(node) class with 3 ActionClient migrations. Local variable `time` renamed to `time_total` to avoid shadowing the time module (pre-existing latent bug). - evaluate_traj_exp.py: 10 planner ActionClient migrations. Every ServiceProxy -> create_client + call_async + spin_until_future_complete. rospy.get_param_names() has no rclpy equivalent; a best-effort fallback using the node's internal parameter map is flagged with inline TODOs. state_machine_core/scripts/: - main_state_machine.py: rclpy.init + create_node + spin loop wrapper. rospy.names.get_namespace() -> node.get_namespace() with trailing- slash normalization. QuadTracker now constructed with (node, ...) first arg. smach ecosystem is ROS1-only; top-of-file TODO note preserved so the smach state graph itself still works when a ROS2 smach port (smach_ros2 / yasmin) becomes available. - QuadTracker.py: constructor signature changed to (self, node, abort_topic). 5 rospy.Subscriber calls -> node.create_subscription. tf.TransformListener -> tf2_ros.Buffer + TransformListener. tf.transformations.euler_from_quaternion -> tf_transformations. euler_from_quaternion. - MainStates.py: _node(quad_monitor) helper pulls the rclpy node off the QuadTracker. Deferred publisher/client creation in PublishBoolMsgState and ArmDisarmMavros because they inherit from smach.State (no __init__ Node access) — main_state_machine.py wires `state.quad_monitor = quad_tracker` after constructing them. rospy.ServiceProxy -> create_client + async call pattern. WaitState.__init__ param renamed `time` -> `time_sec` to avoid shadowing the imported time module. - Replanner.py: rospy.sleep -> time.sleep. smach_ros.SimpleActionState wrapper preserved as-is with a TODO note; the wrapper has no ROS2 equivalent and the class will need to be reimplemented when smach_ros2 lands. - SwitchState.py: rospy.Subscriber -> quad_monitor.node.create_subscription. roslib.load_manifest dropped (ROS1-only). - Utils.py: tf.Transformer (ROS1-only) -> direct 4x4 matrix math via tf_transformations.concatenate_matrices + inverse_matrix. Pure helper module, no node reference. Known limitations (documented inline in each file): - `smach` itself is ROS1-only. Every SMACH state file has a top-of-file TODO note saying the state machine graph must be re-implemented once smach_ros2 or yasmin is available. The rospy -> rclpy translation below the TODO is best-effort and correct in isolation. - Absolute parameter paths like /mapper/global/origin_x and /local_plan_server0/mav_name cannot be read directly in ROS2 (parameters are per-node). The slash-named declare_parameter calls are preserved as best-effort mechanical translations; at runtime these should use rclpy.parameter_client to read from the target node, or the value should be passed in at launch. - smach_ros.SimpleActionState and smach_ros.ServiceState wrappers are used in Replanner.py and MainStates.py. These don't exist in ROS2. Deferred to the smach_ros2 / yasmin follow-up. None of these scripts are installed by any CMakeLists.txt, so this commit does not affect colcon build. It does bring the tree into a state where grep -rn 'import rospy' returns zero hits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 01a1e0a commit 3c7eaea

9 files changed

Lines changed: 518 additions & 289 deletions

File tree

autonomy_core/map_plan/action_planner/scripts/evaluate_traj.py

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
#!/usr/bin/env python3
2-
import rospy
2+
import time
3+
import rclpy
4+
from rclpy.node import Node
5+
from rclpy.action import ActionClient
36
# from kr_planning_msgs.msg import SplineTrajectory
4-
from kr_planning_msgs.msg import PlanTwoPointAction, PlanTwoPointGoal
7+
from kr_planning_msgs.action import PlanTwoPoint
8+
from rclpy.duration import Duration
59
import numpy as np
610
# import matplotlib.pyplot as plt
711
import pandas as pd
812
from copy import deepcopy
913
from visualization_msgs.msg import MarkerArray, Marker
10-
from actionlib import SimpleActionClient
1114
from std_srvs.srv import Empty
1215

1316
# filename = '/home/laura/autonomy_ws/src/kr_autonomous_flight/autonomy_core/map_plan/action_planner/scripts/map_balls_start_goal.csv'
@@ -42,16 +45,17 @@ def evaluate(msg, t, deriv_num):
4245

4346

4447
class Evaluater:
45-
def __init__(self):
48+
def __init__(self, node):
49+
self.node = node
4650
# print("reading "+filename)
4751
# self.start_goals = pd.read_csv(filename)
48-
# self.path_pub = rospy.Publisher('/local_plan_server/plan_local_trajectory/goal', PlanTwoPointActionGoal, queue_size=10, latch=True)
49-
self.client = SimpleActionClient('/local_plan_server/plan_local_trajectory', PlanTwoPointAction)
50-
self.client2 = SimpleActionClient('/local_plan_server2/plan_local_trajectory', PlanTwoPointAction)
51-
self.client3 = SimpleActionClient('/local_plan_server3/plan_local_trajectory', PlanTwoPointAction)
52+
# self.path_pub = node.create_publisher(PlanTwoPointActionGoal, '/local_plan_server/plan_local_trajectory/goal', 10)
53+
self.client = ActionClient(node, PlanTwoPoint, '/local_plan_server/plan_local_trajectory')
54+
self.client2 = ActionClient(node, PlanTwoPoint, '/local_plan_server2/plan_local_trajectory')
55+
self.client3 = ActionClient(node, PlanTwoPoint, '/local_plan_server3/plan_local_trajectory')
5256

53-
self.start_and_goal_pub = rospy.Publisher('/start_and_goal', MarkerArray, queue_size=10, latch=True)
54-
# rospy.Subscriber("/local_plan_server/trajectory", SplineTrajectory, self.callback)
57+
self.start_and_goal_pub = node.create_publisher(MarkerArray, '/start_and_goal', 10)
58+
# node.create_subscription(SplineTrajectory, "/local_plan_server/trajectory", self.callback, 10)
5559
self.num_trials = 20
5660
self.success = np.zeros(self.num_trials, dtype=bool)
5761
self.traj_time = np.zeros(self.num_trials)
@@ -75,28 +79,29 @@ def computeJerk(self, traj):
7579
t_vec = np.append(t_vec, t)
7680
jerk_sq = np.append(jerk_sq, (np.linalg.norm(evaluate(traj, t, 3)))**2 )
7781
return np.sqrt(np.trapz(jerk_sq, t_vec))/traj.data[0].t_total
78-
82+
7983

8084
def computeCost(self, traj, rho):
81-
time = traj.data[0].t_total
82-
cost = rho*time + self.computeJerk(traj)
85+
time_total = traj.data[0].t_total
86+
cost = rho*time_total + self.computeJerk(traj)
8387
return cost
8488

8589
def publisher(self):
8690
print("waiting for map server")
87-
rospy.wait_for_service('/image_to_map')
88-
map_client = rospy.ServiceProxy('/image_to_map', Empty)
89-
map_client()
91+
map_client = self.node.create_client(Empty, '/image_to_map')
92+
while not map_client.wait_for_service(timeout_sec=1.0):
93+
self.node.get_logger().info("Waiting for /image_to_map service...")
94+
map_client.call_async(Empty.Request())
9095

9196
print("waiting for action server")
9297
self.client.wait_for_server()
9398

9499
# for i in range(self.start_goals.shape[0]):
95-
# if rospy.is_shutdown():
100+
# if not rclpy.ok():
96101
# break
97102

98103
# print(i)
99-
# msg = PlanTwoPointGoal()
104+
# msg = PlanTwoPoint.Goal()
100105
# msg.p_init.position.x = self.start_goals['xi'][i]
101106
# msg.p_init.position.y = self.start_goals['yi'][i]
102107
# msg.p_init.position.z = 5
@@ -108,21 +113,21 @@ def publisher(self):
108113

109114
for i in range(self.num_trials):
110115
print(i)
111-
if rospy.is_shutdown():
116+
if not rclpy.ok():
112117
break
113118
if (i > 0):
114-
map_client()
119+
map_client.call_async(Empty.Request())
115120
#TODO(Laura): actually send map ?
116-
rospy.sleep(2)
117-
msg = PlanTwoPointGoal()
121+
time.sleep(2)
122+
msg = PlanTwoPoint.Goal()
118123
msg.p_init.position.x = 1.25
119124
msg.p_init.position.y = 1.25
120-
msg.p_init.position.z = 5
125+
msg.p_init.position.z = 5.0
121126
# msg.v_init.linear.x = 2
122127
# msg.v_init.linear.y = 2
123128
msg.p_final.position.x = 19.5
124129
msg.p_final.position.y = 10 - 1.25
125-
msg.p_final.position.z = 5
130+
msg.p_final.position.z = 5.0
126131

127132

128133

@@ -131,31 +136,36 @@ def publisher(self):
131136
start_and_goal = MarkerArray()
132137
start = Marker()
133138
start.header.frame_id = "map"
134-
start.header.stamp = rospy.Time.now()
139+
start.header.stamp = self.node.get_clock().now().to_msg()
135140
start.pose.position = msg.p_init.position
136-
start.pose.orientation.w = 1
137-
start.color.g = 1
138-
start.color.a = 1
141+
start.pose.orientation.w = 1.0
142+
start.color.g = 1.0
143+
start.color.a = 1.0
139144
start.type = 2
140-
start.scale.x = start.scale.y = start.scale.z = 1
145+
start.scale.x = start.scale.y = start.scale.z = 1.0
141146
goal = deepcopy(start)
142147
goal.pose.position = msg.p_final.position
143148
goal.id = 1
144-
goal.color.r = 1
145-
goal.color.g = 0
149+
goal.color.r = 1.0
150+
goal.color.g = 0.0
146151
start_and_goal.markers.append(start)
147152
start_and_goal.markers.append(goal)
148153
# self.path_pub.publish(msg)
149154
self.start_and_goal_pub.publish(start_and_goal)
150-
self.client.send_goal(msg)
155+
156+
# Action client goal send + wait (was self.client.send_goal(msg) + wait_for_result)
157+
send_goal_future = self.client.send_goal_async(msg)
158+
rclpy.spin_until_future_complete(self.node, send_goal_future)
159+
goal_handle = send_goal_future.result()
160+
result = None
161+
if goal_handle is not None and goal_handle.accepted:
162+
get_result_future = goal_handle.get_result_async()
163+
rclpy.spin_until_future_complete(self.node, get_result_future, timeout_sec=3.0)
164+
if get_result_future.done():
165+
result = get_result_future.result().result
151166
# self.client2.send_goal(msg)
152167
# self.client3.send_goal(msg)
153-
# Waits for the server to finish performing the action.
154-
self.client.wait_for_result(rospy.Duration.from_sec(3.0))
155-
# self.client2.wait_for_result(rospy.Duration.from_sec(5.0))
156-
# self.client3.wait_for_result(rospy.Duration.from_sec(5.0))
157168

158-
result = self.client.get_result()
159169
#TODO(Laura) check if the path is collision free and feasible
160170
if result:
161171
self.success[i] = result.success
@@ -190,15 +200,18 @@ def publisher(self):
190200

191201

192202
def subscriber():
193-
rospy.init_node('evaluate_traj')
194-
Evaluater()
203+
rclpy.init()
204+
node = rclpy.create_node('evaluate_traj')
205+
Evaluater(node)
195206

196207
# spin() simply keeps python from exiting until this node is stopped
197-
# rospy.spin()
208+
# rclpy.spin(node)
209+
node.destroy_node()
210+
rclpy.shutdown()
198211

199212

200213
if __name__ == '__main__':
201214
try:
202215
subscriber()
203-
except rospy.ROSInterruptException:
216+
except KeyboardInterrupt:
204217
pass

0 commit comments

Comments
 (0)