forked from chiyu1468/AirTrapMine
1. (modify) 更新 ros2 對於 GNSS topic 的發佈, 使其包涵 epv eph 衛星數與 fix_type
2. (adding) 新增相應的 msg 檔案與修改 CMakeLists, package 3. (adding) test_vehicleStatusPublisher 相對應修正
This commit is contained in:
@@ -9,13 +9,15 @@ endif()
|
|||||||
find_package(ament_cmake REQUIRED)
|
find_package(ament_cmake REQUIRED)
|
||||||
find_package(rosidl_default_generators REQUIRED)
|
find_package(rosidl_default_generators REQUIRED)
|
||||||
find_package(std_msgs REQUIRED)
|
find_package(std_msgs REQUIRED)
|
||||||
|
find_package(builtin_interfaces REQUIRED)
|
||||||
|
|
||||||
rosidl_generate_interfaces(${PROJECT_NAME}
|
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||||
"msg/AttitudeRaw.msg"
|
"msg/AttitudeRaw.msg"
|
||||||
|
"msg/GnssRaw.msg"
|
||||||
"srv/MavPing.srv"
|
"srv/MavPing.srv"
|
||||||
"srv/MavCommandLong.srv"
|
"srv/MavCommandLong.srv"
|
||||||
"srv/MavPositionTargetGlobalInt.srv"
|
"srv/MavPositionTargetGlobalInt.srv"
|
||||||
DEPENDENCIES std_msgs
|
DEPENDENCIES std_msgs builtin_interfaces
|
||||||
)
|
)
|
||||||
|
|
||||||
ament_package()
|
ament_package()
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
builtin_interfaces/Time stamp
|
||||||
|
float64 latitude
|
||||||
|
float64 longitude
|
||||||
|
float64 altitude
|
||||||
|
uint8 fix_type
|
||||||
|
uint8 satellites_visible
|
||||||
|
float32 eph
|
||||||
|
float32 epv
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
<build_depend>std_msgs</build_depend>
|
<build_depend>std_msgs</build_depend>
|
||||||
<exec_depend>std_msgs</exec_depend>
|
<exec_depend>std_msgs</exec_depend>
|
||||||
|
<build_depend>builtin_interfaces</build_depend>
|
||||||
|
<exec_depend>builtin_interfaces</exec_depend>
|
||||||
|
|
||||||
<export>
|
<export>
|
||||||
<build_type>ament_cmake</build_type>
|
<build_type>ament_cmake</build_type>
|
||||||
|
|||||||
@@ -1145,7 +1145,7 @@ class Orchestrator:
|
|||||||
self.fc_ros_manager = mros.ros2_manager
|
self.fc_ros_manager = mros.ros2_manager
|
||||||
self.fc_ros_manager.initialize()
|
self.fc_ros_manager.initialize()
|
||||||
self.fc_ros_manager.status_publisher.rate_controller.topic_intervals = {
|
self.fc_ros_manager.status_publisher.rate_controller.topic_intervals = {
|
||||||
'position': 1.0,
|
'position_gnss': 1.0,
|
||||||
'position_ned': 1.0,
|
'position_ned': 1.0,
|
||||||
'attitude': 1.0,
|
'attitude': 1.0,
|
||||||
'velocity': 0.0,
|
'velocity': 0.0,
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class mavlink_bridge:
|
|||||||
"""初始化訊息處理器映射表,提高處理效率"""
|
"""初始化訊息處理器映射表,提高處理效率"""
|
||||||
self.message_handlers = {
|
self.message_handlers = {
|
||||||
0: self._handle_heartbeat, # HEARTBEAT
|
0: self._handle_heartbeat, # HEARTBEAT
|
||||||
|
24: self._handle_gps_raw_int, # GPS_RAW_INT
|
||||||
30: self._handle_attitude, # ATTITUDE
|
30: self._handle_attitude, # ATTITUDE
|
||||||
32: self._handle_local_position, # LOCAL_POSITION_NED
|
32: self._handle_local_position, # LOCAL_POSITION_NED
|
||||||
33: self._handle_global_position, # GLOBAL_POSITION_INT
|
33: self._handle_global_position, # GLOBAL_POSITION_INT
|
||||||
@@ -239,6 +240,14 @@ class mavlink_bridge:
|
|||||||
component.status.position.relative_altitude = msg.relative_alt / 1000.0 # 轉換為公尺
|
component.status.position.relative_altitude = msg.relative_alt / 1000.0 # 轉換為公尺
|
||||||
component.status.position.timestamp = timestamp
|
component.status.position.timestamp = timestamp
|
||||||
|
|
||||||
|
def _handle_gps_raw_int(self, vehicle, component, msg, timestamp):
|
||||||
|
"""處理 GPS_RAW_INT 訊息 (msg_id: 24)"""
|
||||||
|
component.status.gps.fix_type = msg.fix_type
|
||||||
|
component.status.gps.satellites_visible = msg.satellites_visible
|
||||||
|
component.status.gps.eph = None if msg.eph == 65535 else msg.eph
|
||||||
|
component.status.gps.epv = None if msg.epv == 65535 else msg.epv
|
||||||
|
component.status.gps.timestamp = timestamp
|
||||||
|
|
||||||
def _handle_vfr_hud(self, vehicle, component, msg, timestamp):
|
def _handle_vfr_hud(self, vehicle, component, msg, timestamp):
|
||||||
"""處理 VFR_HUD 訊息 (msg_id: 74)"""
|
"""處理 VFR_HUD 訊息 (msg_id: 74)"""
|
||||||
component.status.vfr.airspeed = msg.airspeed
|
component.status.vfr.airspeed = msg.airspeed
|
||||||
@@ -386,7 +395,9 @@ class mavlink_object:
|
|||||||
self.MAVLink = mav_common.MAVLink(self.mavlinkPipeline, srcSystem=254, srcComponent=191)
|
self.MAVLink = mav_common.MAVLink(self.mavlinkPipeline, srcSystem=254, srcComponent=191)
|
||||||
|
|
||||||
# 記錄訊息過濾類型 (可選)
|
# 記錄訊息過濾類型 (可選)
|
||||||
self.bridge_msg_types = set([0, 30, 32, 33, 74, 147]) # 0 HEARTBEAT, 30 ATTITUDE, 33 GLOBAL_POSITION_INT, 74 VFR_HUD, 147 BATTERY_STATUS, 32 LOCAL_POSITION_NED
|
# 0 HEARTBEAT, 24 GPS_RAW_INT, 30 ATTITUDE,
|
||||||
|
# 32 LOCAL_POSITION_NED, 33 GLOBAL_POSITION_INT, 74 VFR_HUD, 147 BATTERY_STATUS
|
||||||
|
self.bridge_msg_types = set([0, 24, 30, 32, 33, 74, 147])
|
||||||
self.return_msg_types = set([])
|
self.return_msg_types = set([])
|
||||||
|
|
||||||
# 轉發到別的 mavlink object 作為目標端口 的列表
|
# 轉發到別的 mavlink object 作為目標端口 的列表
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class PublishRateController:
|
|||||||
# 注意 這邊是定義區 不要把參數寫在這裡 所以預設全部關閉
|
# 注意 這邊是定義區 不要把參數寫在這裡 所以預設全部關閉
|
||||||
# 以這個專案 請看 mainOrchestrator.py 的 Orchestrator 初始化階段
|
# 以這個專案 請看 mainOrchestrator.py 的 Orchestrator 初始化階段
|
||||||
self.topic_intervals = {
|
self.topic_intervals = {
|
||||||
'position': 0.0, # GNSS位置
|
'position_gnss': 0.0, # GNSS位置
|
||||||
'position_ned': 0.0, # LOCAL_POSITION_NED (位置+速度)
|
'position_ned': 0.0, # LOCAL_POSITION_NED (位置+速度)
|
||||||
'attitude': 0.0, # 姿態 (pitch yaw row 與其加速狀態)
|
'attitude': 0.0, # 姿態 (pitch yaw row 與其加速狀態)
|
||||||
'velocity': 0.0, # 速度 (已經包含在 vfr_hud 未來移除)
|
'velocity': 0.0, # 速度 (已經包含在 vfr_hud 未來移除)
|
||||||
@@ -171,7 +171,7 @@ class VehicleStatusPublisher(Node):
|
|||||||
# 例如:self._publish_ekf_status(sysid, status)
|
# 例如:self._publish_ekf_status(sysid, status)
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# 發布各種狀態(通過頻率控制器判斷是否發布)
|
# 發布各種狀態(通過頻率控制器判斷是否發布)
|
||||||
self._publish_position(sysid, status)
|
self._publish_position_gnss(sysid, status)
|
||||||
self._publish_position_ned(sysid, status)
|
self._publish_position_ned(sysid, status)
|
||||||
self._publish_attitude(sysid, status)
|
self._publish_attitude(sysid, status)
|
||||||
self._publish_velocity(sysid, status)
|
self._publish_velocity(sysid, status)
|
||||||
@@ -202,9 +202,9 @@ class VehicleStatusPublisher(Node):
|
|||||||
logger.info(f"Created publisher: {topic_name}")
|
logger.info(f"Created publisher: {topic_name}")
|
||||||
return self.fc_publishers[key]
|
return self.fc_publishers[key]
|
||||||
|
|
||||||
def _publish_position(self, sysid: int, status: mvv.ComponentStatus):
|
def _publish_position_gnss(self, sysid: int, status: mvv.ComponentStatus):
|
||||||
"""發布 GPS 位置"""
|
"""發布 GPS 位置"""
|
||||||
if not self.rate_controller.should_publish(sysid, 'position'):
|
if not self.rate_controller.should_publish(sysid, 'position_gnss'):
|
||||||
return
|
return
|
||||||
|
|
||||||
pos = status.position
|
pos = status.position
|
||||||
@@ -212,22 +212,27 @@ class VehicleStatusPublisher(Node):
|
|||||||
return
|
return
|
||||||
|
|
||||||
publisher = self._get_or_create_publisher(
|
publisher = self._get_or_create_publisher(
|
||||||
sysid, 'position', sensor_msgs.msg.NavSatFix
|
sysid, 'position_gnss', fcmsg.GnssRaw
|
||||||
)
|
)
|
||||||
|
|
||||||
# 檢查是否有訂閱者
|
# 檢查是否有訂閱者
|
||||||
if publisher.get_subscription_count() == 0:
|
if publisher.get_subscription_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = sensor_msgs.msg.NavSatFix()
|
msg = fcmsg.GnssRaw()
|
||||||
msg.latitude = pos.latitude
|
msg.stamp = self.get_clock().now().to_msg()
|
||||||
msg.longitude = pos.longitude
|
msg.latitude = float(pos.latitude)
|
||||||
msg.altitude = pos.altitude if pos.altitude is not None else 0.0
|
msg.longitude = float(pos.longitude)
|
||||||
|
msg.altitude = float(pos.altitude) if pos.altitude is not None else 0.0
|
||||||
|
|
||||||
# GPS 狀態資訊
|
# GPS 狀態資訊
|
||||||
gps = status.gps
|
gps = status.gps
|
||||||
if gps.fix_type is not None:
|
if gps.fix_type is not None:
|
||||||
msg.status.status = gps.fix_type - 1 # MAVLink fix_type 轉 NavSatStatus
|
msg.fix_type = int(gps.fix_type)
|
||||||
|
if gps.satellites_visible is not None:
|
||||||
|
msg.satellites_visible = int(gps.satellites_visible)
|
||||||
|
msg.eph = float(gps.eph) / 100.0 if gps.eph is not None else float('nan')
|
||||||
|
msg.epv = float(gps.epv) / 100.0 if gps.epv is not None else float('nan')
|
||||||
|
|
||||||
publisher.publish(msg)
|
publisher.publish(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import std_msgs.msg
|
|||||||
import sensor_msgs.msg
|
import sensor_msgs.msg
|
||||||
import geometry_msgs.msg
|
import geometry_msgs.msg
|
||||||
import mavros_msgs.msg
|
import mavros_msgs.msg
|
||||||
|
import fc_interfaces.msg
|
||||||
|
|
||||||
# 專案 imports
|
# 專案 imports
|
||||||
from ..fc_network_adapter.mavlinkROS2Nodes import (
|
from ..fc_network_adapter.mavlinkROS2Nodes import (
|
||||||
@@ -39,7 +40,7 @@ class TestSubscriber(Node):
|
|||||||
|
|
||||||
self.sysid = sysid
|
self.sysid = sysid
|
||||||
self.received_messages = {
|
self.received_messages = {
|
||||||
'position': [],
|
'position_gnss': [],
|
||||||
'attitude': [],
|
'attitude': [],
|
||||||
'velocity': [],
|
'velocity': [],
|
||||||
'battery': [],
|
'battery': [],
|
||||||
@@ -60,9 +61,9 @@ class TestSubscriber(Node):
|
|||||||
|
|
||||||
# Position
|
# Position
|
||||||
self.create_subscription(
|
self.create_subscription(
|
||||||
sensor_msgs.msg.NavSatFix,
|
fc_interfaces.msg.GnssRaw,
|
||||||
f'{base_topic}/position',
|
f'{base_topic}/position_gnss',
|
||||||
lambda msg: self._on_message('position', msg),
|
lambda msg: self._on_message('position_gnss', msg),
|
||||||
10
|
10
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@ class TestSubscriber(Node):
|
|||||||
|
|
||||||
def _format_msg(self, topic_name: str, msg) -> str:
|
def _format_msg(self, topic_name: str, msg) -> str:
|
||||||
"""格式化消息以便顯示"""
|
"""格式化消息以便顯示"""
|
||||||
if topic_name == 'position':
|
if topic_name == 'position_gnss':
|
||||||
return f"lat={msg.latitude:.6f}, lon={msg.longitude:.6f}, alt={msg.altitude:.2f}m"
|
return f"lat={msg.latitude:.6f}, lon={msg.longitude:.6f}, alt={msg.altitude:.2f}m"
|
||||||
elif topic_name == 'attitude':
|
elif topic_name == 'attitude':
|
||||||
return f"quat=({msg.orientation.x:.3f}, {msg.orientation.y:.3f}, {msg.orientation.z:.3f}, {msg.orientation.w:.3f})"
|
return f"quat=({msg.orientation.x:.3f}, {msg.orientation.y:.3f}, {msg.orientation.z:.3f}, {msg.orientation.w:.3f})"
|
||||||
@@ -264,7 +265,7 @@ def test_basic_publishing():
|
|||||||
# 檢查收到的消息
|
# 檢查收到的消息
|
||||||
print("\n--- 消息統計 ---")
|
print("\n--- 消息統計 ---")
|
||||||
total_messages = 0
|
total_messages = 0
|
||||||
for topic in ['position', 'attitude', 'velocity', 'battery', 'vfr_hud', 'mode', 'summary']:
|
for topic in ['position_gnss', 'attitude', 'velocity', 'battery', 'vfr_hud', 'mode', 'summary']:
|
||||||
count = test_sub.get_message_count(topic)
|
count = test_sub.get_message_count(topic)
|
||||||
total_messages += count
|
total_messages += count
|
||||||
print(f" {topic:15s}: {count:3d} 條消息")
|
print(f" {topic:15s}: {count:3d} 條消息")
|
||||||
@@ -304,7 +305,7 @@ def test_frequency_control():
|
|||||||
# 修改頻率設定
|
# 修改頻率設定
|
||||||
publisher_node = ros2_manager.status_publisher
|
publisher_node = ros2_manager.status_publisher
|
||||||
publisher_node.rate_controller.topic_intervals = {
|
publisher_node.rate_controller.topic_intervals = {
|
||||||
'position': 1.5,
|
'position_gnss': 1.5,
|
||||||
'attitude': 1.0,
|
'attitude': 1.0,
|
||||||
'velocity': 1.0,
|
'velocity': 1.0,
|
||||||
'battery': 1.0,
|
'battery': 1.0,
|
||||||
@@ -329,7 +330,7 @@ def test_frequency_control():
|
|||||||
print("預期:position 約 2 條 (0.67Hz),attitude/battery/velocity 約 3 條 (1Hz),vfr_hud 約 6 條 (2Hz) mode/summary 不發布")
|
print("預期:position 約 2 條 (0.67Hz),attitude/battery/velocity 約 3 條 (1Hz),vfr_hud 約 6 條 (2Hz) mode/summary 不發布")
|
||||||
|
|
||||||
print("2Hz Topics (預期 ~6 條):")
|
print("2Hz Topics (預期 ~6 條):")
|
||||||
for topic in ['position', 'attitude', 'velocity', 'vfr_hud']:
|
for topic in ['position_gnss', 'attitude', 'velocity', 'vfr_hud']:
|
||||||
count = test_sub.get_message_count(topic)
|
count = test_sub.get_message_count(topic)
|
||||||
print(f" {topic:15s}: {count:3d} 條")
|
print(f" {topic:15s}: {count:3d} 條")
|
||||||
for topic in ['battery', 'mode', 'summary']:
|
for topic in ['battery', 'mode', 'summary']:
|
||||||
|
|||||||
Reference in New Issue
Block a user