Untitled
Anonymous
plain_text
02/23/2026 12:05 PM
14.6 KB
10
Indexable
import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from rclpy.callback_groups import ReentrantCallbackGroup
from rclpy.executors import MultiThreadedExecutor
from turtlesim_contest_interface.action import FindHiddenGift
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose, Color
from turtlesim.srv import SetPen
import math
import time
class GiftFinderActionServer(Node):
def __init__(self):
super().__init__('gift_finder_action_server')
self.cb_group = ReentrantCallbackGroup()
self._action_server = ActionServer(
self, FindHiddenGift, 'find_hidden_gift',
self.execute_callback, callback_group=self.cb_group
)
self.pub = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
self.pen_client = self.create_client(SetPen, '/turtle1/set_pen')
self.create_subscription(
Pose, '/turtle1/pose', self.pose_cb, 10, callback_group=self.cb_group
)
self.create_subscription(
Color, '/turtle1/color_sensor', self.color_cb, 10, callback_group=self.cb_group
)
self.pose = None
self.current_color = None
self.traveled = 0.0
self.grid_step = 0.5
# Границы подарка
self.gift_x_min = None
self.gift_x_max = None
self.gift_y_min = None
self.gift_y_max = None
self.get_logger().info('Сервер запущен')
def pose_cb(self, msg):
self.pose = msg
def color_cb(self, msg):
self.current_color = msg
def is_on_gift(self):
if self.current_color is None:
return False
return (self.current_color.r == 0 and
self.current_color.g == 255 and
self.current_color.b == 0)
def update_gift_bounds(self):
if self.pose is None:
return
x, y = self.pose.x, self.pose.y
if self.gift_x_min is None:
self.gift_x_min = self.gift_x_max = x
self.gift_y_min = self.gift_y_max = y
else:
self.gift_x_min = min(self.gift_x_min, x)
self.gift_x_max = max(self.gift_x_max, x)
self.gift_y_min = min(self.gift_y_min, y)
self.gift_y_max = max(self.gift_y_max, y)
def get_gift_center(self):
if self.gift_x_min is None:
return None
center_x = (self.gift_x_min + self.gift_x_max) / 2
center_y = (self.gift_y_min + self.gift_y_max) / 2
return (center_x, center_y)
def gift_found(self):
return self.gift_x_min is not None
def set_pen(self, off):
if self.pen_client.wait_for_service(timeout_sec=1.0):
req = SetPen.Request()
req.off = 1 if off else 0
self.pen_client.call_async(req)
def wait_pose(self):
for _ in range(100):
if self.pose:
return True
time.sleep(0.05)
return False
def norm_angle(self, a):
while a > math.pi:
a -= 2 * math.pi
while a <= -math.pi:
a += 2 * math.pi
return a
def stop(self):
twist = Twist()
self.pub.publish(twist)
time.sleep(0.03)
def rotate_to(self, target_angle):
"""Поворот к заданному углу."""
target_angle = self.norm_angle(target_angle)
twist = Twist()
max_iterations = 500
iterations = 0
while rclpy.ok() and iterations < max_iterations:
iterations += 1
error = self.norm_angle(target_angle - self.pose.theta)
if abs(error) < 0.02:
break
speed = 2.0 * error
speed = max(-1.5, min(1.5, speed))
if 0 < speed < 0.15:
speed = 0.15
elif -0.15 < speed < 0:
speed = -0.15
twist.angular.z = speed
self.pub.publish(twist)
time.sleep(0.02)
self.stop()
def go_to(self, x, y):
"""Быстрое перемещение к точке."""
if not self.wait_pose():
return
start_x, start_y = self.pose.x, self.pose.y
dx = x - self.pose.x
dy = y - self.pose.y
dist = math.sqrt(dx * dx + dy * dy)
if dist < 0.05:
return
angle = math.atan2(dy, dx)
self.rotate_to(angle)
twist = Twist()
twist.linear.x = 5.0 # Быстрое движение
while rclpy.ok():
dx = x - self.pose.x
dy = y - self.pose.y
remaining = math.sqrt(dx * dx + dy * dy)
if remaining < 0.05:
break
# Замедление при приближении
if remaining < 0.5:
twist.linear.x = max(1.0, remaining * 4)
self.pub.publish(twist)
time.sleep(0.01)
self.stop()
end_x, end_y = self.pose.x, self.pose.y
self.traveled += math.sqrt((end_x - start_x)**2 + (end_y - start_y)**2)
def trace_gift_boundary(self):
"""Обходит границу подарка."""
self.get_logger().info('Обхожу границу подарка...')
self.update_gift_bounds()
directions = [0, math.pi/2, math.pi, -math.pi/2]
for direction in directions:
self.trace_one_direction(direction)
self.get_logger().info(
f'Границы: x=[{self.gift_x_min:.2f}, {self.gift_x_max:.2f}], '
f'y=[{self.gift_y_min:.2f}, {self.gift_y_max:.2f}]'
)
def trace_one_direction(self, angle):
"""Движется в направлении до края подарка."""
self.rotate_to(angle)
time.sleep(0.03)
twist = Twist()
twist.linear.x = 2.0 # Ускорено (было 0.3)
max_steps = 500
steps = 0
while rclpy.ok() and self.is_on_gift() and steps < max_steps:
self.update_gift_bounds()
self.pub.publish(twist)
time.sleep(0.01)
steps += 1
self.stop()
# Возврат назад
twist.linear.x = -1.0 # Ускорено (было -0.2)
steps = 0
while rclpy.ok() and not self.is_on_gift() and steps < 100:
self.pub.publish(twist)
time.sleep(0.01)
steps += 1
self.stop()
if self.is_on_gift():
self.update_gift_bounds()
def search(self, area, goal_handle):
"""Быстрый поиск змейкой."""
self.set_pen(off=True)
time.sleep(0.05)
x_min = area.bottom_left.x + 0.3
x_max = area.top_right.x - 0.3
y_min = area.bottom_left.y + 0.3
y_max = area.top_right.y - 0.3
self.go_to(x_min, y_min)
y = y_min
moving_right = True
while y <= y_max and rclpy.ok():
target_x = x_max if moving_right else x_min
if moving_right:
self.rotate_to(0)
else:
self.rotate_to(math.pi)
start_line_x = self.pose.x
twist = Twist()
twist.linear.x = 5.0 # Быстрое сканирование (было 1.0)
while rclpy.ok():
if moving_right and self.pose.x >= target_x:
break
if not moving_right and self.pose.x <= target_x:
break
if self.is_on_gift():
self.stop()
self.trace_gift_boundary()
fb = FindHiddenGift.Feedback()
fb.gift_ever_detected = True
fb.traveled_distance = self.traveled
goal_handle.publish_feedback(fb)
return
self.pub.publish(twist)
time.sleep(0.01) # Чаще проверяем (было 0.02)
self.stop()
self.traveled += abs(self.pose.x - start_line_x)
fb = FindHiddenGift.Feedback()
fb.gift_ever_detected = self.gift_found()
fb.traveled_distance = self.traveled
goal_handle.publish_feedback(fb)
y += self.grid_step
moving_right = not moving_right
if y <= y_max:
self.rotate_to(math.pi / 2)
twist = Twist()
twist.linear.x = 3.0 # Ускорено (было 0.5)
start_y = self.pose.y
while rclpy.ok() and self.pose.y < y:
if self.is_on_gift():
self.stop()
self.trace_gift_boundary()
fb = FindHiddenGift.Feedback()
fb.gift_ever_detected = True
fb.traveled_distance = self.traveled
goal_handle.publish_feedback(fb)
return
self.pub.publish(twist)
time.sleep(0.01)
self.stop()
self.traveled += abs(self.pose.y - start_y)
def execute_callback(self, goal_handle):
self.get_logger().info('Начинаю поиск')
self.gift_x_min = None
self.gift_x_max = None
self.gift_y_min = None
self.gift_y_max = None
self.traveled = 0.0
area = goal_handle.request.search_area
if not self.wait_pose():
goal_handle.abort()
return FindHiddenGift.Result()
self.search(area, goal_handle)
result = FindHiddenGift.Result()
center = self.get_gift_center()
if center:
result.gift_found = True
result.gift_coordinates.x = center[0]
result.gift_coordinates.y = center[1]
self.get_logger().info(f'Центр: ({center[0]:.3f}, {center[1]:.3f})')
else:
result.gift_found = False
self.get_logger().info('Подарок не найден')
self.get_logger().info(f'Пройдено: {self.traveled:.1f} м')
goal_handle.succeed()
return result
def main(args=None):
rclpy.init(args=args)
node = GiftFinderActionServer()
executor = MultiThreadedExecutor()
executor.add_node(node)
try:
executor.spin()
except KeyboardInterrupt:
pass
finally:
node.stop()
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Editor is loading...
Leave a Comment