check: 查看机器人速度topic信息
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
<exec_depend>launch</exec_depend>
|
<exec_depend>launch</exec_depend>
|
||||||
<exec_depend>launch_ros</exec_depend>
|
<exec_depend>launch_ros</exec_depend>
|
||||||
<exec_depend>rclpy</exec_depend>
|
<exec_depend>rclpy</exec_depend>
|
||||||
|
<exec_depend>rosidl_runtime_py</exec_depend>
|
||||||
<exec_depend>teleop_twist_joy</exec_depend>
|
<exec_depend>teleop_twist_joy</exec_depend>
|
||||||
<exec_depend>teleop_twist_keyboard</exec_depend>
|
<exec_depend>teleop_twist_keyboard</exec_depend>
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ setup(
|
|||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'cmd_vel_udp_sender = udp_teleop_bridge.cmd_vel_udp_sender:main',
|
'cmd_vel_udp_sender = udp_teleop_bridge.cmd_vel_udp_sender:main',
|
||||||
'udp_cmd_vel_receiver = udp_teleop_bridge.udp_cmd_vel_receiver:main',
|
'udp_cmd_vel_receiver = udp_teleop_bridge.udp_cmd_vel_receiver:main',
|
||||||
|
'topic_status_reader = udp_teleop_bridge.topic_status_reader:main',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
"""Subscribe to a ROS 2 topic with runtime type discovery and print messages."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from rosidl_runtime_py.convert import message_to_ordereddict
|
||||||
|
from rosidl_runtime_py.utilities import get_message
|
||||||
|
|
||||||
|
|
||||||
|
WAIT_LOG_INTERVAL_SEC = 5.0
|
||||||
|
|
||||||
|
|
||||||
|
class TopicStatusReader(Node):
|
||||||
|
"""Wait for a topic to appear, subscribe to it, and print each message."""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__('topic_status_reader')
|
||||||
|
|
||||||
|
self.declare_parameter('topic', '/hrc/robot/cmd_vel_status')
|
||||||
|
self.declare_parameter('qos_depth', 10)
|
||||||
|
self.declare_parameter('poll_interval_sec', 0.5)
|
||||||
|
|
||||||
|
self._topic = str(self.get_parameter('topic').value).strip()
|
||||||
|
self._qos_depth = int(self.get_parameter('qos_depth').value)
|
||||||
|
self._poll_interval_sec = float(self.get_parameter('poll_interval_sec').value)
|
||||||
|
|
||||||
|
if not self._topic:
|
||||||
|
raise ValueError('topic must not be empty')
|
||||||
|
if self._qos_depth <= 0:
|
||||||
|
raise ValueError('qos_depth must be > 0')
|
||||||
|
if self._poll_interval_sec <= 0.0:
|
||||||
|
raise ValueError('poll_interval_sec must be > 0')
|
||||||
|
|
||||||
|
self._topic_type: str | None = None
|
||||||
|
self._subscription = None
|
||||||
|
self._message_count = 0
|
||||||
|
self._last_wait_log_monotonic = 0.0
|
||||||
|
|
||||||
|
self._poll_timer = self.create_timer(self._poll_interval_sec, self._ensure_subscription)
|
||||||
|
self._ensure_subscription()
|
||||||
|
|
||||||
|
def _discover_topic_types(self) -> list[str]:
|
||||||
|
for topic_name, topic_types in self.get_topic_names_and_types():
|
||||||
|
if topic_name == self._topic:
|
||||||
|
return list(topic_types)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _log_waiting(self) -> None:
|
||||||
|
now = time.monotonic()
|
||||||
|
if (now - self._last_wait_log_monotonic) < WAIT_LOG_INTERVAL_SEC:
|
||||||
|
return
|
||||||
|
self._last_wait_log_monotonic = now
|
||||||
|
self.get_logger().info(f'Waiting for topic {self._topic} to appear...')
|
||||||
|
|
||||||
|
def _ensure_subscription(self) -> None:
|
||||||
|
if self._subscription is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
topic_types = self._discover_topic_types()
|
||||||
|
if not topic_types:
|
||||||
|
self._log_waiting()
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(topic_types) > 1:
|
||||||
|
joined = ', '.join(topic_types)
|
||||||
|
self.get_logger().warning(
|
||||||
|
f'Topic {self._topic} reports multiple types ({joined}); using {topic_types[0]}'
|
||||||
|
)
|
||||||
|
|
||||||
|
self._topic_type = topic_types[0]
|
||||||
|
try:
|
||||||
|
message_type = get_message(self._topic_type)
|
||||||
|
except Exception as exc:
|
||||||
|
self.get_logger().error(
|
||||||
|
f'Failed to import message type {self._topic_type} for {self._topic}: {exc}'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._subscription = self.create_subscription(
|
||||||
|
message_type,
|
||||||
|
self._topic,
|
||||||
|
self._handle_message,
|
||||||
|
self._qos_depth,
|
||||||
|
)
|
||||||
|
self._poll_timer.cancel()
|
||||||
|
self.get_logger().info(
|
||||||
|
f'Subscribed to {self._topic} with type {self._topic_type} (qos_depth={self._qos_depth})'
|
||||||
|
)
|
||||||
|
|
||||||
|
def _format_message(self, msg: Any) -> str:
|
||||||
|
try:
|
||||||
|
payload = message_to_ordereddict(msg)
|
||||||
|
except Exception:
|
||||||
|
return str(msg)
|
||||||
|
return json.dumps(payload, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def _handle_message(self, msg: Any) -> None:
|
||||||
|
self._message_count += 1
|
||||||
|
received_at = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
topic_type = self._topic_type or type(msg).__name__
|
||||||
|
rendered = self._format_message(msg)
|
||||||
|
print(
|
||||||
|
f'[{received_at}] #{self._message_count} {self._topic} ({topic_type})\n{rendered}\n',
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args: list[str] | None = None) -> None:
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = TopicStatusReader()
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
Reference in New Issue
Block a user