Skip to content

Commit 55d9b90

Browse files
authored
Merge pull request #181 from DoernerD/planner
Planner
2 parents 524fc75 + 2fce25b commit 55d9b90

File tree

23 files changed

+1242
-234
lines changed

23 files changed

+1242
-234
lines changed

behaviours/go_to_hydrobaticpoint/go_to_hydrobaticpoint/hydrobaticpoint_action.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ def decode(
3535
hydropoint = PoseStamped()
3636
hydropoint.header.frame_id = str(fmt_dict["hydropoint"]["frame_id"])
3737
hydropoint.pose.position.x = float(fmt_dict["hydropoint"]["position"]["x"])
38-
hydropoint.pose.position.x = float(fmt_dict["hydropoint"]["position"]["y"])
39-
hydropoint.pose.position.x = float(fmt_dict["hydropoint"]["position"]["z"])
38+
hydropoint.pose.position.y = float(fmt_dict["hydropoint"]["position"]["y"])
39+
hydropoint.pose.position.z = float(fmt_dict["hydropoint"]["position"]["z"])
4040
hydropoint.pose.orientation.x = float(fmt_dict["hydropoint"]["orientation"]["x"])
4141
hydropoint.pose.orientation.y = float(fmt_dict["hydropoint"]["orientation"]["y"])
4242
hydropoint.pose.orientation.z = float(fmt_dict["hydropoint"]["orientation"]["z"])

behaviours/sam_diving_controller/config/sam_diving_controller_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
lcg_pid_ki: 5.0
1616
lcg_pid_kd: 1.0
1717
lcg_pid_kaw: 1.0
18-
lcg_u_neutral: 75.0
18+
lcg_u_neutral: 50.0
1919
lcg_u_min: 0.0
2020
lcg_u_max: 100.0
2121
tv_pid_kp: 1.0
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<launch>
2+
3+
<arg name="robot_name" default="sam0"/>
4+
<arg name="tf_suffix" default=""/>
5+
<arg name="control_yaml" default="$(find-pkg-share sam_diving_controller)/config/sam_diving_controller_config.yaml"/>
6+
7+
<group>
8+
<push-ros-namespace namespace="$(var robot_name)"/>
9+
<node name="diving_node" pkg="sam_diving_controller" exec="mpc_trajectory_tracking" output="screen">
10+
<param name="robot_name" value="$(var robot_name)"/>
11+
<param name="tf_suffix" value="$(var tf_suffix)"/>
12+
<param from="$(var control_yaml)"/>
13+
</node>
14+
</group>
15+
16+
</launch>

behaviours/sam_diving_controller/sam_diving_controller/ActionServerDiveSub.py

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import json
55
import threading
66

7+
import numpy as np
8+
79
from rclpy.node import Node
810
from rclpy.action import ActionServer, CancelResponse, GoalResponse
911
from rclpy.action.server import ServerGoalHandle
@@ -28,6 +30,7 @@
2830
from action_msgs.srv import CancelGoal
2931

3032
# ROS Imports
33+
import rclpy
3134
from rclpy.action import ActionClient, ActionServer, CancelResponse, GoalResponse
3235
from rclpy.action.client import ClientGoalHandle
3336
from rclpy.action.server import ServerGoalHandle
@@ -46,6 +49,8 @@
4649
SMARCActionServer,
4750
)
4851

52+
from sam_path_following.path_action import ActionComponent as ActC
53+
from sam_path_following.path_action import PathAction
4954

5055
try:
5156
from .DiveSub import DiveSub
@@ -231,3 +236,203 @@ def cancel_callback(self, goal_handle) -> CancelResponse:
231236
return CancelResponse.ACCEPT
232237

233238

