fire kill in unity works

main
moscowsky 4 weeks ago
parent be6398ba68
commit a32b2951f3

@ -28,7 +28,7 @@ Visualization Manager:
Class: "" Class: ""
Displays: Displays:
- Alpha: 0.5 - Alpha: 0.5
Cell Size: 50 Cell Size: 10
Class: rviz_default_plugins/Grid Class: rviz_default_plugins/Grid
Color: 160; 160; 164 Color: 160; 160; 164
Enabled: true Enabled: true
@ -38,11 +38,11 @@ Visualization Manager:
Name: Grid Name: Grid
Normal Cell Count: 0 Normal Cell Count: 0
Offset: Offset:
X: 20 X: 200
Y: 10 Y: 200
Z: 0 Z: 0
Plane: XY Plane: XY
Plane Cell Count: 6 Plane Cell Count: 60
Reference Frame: <Fixed Frame> Reference Frame: <Fixed Frame>
Value: true Value: true
- Class: rviz_default_plugins/Image - Class: rviz_default_plugins/Image
@ -132,6 +132,45 @@ Visualization Manager:
{} {}
Update Interval: 0 Update Interval: 0
Value: true Value: true
- Alpha: 1
Axes Length: 1
Axes Radius: 0.10000000149011612
Class: rviz_default_plugins/Pose
Color: 255; 25; 0
Enabled: true
Head Length: 0.30000001192092896
Head Radius: 0.10000000149011612
Name: CrossHair Vector
Shaft Length: 1
Shaft Radius: 0.05000000074505806
Shape: Arrow
Topic:
Depth: 5
Durability Policy: Volatile
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
Value: /sirius_bot/crosshair_vector
Value: true
- Class: rviz_default_plugins/MarkerArray
Enabled: true
Name: MarkerArray
Namespaces:
"": true
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /locations_markers
Value: true
- Class: rviz_default_plugins/Axes
Enabled: false
Length: 10
Name: Axes
Radius: 1
Reference Frame: <Fixed Frame>
Value: false
Enabled: true Enabled: true
Global Options: Global Options:
Background Color: 48; 48; 48 Background Color: 48; 48; 48
@ -178,25 +217,25 @@ Visualization Manager:
Views: Views:
Current: Current:
Class: rviz_default_plugins/ThirdPersonFollower Class: rviz_default_plugins/ThirdPersonFollower
Distance: 61.71200180053711 Distance: 72.5179443359375
Enable Stereo Rendering: Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549 Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1 Stereo Focal Distance: 1
Swap Stereo Eyes: false Swap Stereo Eyes: false
Value: false Value: false
Focal Point: Focal Point:
X: -0.47304797172546387 X: 5.716884613037109
Y: -0.08951294422149658 Y: 3.2075774669647217
Z: -0.25962093472480774 Z: -3.7333018553908914e-05
Focal Shape Fixed Size: true Focal Shape Fixed Size: true
Focal Shape Size: 0.05000000074505806 Focal Shape Size: 0.05000000074505806
Invert Z Axis: false Invert Z Axis: false
Name: Current View Name: Current View
Near Clip Distance: 0.009999999776482582 Near Clip Distance: 0.009999999776482582
Pitch: 0.5053984522819519 Pitch: 0.5053315162658691
Target Frame: base_link Target Frame: base_link
Value: ThirdPersonFollower (rviz_default_plugins) Value: ThirdPersonFollower (rviz_default_plugins)
Yaw: 3.5853965282440186 Yaw: 3.1497514247894287
Saved: Saved:
- Class: rviz_default_plugins/ThirdPersonFollower - Class: rviz_default_plugins/ThirdPersonFollower
Distance: 2.978720188140869 Distance: 2.978720188140869

@ -57,6 +57,7 @@ def generate_launch_description():
output="both", output="both",
parameters=[ parameters=[
ParameterFile(joy_params), ParameterFile(joy_params),
{"fire_ext_max_capacity": 100},
], ],
) )

