.
This commit is contained in:
parent
4a8e372c98
commit
ba65fee28a
@ -34,8 +34,9 @@
|
||||
</include>
|
||||
|
||||
<param name="/move_base/DWAPlannerROS/yaw_goal_tolerance" value="6.28" />
|
||||
<param name="/move_base/DWAPlannerROS/xy_goal_tolerance" value="0.20" />
|
||||
|
||||
<param name="/move_base/DWAPlannerROS/xy_goal_tolerance" value="0.1" />
|
||||
<param name="/move_base/DWAPlannerROS/latch_xy_goal_tolerance" value="false" />
|
||||
|
||||
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find turtlebot3_navigation)/rviz/turtlebot3_navigation.rviz"/>
|
||||
|
||||
<node pkg="myrobot_pkg" type="controller.py" name="nav_controller" output="screen">
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import rospy
|
||||
import actionlib
|
||||
import threading
|
||||
@ -12,41 +9,31 @@ class MissionController:
|
||||
def __init__(self):
|
||||
rospy.init_node('nav_controller')
|
||||
|
||||
# --- 設定 ---
|
||||
self.waypoint_queue = []
|
||||
self.current_goal_idx = 0
|
||||
self.mission_complete = False
|
||||
|
||||
# --- ROS Publisher ---
|
||||
# 1. 速度控制 (強制停車用) - 提高 queue_size 確保指令送達
|
||||
self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
|
||||
# 2. 視覺化標記
|
||||
# 提高發布頻率與佇列大小,確保煞車指令優先權
|
||||
self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=100)
|
||||
self.marker_pub = rospy.Publisher('/waypoint_markers', MarkerArray, queue_size=10)
|
||||
|
||||
# --- ROS Subscriber ---
|
||||
rospy.Subscriber('/clicked_point', PointStamped, self.click_callback)
|
||||
|
||||
# --- 導航客戶端 ---
|
||||
self.client = actionlib.SimpleActionClient('move_base', MoveBaseAction)
|
||||
rospy.loginfo("正在連接 Move Base Server...")
|
||||
rospy.loginfo("Waiting for move_base...")
|
||||
self.client.wait_for_server()
|
||||
rospy.loginfo("導航系統已連線!")
|
||||
rospy.loginfo("Connected!")
|
||||
|
||||
# --- 讀取參數 ---
|
||||
self.load_waypoints_from_param()
|
||||
|
||||
# 啟動監控執行緒
|
||||
self.monitor_thread = threading.Thread(target=self.mission_loop)
|
||||
self.monitor_thread.start()
|
||||
|
||||
def load_waypoints_from_param(self):
|
||||
point_list = rospy.get_param('~waypoints', [])
|
||||
if point_list:
|
||||
rospy.loginfo(f"讀取到 {len(point_list)} 個路徑點。")
|
||||
for p in point_list:
|
||||
self.add_waypoint(p[0], p[1])
|
||||
else:
|
||||
rospy.logwarn("無預設路徑點。")
|
||||
|
||||
def add_waypoint(self, x, y):
|
||||
target_pose = PoseStamped()
|
||||
@ -57,59 +44,29 @@ class MissionController:
|
||||
target_pose.pose.orientation.w = 1.0
|
||||
|
||||
self.waypoint_queue.append(target_pose)
|
||||
rospy.loginfo(f"加入點: ({x}, {y})")
|
||||
rospy.loginfo(f"Add waypoint: ({x}, {y})")
|
||||
self.publish_markers()
|
||||
|
||||
def click_callback(self, msg):
|
||||
# 如果任務已結束,允許手動點擊來重置並開始新任務
|
||||
if self.mission_complete:
|
||||
rospy.loginfo("檢測到新輸入,重置任務狀態...")
|
||||
self.mission_complete = False
|
||||
self.current_goal_idx = 0
|
||||
self.waypoint_queue = [] # 清空舊的
|
||||
self.waypoint_queue = []
|
||||
|
||||
self.add_waypoint(msg.point.x, msg.point.y)
|
||||
|
||||
def publish_markers(self):
|
||||
marker_array = MarkerArray()
|
||||
if self.current_goal_idx >= len(self.waypoint_queue):
|
||||
delete_all = Marker()
|
||||
delete_all.action = Marker.DELETEALL
|
||||
marker_array.markers.append(delete_all)
|
||||
self.marker_pub.publish(marker_array)
|
||||
return
|
||||
|
||||
line_marker = Marker()
|
||||
line_marker.header.frame_id = "map"
|
||||
line_marker.type = Marker.LINE_STRIP
|
||||
line_marker.action = Marker.ADD
|
||||
line_marker.id = 999
|
||||
line_marker.scale.x = 0.05
|
||||
line_marker.color.r, line_marker.color.g, line_marker.color.b, line_marker.color.a = 0.0, 0.0, 1.0, 0.8
|
||||
|
||||
for i in range(self.current_goal_idx, len(self.waypoint_queue)):
|
||||
p = self.waypoint_queue[i].pose.position
|
||||
line_marker.points.append(p)
|
||||
|
||||
text_marker = Marker()
|
||||
text_marker.header.frame_id = "map"
|
||||
text_marker.type = Marker.TEXT_VIEW_FACING
|
||||
text_marker.action = Marker.ADD
|
||||
text_marker.id = i
|
||||
text_marker.pose.position.x, text_marker.pose.position.y, text_marker.pose.position.z = p.x, p.y, 0.5
|
||||
text_marker.text = f"P{i+1}"
|
||||
text_marker.scale.z = 0.3
|
||||
text_marker.color.r, text_marker.color.g, text_marker.color.b, text_marker.color.a = 1.0, 0.0, 0.0, 1.0
|
||||
marker_array.markers.append(text_marker)
|
||||
|
||||
marker_array.markers.append(line_marker)
|
||||
self.marker_pub.publish(marker_array)
|
||||
# (視覺化程式碼保持不變,為節省篇幅省略,請保留您原本的視覺化代碼)
|
||||
pass
|
||||
|
||||
def stop_robot(self):
|
||||
"""持續發送速度 0 的指令"""
|
||||
"""暴力煞車:發送全 0 速度"""
|
||||
stop_msg = Twist()
|
||||
stop_msg.linear.x = 0.0
|
||||
stop_msg.linear.y = 0.0
|
||||
stop_msg.linear.z = 0.0
|
||||
stop_msg.angular.x = 0.0
|
||||
stop_msg.angular.y = 0.0
|
||||
stop_msg.angular.z = 0.0
|
||||
self.cmd_vel_pub.publish(stop_msg)
|
||||
|
||||
@ -117,44 +74,41 @@ class MissionController:
|
||||
goal = MoveBaseGoal()
|
||||
goal.target_pose = target_pose
|
||||
self.client.send_goal(goal)
|
||||
|
||||
# 等待結果
|
||||
self.client.wait_for_result()
|
||||
return self.client.get_state() == actionlib.GoalStatus.SUCCEEDED
|
||||
|
||||
# 只要不是 ABORTED (失敗),我們都視為成功,讓它繼續下一點
|
||||
# 這樣可以避免因為一點點誤差卡在原地
|
||||
state = self.client.get_state()
|
||||
return state in [actionlib.GoalStatus.SUCCEEDED, actionlib.GoalStatus.PREEMPTED]
|
||||
|
||||
def mission_loop(self):
|
||||
# 提高頻率到 10Hz,讓煞車指令更密集
|
||||
rate = rospy.Rate(10)
|
||||
rate = rospy.Rate(10) # 10Hz
|
||||
rospy.sleep(1.0)
|
||||
|
||||
while not rospy.is_shutdown():
|
||||
# 1. 執行導航
|
||||
if self.current_goal_idx < len(self.waypoint_queue):
|
||||
# --- 導航中 ---
|
||||
self.publish_markers()
|
||||
target = self.waypoint_queue[self.current_goal_idx]
|
||||
rospy.loginfo(f"--- 前往 P{self.current_goal_idx + 1} ---")
|
||||
rospy.loginfo(f"Go to Waypoint {self.current_goal_idx + 1}")
|
||||
|
||||
success = self.execute_navigation(target)
|
||||
|
||||
if success:
|
||||
rospy.loginfo(f"P{self.current_goal_idx + 1} 到達!")
|
||||
else:
|
||||
rospy.logwarn("導航受阻,跳過此點。")
|
||||
self.execute_navigation(target)
|
||||
|
||||
# 不管結果如何,強制切換到下一點 (避免卡死)
|
||||
# 如果你想確保一定到達,可以加判斷,但容易卡住
|
||||
self.current_goal_idx += 1
|
||||
|
||||
# 2. 任務結束
|
||||
else:
|
||||
# --- 任務結束 ---
|
||||
if len(self.waypoint_queue) > 0:
|
||||
if not self.mission_complete:
|
||||
rospy.loginfo(">>> 任務全部完成,執行強制停車。")
|
||||
self.publish_markers() # 清除標記
|
||||
|
||||
# 1. 告訴 Move Base 取消所有目標 (不要再嘗試對齊方向)
|
||||
rospy.loginfo("Mission Complete! Stopping robot.")
|
||||
# 【關鍵】取消所有 MoveBase 任務,讓它停止規劃
|
||||
self.client.cancel_all_goals()
|
||||
self.mission_complete = True
|
||||
|
||||
# 2. 【關鍵修改】在迴圈內持續呼叫 stop_robot
|
||||
# 只要任務結束,每一輪迴圈都會發送速度0
|
||||
# 這會強制覆蓋掉任何滑行或旋轉的殘留指令
|
||||
# 【關鍵】持續發送停止指令,覆蓋掉任何滑行或殘留命令
|
||||
self.stop_robot()
|
||||
|
||||
rate.sleep()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user