239+
class PathServer(SMARCActionServer, DiveSub):
240+
"""Action point server that handle GotoGeopoint messages.
241+
242+
Attributes:
243+
logger: shorthand for `node.get_logger()`
244+
robot_name: provided robot name from launch file
245+
target_frame: frame that goal's should be transformed to
246+
"""
247+
248+
def __init__(self, node: Node, action_name, action_type: ActionType, param):
249+
250+
self.param = param
251+
self._node = node
252+
253+
SMARCActionServer.__init__(self,
254+
node,
255+
action_name,
256+
action_type,
257+
SMaRCTopics.WARA_PS_ACTION_SERVER_HB_TOPIC,
258+
)
259+
DiveSub.__init__(self,self._node, self.param)
260+
261+
self.logger = node.get_logger()
262+
263+
self.path = None
264+
self.path_len = None
265+
self.current_idx = 0
266+
267+
#self.declare_parameters()
268+
269+
self.logger.set_level(rclpy.logging.LoggingSeverity.INFO)
270+
271+
self._json_ops: PathAction = PathAction()
272+
273+
self._loginfo("Path Action Server started")
274+
275+
276+
# TODO: Cancel process. Need to stop the controller as well, similar to the wp action server.
277+
278+
# def declare_parameters(self):
279+
# """Declares all of node's parameters in a single location."""
280+
#
281+
# # TODO: Which ones are actually needed?
282+
#
283+
# node = self._node
284+
# self.robot_name = node.declare_parameter("robot_name", "Quadrotor").value
285+
# self._target_frame_param = node.declare_parameter("target_frame", "odom").value
286+
#
287+
# self._distance_frame_param = node.declare_parameter(
288+
# "distance_frame",
289+
# "base_link",
290+
# ParameterDescriptor(
291+
# description="Frame for which the distance to target will be computed (usually base_link)"
292+
# ),
293+
# ).value
294+
#
295+
# self._distance_frame_suffix = node.declare_parameter(
296+
# "distance_frame_suffix",
297+
# "_gt",
298+
# ParameterDescriptor(
299+
# description="Frame suffix for distance frame. Commonly is '_gt' for ground truth if applicable"
300+
# ),
301+
# ).value
302+
#
303+
# self._frame_suffix = node.declare_parameter(
304+
# "frame_suffix",
305+
# "_gt",
306+
# ParameterDescriptor(
307+
# description="Frame suffix for transform. Commonly is '_gt' for ground truth if applicable"
308+
# ),
309+
# ).value
310+
#
311+
# self._setpoint_tol: float = node.declare_parameter(
312+
# "setpoint_tolerance",
313+
# 0.25,
314+
# ParameterDescriptor(
315+
# description="Setpoint tolerance for when the goal is considered achieved (Euclidean norm)."
316+
# ),
317+
# ).value
318+
#
319+
# # self._setpoint_topic = node.declare_parameter(
320+
# # "setpoint_topic",
321+
# # "go_to_setpoint",
322+
# # ParameterDescriptor(
323+
# # description="Topic to publish setpoint targets to. Will be prepended with 'robot_name'"
324+
# # ),
325+
# # ).value
326+
#
327+
# self._goal_threshold = (
328+
# node.declare_parameter(
329+
# "goal_threshold",
330+
# 10,
331+
# ParameterDescriptor(
332+
# description="Distance threshold in meters where a goal should be rejected. (Euclidean Norm)"
333+
# ),
334+
# ).value
335+
# )
336+
#
337+
# self.target_frame = (
338+
# f"{self.robot_name}/{self._target_frame_param}{self._frame_suffix}"
339+
# )
340+
# self.logger.info(f"Target frame {self.target_frame}")
341+
#
342+
# self.distance_frame = f"{self.robot_name}/{self._distance_frame_param}{self._distance_frame_suffix}"
343+
# self.logger.info(f"Distance frame {self.distance_frame}")
344+
345+
def _save_path(self, path):
346+
"""
347+
Convert path from list to numpy array.
348+
"""
349+
self.path = np.asarray(path)
350+
self.logger.info(f"AS: saved path")
351+
352+
353+
def goal_callback(self, goal_request: ActionType.Goal) -> GoalResponse:
354+
"""Considers a goal validity and evaluates whether it should be accepted or not.
355+
356+
Args:
357+
goal_request (ActionType.Goal): Goal message
358+
359+
Returns:
360+
response: Either GoalResponse.Accept or GoalResponse.Reject
361+
362+
"""
363+
# TODO: Think of whether you want to reject any goal. For now, accept
364+
# everything, as we assume the planner to know what it's doing.
365+
goal_request = goal_request.goal
366+
path = self._json_ops.decode(goal_request, ActC.GOAL)
367+
self.logger.info(f"Recieved path")
368+
self._save_path(path)
369+
self.path_len = len(self.path) # TODO: Check which index to use, 0 or 1
370+
371+
# Accepts as all criteria fulfilled
372+
return GoalResponse.ACCEPT
373+
374+
375+
def execution_callback(self, goal_handle: ServerGoalHandle) -> ActionResult:
376+
"""Primary execution callback where goal's are handled after acceptance.
377+
378+
Args:
379+
goal_handle: handle to control server and add callbacks
380+
381+
Returns:
382+
A populated ActionResult message
383+
"""
384+
result_msg = self.action_type.Result
385+
status = self.feedback_loop(goal_handle)
386+
if status == "cancelled":
387+
self.logger.info("Goal was cancelled by client.")
388+
result_msg.success = False
389+
return result_msg
390+
391+
self.set_mission_state(MissionStates.COMPLETED, "AS")
392+
result_msg.success = True
393+
return result_msg
394+
395+
def feedback_loop(self, goal_handle: ServerGoalHandle):
396+
"""Abstracted feedback loop where tolerance checks are conducted.
397+
398+
Args:
399+
pose_stamped: target location
400+
goal_handle: passed in to enable feedback publishing
401+
"""
402+
rate = self._node.create_rate(2)
403+
feedback = self.action_type.Feedback
404+
405+
while self.current_idx <= self.path_len:
406+
407+
self.set_mission_state(MissionStates.RUNNING, "AS")
408+
409+
if goal_handle.is_cancel_requested:
410+
self.logger.info("Goal was cancelled by client.")
411+
goal_handle.canceled()
412+
return "cancelled"
413+
414+
feedback.feedback = self._json_ops.encode(self.current_idx)
415+
goal_handle.publish_feedback(feedback)
416+
rate.sleep()
417+
418+
goal_handle.succeed()
419+
rate.destroy()
420+
return "done"
421+
422+
423+
def cancel_callback(self, goal_handle: ServerGoalHandle) -> CancelResponse:
424+
"""Handles canceling of goal requests.
425+
426+
Args:
427+
goal_handle: handle
428+
429+
Returns:
430+
Cancel response as ACCEPT
431+
"""
432+
433+
self._loginfo("Cancelled")
434+
435+
self.set_mission_state(MissionStates.CANCELLED, "AS")
436+
437+
return CancelResponse.ACCEPT
438+

