Untitled
Anonymous
plain_text
02/23/2026 10:15 AM
10.4 KB
11
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
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.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.traveled = 0.0
self.grid_step = 0.5
# Для поиска центра подарка
self.on_gift = False # Сейчас на зелёном?
self.gift_points = [] # Все точки на подарке
self.gift_center = None # Вычисленный центр
self.get_logger().info('Сервер запущен')
def pose_cb(self, msg):
self.pose = msg
def color_cb(self, msg):
is_green = (msg.r == 0 and msg.g == 255 and msg.b == 0)
if is_green and self.pose:
if not self.on_gift:
# Въехали на подарок
self.on_gift = True
self.get_logger().info(f'Въезд на подарок: ({self.pose.x:.2f}, {self.pose.y:.2f})')
# Собираем все точки на подарке
self.gift_points.append((self.pose.x, self.pose.y))
else:
if self.on_gift:
# Выехали с подарка — вычисляем центр
self.on_gift = False
self.calculate_center()
def calculate_center(self):
"""Вычисляет центр по собранным точкам."""
if not self.gift_points:
return
# Находим границы
xs = [p[0] for p in self.gift_points]
ys = [p[1] for p in self.gift_points]
# Центр = середина между крайними точками
center_x = (min(xs) + max(xs)) / 2
center_y = (min(ys) + max(ys)) / 2
# Обновляем центр (берём среднее с предыдущим, если был)
if self.gift_center:
old_x, old_y = self.gift_center
self.gift_center = ((old_x + center_x) / 2, (old_y + center_y) / 2)
else:
self.gift_center = (center_x, center_y)
self.get_logger().info(f'Центр подарка: ({self.gift_center[0]:.2f}, {self.gift_center[1]:.2f})')
# Очищаем точки для следующего прохода
self.gift_points = []
def gift_found(self):
"""Проверяет, найден ли подарок."""
return self.gift_center is not None
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()
for _ in range(3):
self.pub.publish(twist)
time.sleep(0.15)
def rotate_to(self, target_angle):
twist = Twist()
while rclpy.ok():
error = self.norm_angle(target_angle - self.pose.theta)
if abs(error) < 0.015: # ~0.9 градуса
break
# Уменьшенный Kp и ограничение скорости
speed = 1.5 * error
speed = max(-0.8, min(0.8, speed))
# Минимальная скорость, чтобы не застрять
if 0 < abs(speed) < 0.15:
speed = 0.15 if speed > 0 else -0.15
twist.angular.z = speed
self.pub.publish(twist)
time.sleep(0.02)
self.stop()
def move_forward(self, distance):
start_x, start_y = self.pose.x, self.pose.y
twist = Twist()
twist.linear.x = 1.5
traveled = 0.0
while rclpy.ok():
dx = self.pose.x - start_x
dy = self.pose.y - start_y
traveled = math.sqrt(dx*dx + dy*dy)
if traveled >= distance:
break
self.pub.publish(twist)
time.sleep(0.02)
self.stop()
self.traveled += traveled
def go_to(self, x, y):
if not self.wait_pose():
return
dx, dy = x - self.pose.x, y - self.pose.y
dist = math.sqrt(dx*dx + dy*dy)
if dist < 0.1:
return
angle = math.atan2(dy, dx)
self.rotate_to(angle)
self.move_forward(dist)
def search(self, area, goal_handle):
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
right = True
while y <= y_max and rclpy.ok():
target_x = x_max if right else x_min
self.go_to(target_x, y)
fb = FindHiddenGift.Feedback()
fb.gift_ever_detected = self.gift_found()
fb.traveled_distance = self.traveled
goal_handle.publish_feedback(fb)
y += self.grid_step
right = not right
if y <= y_max:
self.go_to(self.pose.x, y)
# Финальная обработка, если закончили на подарке
if self.on_gift and self.gift_points:
self.calculate_center()
def execute_callback(self, goal_handle):
self.get_logger().info('Начинаю поиск')
self.on_gift = False
self.gift_points = []
self.gift_center = 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()
if self.gift_center:
result.gift_found = True
result.gift_coordinates.x = self.gift_center[0]
result.gift_coordinates.y = self.gift_center[1]
self.get_logger().info(f'Центр подарка: ({self.gift_center[0]:.2f}, {self.gift_center[1]:.2f})')
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