@ -57,7 +57,9 @@ def generate_launch_description():
namespace=LaunchConfiguration("namespace"), namespace=LaunchConfiguration("namespace"),
parameters=[ parameters=[
{"use_sim_time": LaunchConfiguration("use_sim_time")}, {"use_sim_time": LaunchConfiguration("use_sim_time")},
{"crosshair_alpha": 1.0} {"crosshair_alpha": 1.0},
{"sof_cos_sim": 0.999},
{"fe_distance": 5.0},
], ],
remappings=[ remappings=[
("raw_image", "/sirius_bot/camera/image_view"), ("raw_image", "/sirius_bot/camera/image_view"),
@ -81,7 +83,7 @@ def generate_launch_description():
pkg_gen_location_on_mesh = FindPackageShare(package="").find("gen_locations_on_mesh") pkg_gen_location_on_mesh = FindPackageShare(package="").find("gen_locations_on_mesh")
mesh_path = PathJoinSubstitution([pkg_gen_location_on_mesh, "share", "scene_init.ply"]) mesh_path = PathJoinSubstitution([pkg_gen_location_on_mesh, "share", "sirius_scene_floor.ply"])
cache_path = PathJoinSubstitution([pkg_gen_location_on_mesh, "share", "cache.pkl"]) cache_path = PathJoinSubstitution([pkg_gen_location_on_mesh, "share", "cache.pkl"])

@ -3,7 +3,7 @@
import rclpy import rclpy
from rclpy.node import Node from rclpy.node import Node
from rclpy.executors import SingleThreadedExecutor, MultiThreadedExecutor from rclpy.executors import SingleThreadedExecutor, MultiThreadedExecutor
from geometry_msgs.msg import Point, Vector3 from geometry_msgs.msg import Point, Vector3, PoseStamped
from sensor_msgs.msg import Image, CameraInfo from sensor_msgs.msg import Image, CameraInfo
from cv_bridge import CvBridge from cv_bridge import CvBridge
import cv2 import cv2
@ -12,6 +12,9 @@ from std_srvs.srv import Trigger
from tf2_ros import Buffer, TransformListener from tf2_ros import Buffer, TransformListener
from gen_locations_on_mesh_msgs.msg import LocationsOnMesh from gen_locations_on_mesh_msgs.msg import LocationsOnMesh
from gen_locations_on_mesh_msgs.srv import RemoveLocation from gen_locations_on_mesh_msgs.srv import RemoveLocation
from scipy.spatial.transform import Rotation
from visualization_msgs.msg import MarkerArray
class FireExtinguisherEmulatorNode(Node): class FireExtinguisherEmulatorNode(Node):
@ -27,7 +30,7 @@ class FireExtinguisherEmulatorNode(Node):
self.declare_parameter('show_target', True) self.declare_parameter('show_target', True)
self.declare_parameter('sof_r', 0.5) self.declare_parameter('sof_cos_sim', 0.5)
self.crosshair_alpha = self.get_parameter('crosshair_alpha').value self.crosshair_alpha = self.get_parameter('crosshair_alpha').value
self.crosshair_speed = self.get_parameter('crosshair_speed').value self.crosshair_speed = self.get_parameter('crosshair_speed').value
@ -37,14 +40,14 @@ class FireExtinguisherEmulatorNode(Node):
self.robot_frame = self.get_parameter('robot_frame').value self.robot_frame = self.get_parameter('robot_frame').value
self.show_target = self.get_parameter('show_target').value self.show_target = self.get_parameter('show_target').value
self.sof_r = self.get_parameter('sof_r').value self.sof_cos_sim = self.get_parameter('sof_cos_sim').value
# Sources of Fire positions in (id, x, y, z, r) # Sources of Fire positions in (id, x, y, z, r)
self.sof_poisitions = [] self.sof_poisitions = []
# Publishers # Publishers
self.fire_ext_image_pub = self.create_publisher(Image, 'crosshair_image', 10) self.fire_ext_image_pub = self.create_publisher(Image, 'crosshair_image', 10)
self.fire_ext_vector_pub = self.create_publisher(Vector3, 'crosshair_vector', 10) self.fire_ext_vector_pub = self.create_publisher(PoseStamped, 'crosshair_vector', 10)
# State variables # State variables
self.target_x = 0.0 self.target_x = 0.0
@ -71,7 +74,9 @@ class FireExtinguisherEmulatorNode(Node):
self.tf_listener = TransformListener(self.tf_buffer, self) self.tf_listener = TransformListener(self.tf_buffer, self)
# remove SoF # remove SoF
self.remove_sof_srv = self.create_client(RemoveLocation, 'remove_location') self.remove_sof_srv = self.create_client(RemoveLocation, '/remove_location')
self.fe_vector = None
# Timers and subscriptions # Timers and subscriptions
self.target_timer = self.create_timer(0.1, self.target_timer_cb) # 10 Hz self.target_timer = self.create_timer(0.1, self.target_timer_cb) # 10 Hz
@ -80,25 +85,35 @@ class FireExtinguisherEmulatorNode(Node):
self.create_subscription(Point, 'target_point', self.target_cb, 10) self.create_subscription(Point, 'target_point', self.target_cb, 10)
self.sub_camera_info = self.create_subscription(CameraInfo, 'camera_info', self.cam_info_cb, 10) self.sub_camera_info = self.create_subscription(CameraInfo, 'camera_info', self.cam_info_cb, 10)
self.create_subscription(Image, 'raw_image', self.raw_image_cb, 10) self.create_subscription(Image, 'raw_image', self.raw_image_cb, 10)
self.create_subscription(LocationsOnMesh, 'locations', self.locations_cb, 10) self.create_subscription(MarkerArray, '/locations_markers', self.locations_cb, 10)
self.create_service(Trigger, 'fe_burst', self.fe_burst_cb) self.create_service(Trigger, 'fe_burst', self.fe_burst_cb)
def locations_cb(self, msg): def locations_cb(self, msg):
for idx, x, y, z in zip(msg.location_id, msg.x, msg.y, msg.z): #for idx, x, y, z in zip(msg.location_id, msg.x, msg.y, msg.z):
self.sof_poisitions.append((idx, x, y, z, self.sof_r)) self.sof_poisitions = []
for marker in msg.markers:
self.sof_poisitions.append((marker.id,
marker.pose.position.x,
marker.pose.position.y,
marker.pose.position.z,
))
def fe_burst_cb(self, req, res): def fe_burst_cb(self, req, res):
intersected_spheres = find_intersected_spheres( intersected_spheres = find_intersected_spheres(
spheres=self.sof_poisitions, spheres=self.sof_poisitions,
camera_vector=self.vector_msg, # ваш Vector3 с направлением camera_vector=self.fe_vector, # ваш Vector3 с направлением
camera_vector_length=self.fe_distance, # длина луча camera_vector_length=self.fe_distance, # длина луча
tf_buffer=self.tf_buffer, tf_buffer=self.tf_buffer,
camera_frame=self.camera_info.header.frame_id, # из CameraInfo camera_frame=self.camera_info.header.frame_id, # из CameraInfo
world_frame="map" world_frame="map",
sof_cos_sim=self.sof_cos_sim,
print_f = self.get_logger().info
) )
self.get_logger().info(f"{intersected_spheres}")
if len(intersected_spheres): if len(intersected_spheres):
res.success = True res.success = True
res.message = str(intersected_spheres[0][0]) # add ID of source of fire if success res.message = str(intersected_spheres[0][0]) # add ID of source of fire if success
@ -184,8 +199,6 @@ class FireExtinguisherEmulatorNode(Node):
self.current_y += step_y self.current_y += step_y
#self.get_logger().info(f"Target {self.target_x} {self.target_y} current {self.current_x} {self.current_y}") #self.get_logger().info(f"Target {self.target_x} {self.target_y} current {self.current_x} {self.current_y}")
# Calculate 3D unit vector from camera frame
if self.fx > 0 and self.fy > 0: if self.fx > 0 and self.fy > 0:
px = self.current_x px = self.current_x
py = self.current_y py = self.current_y
@ -200,11 +213,20 @@ class FireExtinguisherEmulatorNode(Node):
y /= norm y /= norm
z /= norm z /= norm
self.vector_msg = Vector3() self.fe_vector = (x, y, z)
self.vector_msg.x = x
self.vector_msg.y = y msg = PoseStamped()
self.vector_msg.z = z msg.header.frame_id = self.camera_info.header.frame_id
self.fire_ext_vector_pub.publish(self.vector_msg) msg.header.stamp = self.get_clock().now().to_msg()
q = quaternion_from_two_vectors([1.0, 0.0, 0.0], self.fe_vector)
msg.pose.orientation.x = float(q[0])
msg.pose.orientation.y = float(q[1])
msg.pose.orientation.z = float(q[2])
msg.pose.orientation.w = float(q[3])
self.fire_ext_vector_pub.publish(msg)
def raw_image_cb(self, raw_msg): def raw_image_cb(self, raw_msg):
"""Draw crosshair on image and publish 3D vector""" """Draw crosshair on image and publish 3D vector"""
@ -260,12 +282,14 @@ class FireExtinguisherEmulatorNode(Node):
def find_intersected_spheres( def find_intersected_spheres(
spheres: list[tuple[int, float, float, float, float]], # [(id, x, y, z, r)] spheres: list[tuple[int, float, float, float]], # [(id, x, y, z)]
camera_vector: Vector3, # единичный вектор в координатах камеры camera_vector: (float, float, float), # единичный вектор в координатах камеры
camera_vector_length: float, # длина луча camera_vector_length: float, # длина луча
tf_buffer: Buffer, tf_buffer: Buffer,
camera_frame: str, camera_frame: str,
world_frame: str = "world" world_frame: str,
sof_cos_sim,
print_f = print
) -> list[tuple[int, float, float]]: ) -> list[tuple[int, float, float]]:
""" """
Находит все сферы, пересекаемые лучом. Находит все сферы, пересекаемые лучом.
@ -275,8 +299,8 @@ def find_intersected_spheres(
# 1. Получаем преобразование камеры -> мир # 1. Получаем преобразование камеры -> мир
transform = tf_buffer.lookup_transform( transform = tf_buffer.lookup_transform(
world_frame, # target frame (мир) world_frame, # target frame
camera_frame, # source frame (камера) camera_frame, # source frame
rclpy.time.Time() # latest transform rclpy.time.Time() # latest transform
) )
@ -286,10 +310,11 @@ def find_intersected_spheres(
transform.transform.translation.y, transform.transform.translation.y,
transform.transform.translation.z transform.transform.translation.z
]) ])
print_f(f"{p0}")
# 3. Преобразуем вектор камеры в координаты мира # 3. Преобразуем вектор камеры в координаты мира
# Вектор как точка (с нулевой координатой w) # Вектор как точка (с нулевой координатой w)
camera_vec = np.array([camera_vector.x, camera_vector.y, camera_vector.z]) camera_vec = np.array(camera_vector)
# Матрица вращения из transform (кватернион -> матрица) # Матрица вращения из transform (кватернион -> матрица)
q = transform.transform.rotation q = transform.transform.rotation
@ -306,31 +331,80 @@ def find_intersected_spheres(
# 4. Проходим по всем сферам # 4. Проходим по всем сферам
intersected = [] intersected = []
l2 = camera_vector_length**2
for sphere in spheres: for sphere in spheres:
idx, xs, ys, zs, r = sphere idx, xs, ys, zs = sphere
p_s = np.array([xs, ys, zs]) # центр сферы в мире
dx = p0[0] - xs
# k = p₀ - pₛ (вектор от сферы до камеры) dy = p0[1] - ys
k = p0 - p_s D2 = (dx)**2 + (dy)**2
# Коэффициенты квадратного уравнения if D2 <= l2:
# t² + 2*b*t + c = 0, где a = 1 (у нас u единичный) dz = p0[2] - zs
b = np.dot(k, u)
c = np.dot(k, k) - r**2 def cos_sim(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Дискриминант
D = b**2 - c sim = cos_sim(u, -np.array((dx, dy, dz)))
if D >= 0: # есть пересечение (D=0 — касание, D>0 — 2 точки) if sim >= sof_cos_sim:
sqrt_D = np.sqrt(D) intersected.append((idx, np.sqrt(D2), sim))
t1 = -b - sqrt_D
t2 = -b + sqrt_D return sorted(intersected, key=lambda x: x[1])
# Проверяем, что хотя бы одна точка пересечения на луче [0, length]
if (0 <= t1 <= camera_vector_length) or (0 <= t2 <= camera_vector_length):
intersected.append((idx, t1, t2)) # for sphere in spheres:
# idx, xs, ys, zs, r = sphere
return intersected # p_s = np.array([xs, ys, zs]) # центр сферы в мире
#
# # k = p₀ - pₛ (вектор от сферы до камеры)
# k = p0 - p_s
#
# # Коэффициенты квадратного уравнения
# # t² + 2*b*t + c = 0, где a = 1 (у нас u единичный)
# b = np.dot(k, u)
# c = np.dot(k, k) - r**2
#
# # Дискриминант
# D = b**2 - c
#
# if D >= 0: # есть пересечение (D=0 — касание, D>0 — 2 точки)
# sqrt_D = np.sqrt(D)
# t1 = -b - sqrt_D
# t2 = -b + sqrt_D
#
# # Проверяем, что хотя бы одна точка пересечения на луче [0, length]
# if (0 <= t1 <= camera_vector_length) or (0 <= t2 <= camera_vector_length):
# intersected.append((idx, t1, t2))
#
# return intersected
def quaternion_from_two_vectors(a, b):
a = np.array(a, dtype=float)
b = np.array(b, dtype=float)
a /= np.linalg.norm(a)
b /= np.linalg.norm(b)
v = np.cross(a, b)
c = np.dot(a, b)
if c < -0.999999:
axis = np.array([1.0, 0.0, 0.0])
if abs(a[0]) > 0.9:
axis = np.array([0.0, 1.0, 0.0])
v = np.cross(a, axis)
v /= np.linalg.norm(v)
q = np.array([v[0], v[1], v[2], 0.0])
return q
s = np.sqrt((1.0 + c) * 2.0)
invs = 1.0 / s
qx = v[0] * invs
qy = v[1] * invs
qz = v[2] * invs
qw = 0.5 * s
return np.array([qx, qy, qz, qw], dtype=float)
def main(args=None): def main(args=None):

@ -11,7 +11,7 @@ from std_srvs.srv import Trigger
class UnityRobotController(RobotController): class UnityRobotController(RobotController):
def __init__(self, node): def __init__(self, node):
super().__init__()
self._node = node self._node = node
@ -19,10 +19,15 @@ class UnityRobotController(RobotController):
self._node.declare_parameter('max_angular_speed', 1.5) self._node.declare_parameter('max_angular_speed', 1.5)
self._node.declare_parameter('fire_ext_pixel_speed', 1.0) self._node.declare_parameter('fire_ext_pixel_speed', 1.0)
self._node.declare_parameter('fire_ext_max_capacity', 5)
self._lin_max = self._node.get_parameter('max_linear_speed').value self._lin_max = self._node.get_parameter('max_linear_speed').value
self._ang_max = self._node.get_parameter('max_angular_speed').value self._ang_max = self._node.get_parameter('max_angular_speed').value
fire_ext_max_capacity = self._node.get_parameter('fire_ext_max_capacity').value
super().__init__(fire_ext_max_capacity = fire_ext_max_capacity)
self._twist_pub = self._node.create_publisher(Twist, 'cmd_vel', 10) self._twist_pub = self._node.create_publisher(Twist, 'cmd_vel', 10)
self._target_point_pub = self._node.create_publisher(Point, 'target_point', 10) self._target_point_pub = self._node.create_publisher(Point, 'target_point', 10)
@ -62,7 +67,7 @@ class UnityRobotController(RobotController):
def _fe_burst_done_cb(self, future): def _fe_burst_done_cb(self, future):
result = future.result() result = future.result()
if result.success: if result.success:
self._register_exted_sof(self.get_relative_time(), {'id': success.message}) self._register_exted_sof(self.get_relative_time(), {'id': result.message})
def send_fire_ext_pose_cmd(self, horisontal_pose, vertical_pose): def send_fire_ext_pose_cmd(self, horisontal_pose, vertical_pose):

Loading…
Cancel
Save