behaviours/sam_diving_controller/sam_diving_controller/ConveniencePub.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class ConveniencePub(IDivePub):
2020
Implements convenience topic publishers for debugging
2121
"""
2222
def __init__(self, node: Node, dive_sub, dive_controller) -> None:
23+
24+
self._node = node
25+
26+
#self._node.declare_parameter('robot_name', 'sam0')
27+
self._robot_name = self._node.get_parameter('robot_name').get_parameter_value().string_value
28+
2329
self._state_pub = node.create_publisher(ControlState, ControlTopics.STATES_CONV, 10)
2430
self._ref_pub = node.create_publisher(ControlReference, ControlTopics.REF_CONV, 10)
2531
self._error_pub = node.create_publisher(ControlError, ControlTopics.CONTROL_ERROR_CONV, 10)
@@ -80,15 +86,15 @@ def _update_input(self) -> None:
8086
self._input_pub.publish(self._input_msg)
8187

8288
def _update_waypoint(self) -> None:
83-
self._waypoint = self._dive_sub.get_waypoint()
89+
self._waypoint = self._dive_sub.get_odom_waypoint()
8490
self._goal_tolerance = self._dive_sub.get_goal_tolerance()
8591

8692
if self._waypoint is None:
8793
return
8894

8995
self._waypoint_msg = PoseWithCovarianceStamped()
90-
self._waypoint_msg.header = self._waypoint.header
91-
self._waypoint_msg.pose.pose = self._waypoint.pose
96+
self._waypoint_msg.header.frame_id = self._robot_name + 'odom'
97+
self._waypoint_msg.pose.pose = self._waypoint
9298
self._waypoint_msg.pose.pose.orientation.w = 1.0
9399
cov = np.zeros([6,6])
94100
cov[0][0] = np.sqrt(self._goal_tolerance)
@@ -166,5 +172,5 @@ def update(self) -> None:
166172
self._update_error()
167173
self._update_input()
168174
self._update_waypoint()
169-
self._print_state()
175+
#self._print_state()
170176

0 commit comments

Comments
 (0)