works somehow on unity in start config
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
arctic_bot/joy_control:
|
||||||
|
ros__parameters:
|
||||||
|
speed_x_axis: 4
|
||||||
|
speed_y_axis: 3
|
||||||
|
speed_throttle_btn: 7
|
||||||
|
|
||||||
|
crosshair_plus_x_btn: -1
|
||||||
|
crosshair_minus_x_btn: -1
|
||||||
|
crosshair_plus_y_btn: -1
|
||||||
|
crosshair_minus_y_btn: -1
|
||||||
|
|
||||||
|
crosshair_step: 0.05
|
||||||
|
|
||||||
|
crosshair_x_axis: 6
|
||||||
|
crosshair_y_axis: 7
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
arctic_bot/joy:
|
||||||
|
ros__parameters:
|
||||||
|
# to list connected devices, use:
|
||||||
|
# ros2 run joy joy_enumerate_devices
|
||||||
|
device_id: 0
|
||||||
|
# device_name can used instead of device_id. device_name takes precedence
|
||||||
|
device_name: ""
|
||||||
|
# in [0.; 1.0]
|
||||||
|
deadzone: 0.3
|
||||||
|
# set autorepeat to 0 to disable
|
||||||
|
autorepeat_rate: 20.0
|
||||||
|
sticky_buttons: false
|
||||||
|
# rate control
|
||||||
|
coalesce_interval_ms: 1
|
||||||
+54
-5
@@ -3,9 +3,7 @@ Panels:
|
|||||||
Help Height: 87
|
Help Height: 87
|
||||||
Name: Displays
|
Name: Displays
|
||||||
Property Tree Widget:
|
Property Tree Widget:
|
||||||
Expanded:
|
Expanded: ~
|
||||||
- /Global Options1
|
|
||||||
- /Status1
|
|
||||||
Splitter Ratio: 0.5
|
Splitter Ratio: 0.5
|
||||||
Tree Height: 696
|
Tree Height: 696
|
||||||
- Class: rviz_common/Selection
|
- Class: rviz_common/Selection
|
||||||
@@ -73,7 +71,58 @@ Visualization Manager:
|
|||||||
Durability Policy: Volatile
|
Durability Policy: Volatile
|
||||||
History Policy: Keep Last
|
History Policy: Keep Last
|
||||||
Reliability Policy: Reliable
|
Reliability Policy: Reliable
|
||||||
Value: /fire_extinguisher_emulator/fire_extinguisher_image
|
Value: /arctic_bot/crosshair_image
|
||||||
|
Value: true
|
||||||
|
- Class: rviz_default_plugins/TF
|
||||||
|
Enabled: true
|
||||||
|
Filter (blacklist): ""
|
||||||
|
Filter (whitelist): ""
|
||||||
|
Frame Timeout: 15
|
||||||
|
Frames:
|
||||||
|
All Enabled: true
|
||||||
|
back_left_wheel_link:
|
||||||
|
Value: true
|
||||||
|
back_right_wheel_link:
|
||||||
|
Value: true
|
||||||
|
base_footprint:
|
||||||
|
Value: true
|
||||||
|
base_link:
|
||||||
|
Value: true
|
||||||
|
camera_link:
|
||||||
|
Value: true
|
||||||
|
front_left_wheel_link:
|
||||||
|
Value: true
|
||||||
|
front_right_wheel_link:
|
||||||
|
Value: true
|
||||||
|
laser_link:
|
||||||
|
Value: true
|
||||||
|
map:
|
||||||
|
Value: true
|
||||||
|
odom:
|
||||||
|
Value: true
|
||||||
|
Marker Scale: 1
|
||||||
|
Name: TF
|
||||||
|
Show Arrows: true
|
||||||
|
Show Axes: true
|
||||||
|
Show Names: false
|
||||||
|
Tree:
|
||||||
|
map:
|
||||||
|
odom:
|
||||||
|
base_footprint:
|
||||||
|
base_link:
|
||||||
|
back_left_wheel_link:
|
||||||
|
{}
|
||||||
|
back_right_wheel_link:
|
||||||
|
{}
|
||||||
|
camera_link:
|
||||||
|
{}
|
||||||
|
front_left_wheel_link:
|
||||||
|
{}
|
||||||
|
front_right_wheel_link:
|
||||||
|
{}
|
||||||
|
laser_link:
|
||||||
|
{}
|
||||||
|
Update Interval: 0
|
||||||
Value: true
|
Value: true
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Global Options:
|
Global Options:
|
||||||
@@ -121,7 +170,7 @@ Visualization Manager:
|
|||||||
Views:
|
Views:
|
||||||
Current:
|
Current:
|
||||||
Class: rviz_default_plugins/Orbit
|
Class: rviz_default_plugins/Orbit
|
||||||
Distance: 10
|
Distance: 8.9044189453125
|
||||||
Enable Stereo Rendering:
|
Enable Stereo Rendering:
|
||||||
Stereo Eye Separation: 0.05999999865889549
|
Stereo Eye Separation: 0.05999999865889549
|
||||||
Stereo Focal Distance: 1
|
Stereo Focal Distance: 1
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import DeclareLaunchArgument
|
||||||
|
from launch.substitutions import (
|
||||||
|
PathJoinSubstitution,
|
||||||
|
LaunchConfiguration,
|
||||||
|
TextSubstitution,
|
||||||
|
)
|
||||||
|
from launch_ros.substitutions import FindPackageShare
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
from launch_ros.descriptions import ParameterFile
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
pkg_unity_robot_controller = FindPackageShare(package="").find("unity_robot_controller")
|
||||||
|
|
||||||
|
|
||||||
|
# Launch arguments
|
||||||
|
launch_args = [
|
||||||
|
DeclareLaunchArgument(
|
||||||
|
"use_sim_time",
|
||||||
|
description="Use clock from simulation",
|
||||||
|
default_value="True",
|
||||||
|
),
|
||||||
|
DeclareLaunchArgument(
|
||||||
|
"joy_model",
|
||||||
|
description="Used joystick model (default is T-29)",
|
||||||
|
default_value="T-29.yaml",
|
||||||
|
),
|
||||||
|
DeclareLaunchArgument(
|
||||||
|
"namespace", default_value="arctic_bot", description="Top-level namespace"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
joy_config = PathJoinSubstitution([pkg_unity_robot_controller, "config", "joy_config.yaml"])
|
||||||
|
|
||||||
|
model = LaunchConfiguration("joy_model")
|
||||||
|
joy_params = PathJoinSubstitution([pkg_unity_robot_controller,
|
||||||
|
"config",
|
||||||
|
model
|
||||||
|
])
|
||||||
|
|
||||||
|
joy_node = Node(
|
||||||
|
package="joy",
|
||||||
|
executable="joy_node",
|
||||||
|
name="joy",
|
||||||
|
namespace=LaunchConfiguration("namespace"),
|
||||||
|
output="both",
|
||||||
|
parameters=[
|
||||||
|
ParameterFile(joy_config),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
joy_control_node = Node(
|
||||||
|
package="unity_robot_controller",
|
||||||
|
executable="joy_control_node",
|
||||||
|
name="joy_control",
|
||||||
|
namespace=LaunchConfiguration("namespace"),
|
||||||
|
output="both",
|
||||||
|
parameters=[
|
||||||
|
ParameterFile(joy_params),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return LaunchDescription(
|
||||||
|
launch_args
|
||||||
|
+ [
|
||||||
|
joy_node,
|
||||||
|
joy_control_node,
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -54,6 +54,7 @@ def generate_launch_description():
|
|||||||
fe_emulator = Node(
|
fe_emulator = Node(
|
||||||
package="unity_robot_controller",
|
package="unity_robot_controller",
|
||||||
executable="fire_extinguisher_emulator_node",
|
executable="fire_extinguisher_emulator_node",
|
||||||
|
namespace=LaunchConfiguration("namespace"),
|
||||||
parameters=[
|
parameters=[
|
||||||
{"use_sim_time": LaunchConfiguration("use_sim_time")},
|
{"use_sim_time": LaunchConfiguration("use_sim_time")},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ setup(
|
|||||||
},
|
},
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'fire_extinguisher_emulator_node = unity_robot_controller.fire_extinguisher_emulator:main'
|
'fire_extinguisher_emulator_node = unity_robot_controller.fire_extinguisher_emulator:main',
|
||||||
|
'joy_control_node = unity_robot_controller.joy_control_node:main'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ class FireExtinguisherEmulatorNode(Node):
|
|||||||
self.crosshair_color = self.get_parameter('crosshair_color').value
|
self.crosshair_color = self.get_parameter('crosshair_color').value
|
||||||
|
|
||||||
# Publishers
|
# Publishers
|
||||||
self.fire_ext_image_pub = self.create_publisher(Image, '~/fire_extinguisher_image', 10)
|
self.fire_ext_image_pub = self.create_publisher(Image, 'crosshair_image', 10)
|
||||||
self.fire_ext_vector_pub = self.create_publisher(Vector3, '~/fire_extinguisher_vector', 10)
|
self.fire_ext_vector_pub = self.create_publisher(Vector3, 'crosshair_vector', 10)
|
||||||
|
|
||||||
# State variables
|
# State variables
|
||||||
self.target_x = 0.0
|
self.target_x = 0.0
|
||||||
@@ -48,23 +48,15 @@ class FireExtinguisherEmulatorNode(Node):
|
|||||||
|
|
||||||
# Timers and subscriptions
|
# Timers and subscriptions
|
||||||
self.target_timer = self.create_timer(0.01, self.target_timer_cb) # 100 Hz
|
self.target_timer = self.create_timer(0.01, self.target_timer_cb) # 100 Hz
|
||||||
self.create_subscription(Point, '~/target_point', self.target_cb, 10)
|
self.create_subscription(Point, 'target_point', self.target_cb, 10)
|
||||||
self.create_subscription(CameraInfo, 'camera_info', self.cam_info_cb, 10)
|
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)
|
||||||
|
|
||||||
# UI control subscription (optional)
|
|
||||||
self.create_subscription(Vector3, '~/ui_control', self.ui_control_cb, 10)
|
|
||||||
|
|
||||||
def target_cb(self, msg):
|
def target_cb(self, msg):
|
||||||
"""Received target point in normalized coordinates [-1, 1]"""
|
"""Received target point in normalized coordinates [-1, 1]"""
|
||||||
self.target_x = min(max(-1.0, msg.x), 1.0)
|
self.target_x = min(max(-1.0, msg.x), 1.0)
|
||||||
self.target_y = min(max(-1.0, msg.y), 1.0)
|
self.target_y = min(max(-1.0, msg.y), 1.0)
|
||||||
|
|
||||||
def ui_control_cb(self, msg):
|
|
||||||
"""Manual UI control for crosshair (normalized coordinates)"""
|
|
||||||
self.target_x = min(max(-1.0, msg.x), 1.0)
|
|
||||||
self.target_y = min(max(-1.0, msg.y), 1.0)
|
|
||||||
|
|
||||||
def cam_info_cb(self, msg):
|
def cam_info_cb(self, msg):
|
||||||
"""Save camera info and shutdown subscription"""
|
"""Save camera info and shutdown subscription"""
|
||||||
if self.camera_info is None:
|
if self.camera_info is None:
|
||||||
@@ -116,6 +108,30 @@ class FireExtinguisherEmulatorNode(Node):
|
|||||||
self.current_x += step_x
|
self.current_x += step_x
|
||||||
self.current_y += step_y
|
self.current_y += step_y
|
||||||
|
|
||||||
|
# Calculate 3D unit vector from camera frame
|
||||||
|
if self.fx > 0 and self.fy > 0:
|
||||||
|
# Pixel coordinates to normalized coordinates
|
||||||
|
px = self.current_x
|
||||||
|
py = self.current_y
|
||||||
|
|
||||||
|
# Inverse projection: (px, py) -> (x, y, z) in camera frame
|
||||||
|
x = (px - self.cx) / self.fx
|
||||||
|
y = (py - self.cy) / self.fy
|
||||||
|
z = 1.0
|
||||||
|
|
||||||
|
# Normalize to unit vector
|
||||||
|
norm = np.sqrt(x**2 + y**2 + z**2)
|
||||||
|
if norm > 0:
|
||||||
|
x /= norm
|
||||||
|
y /= norm
|
||||||
|
z /= norm
|
||||||
|
|
||||||
|
vector_msg = Vector3()
|
||||||
|
vector_msg.x = x
|
||||||
|
vector_msg.y = y
|
||||||
|
vector_msg.z = z
|
||||||
|
self.fire_ext_vector_pub.publish(vector_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"""
|
||||||
if self.camera_info is None:
|
if self.camera_info is None:
|
||||||
@@ -163,29 +179,7 @@ class FireExtinguisherEmulatorNode(Node):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.get_logger().error(f"Failed to publish image: {e}")
|
self.get_logger().error(f"Failed to publish image: {e}")
|
||||||
|
|
||||||
# Calculate 3D unit vector from camera frame
|
|
||||||
if self.fx > 0 and self.fy > 0:
|
|
||||||
# Pixel coordinates to normalized coordinates
|
|
||||||
px = self.current_x
|
|
||||||
py = self.current_y
|
|
||||||
|
|
||||||
# Inverse projection: (px, py) -> (x, y, z) in camera frame
|
|
||||||
x = (px - self.cx) / self.fx
|
|
||||||
y = (py - self.cy) / self.fy
|
|
||||||
z = 1.0
|
|
||||||
|
|
||||||
# Normalize to unit vector
|
|
||||||
norm = np.sqrt(x**2 + y**2 + z**2)
|
|
||||||
if norm > 0:
|
|
||||||
x /= norm
|
|
||||||
y /= norm
|
|
||||||
z /= norm
|
|
||||||
|
|
||||||
vector_msg = Vector3()
|
|
||||||
vector_msg.x = x
|
|
||||||
vector_msg.y = y
|
|
||||||
vector_msg.z = z
|
|
||||||
self.fire_ext_vector_pub.publish(vector_msg)
|
|
||||||
|
|
||||||
def destroy_node(self):
|
def destroy_node(self):
|
||||||
"""Clean up subscriptions"""
|
"""Clean up subscriptions"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class UnityRobotController(RobotController):
|
|||||||
self._ang_max = self._node.get_parameter('max_angular_speed').value
|
self._ang_max = self._node.get_parameter('max_angular_speed').value
|
||||||
|
|
||||||
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, '/fire_extinguisher_emulator/target_point', 10)
|
self._target_point_pub = self._node.create_publisher(Point, 'target_point', 10)
|
||||||
|
|
||||||
|
|
||||||
def send_speed_cmd(self, v, w):
|
def send_speed_cmd(self, v, w):
|
||||||
|
|||||||
Reference in New Issue
Block a user