From c14cc2b267528e5a1706f7201c7cf1fa2d76e7b9 Mon Sep 17 00:00:00 2001 From: yabuta Date: Thu, 23 Apr 2026 17:31:53 +0900 Subject: [PATCH 1/4] fix: do not launch external_signage from signage.launch.xml (#194) Co-authored-by: Claude Opus 4.7 (1M context) --- src/signage/launch/signage.launch.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/signage/launch/signage.launch.xml b/src/signage/launch/signage.launch.xml index fafc03f..0061216 100755 --- a/src/signage/launch/signage.launch.xml +++ b/src/signage/launch/signage.launch.xml @@ -7,8 +7,6 @@ - - From aa58eee3e7acc00242d96b3bf5c12311550df201 Mon Sep 17 00:00:00 2001 From: yabuta Date: Fri, 24 Apr 2026 10:35:25 +0900 Subject: [PATCH 2/4] delete external control --- src/signage/package.xml | 1 - src/signage/src/signage/ros_service_interface.py | 11 ----------- src/signage/src/signage/route_handler.py | 16 ---------------- 3 files changed, 28 deletions(-) diff --git a/src/signage/package.xml b/src/signage/package.xml index 349e057..04fdff7 100644 --- a/src/signage/package.xml +++ b/src/signage/package.xml @@ -11,7 +11,6 @@ signage_version diagnostic_updater - external_signage python-pulsectl-pip rclpy signage_fms_client diff --git a/src/signage/src/signage/ros_service_interface.py b/src/signage/src/signage/ros_service_interface.py index f390fe2..32c8700 100644 --- a/src/signage/src/signage/ros_service_interface.py +++ b/src/signage/src/signage/ros_service_interface.py @@ -16,7 +16,6 @@ import rclpy import time import threading -from std_srvs.srv import SetBool from autoware_adapi_v1_msgs.srv import AcceptStart from std_srvs.srv import Trigger @@ -35,7 +34,6 @@ def __init__(self, node, parameter_interface): self._cli_accept_start = self.__create_client( AcceptStart, "/api/motion/accept_start" ) - self._cli_trigger_external = self.__create_client(SetBool, "/signage/trigger_external") # service call function def accept_start(self): @@ -45,15 +43,6 @@ def accept_start(self): request = AcceptStart.Request() self.__service_call(self._cli_accept_start, request, True) - # service call function - def trigger_external_signage(self, on): - if self._parameter.debug_mode: - return - - request = SetBool.Request() - request.data = on - self.__service_call(self._cli_trigger_external, request, True) - # common denominator def __create_client(self, service_type, service_name): client = self._node.create_client(service_type, service_name) diff --git a/src/signage/src/signage/route_handler.py b/src/signage/src/signage/route_handler.py index d7dd41b..393fc65 100644 --- a/src/signage/src/signage/route_handler.py +++ b/src/signage/src/signage/route_handler.py @@ -54,7 +54,6 @@ def __init__( self._in_slowing_state = False self._announced_depart = False self._announced_arrive = False - self._trigger_external_signage = False self._processing_thread = False self.process_station_list_from_fms() @@ -298,20 +297,12 @@ def route_checker_callback(self): self._is_stopping = False self._announced_depart = False - if ( - not self._trigger_external_signage - and self._autoware.information.autoware_control - ): - self._service_interface.trigger_external_signage(True) - self._trigger_external_signage = True if ( not self._announce_engage and self._parameter.signage_stand_alone and self._autoware.information.autoware_control ): self._announce_interface.send_announce("engage") - self._service_interface.trigger_external_signage(True) - self._trigger_external_signage = True self._announce_engage = True elif self._autoware.information.route_state in [RouteState.ARRIVED, RouteState.UNSET]: # Check whether the vehicle arrive to goal @@ -321,13 +312,6 @@ def route_checker_callback(self): self._announce_engage = False self._announced_arrive = False - if ( - self._autoware.information.operation_mode != OperationModeState.AUTONOMOUS - or self._autoware.information.autoware_control != True - ) and self._trigger_external_signage: - self._service_interface.trigger_external_signage(False) - self._trigger_external_signage = False - if self._prev_route_state != RouteState.SET: if self._autoware.information.route_state == RouteState.SET: self.process_station_list_from_fms(force_update=True) From 31f8f72e11b6a83cad91d31944540dbd39420044 Mon Sep 17 00:00:00 2001 From: yabuta Date: Fri, 24 Apr 2026 12:10:43 +0900 Subject: [PATCH 3/4] feat: add use_external_signage flag and relax airport_mode QoS Introduce use_external_signage launch argument (default false) so the external_signage node and its ROS service wiring are only activated when explicitly enabled, preventing signage from blocking on a non-existent /signage/trigger_external service. Also relax /signage/airport_mode subscription durability to VOLATILE so external_signage can receive from both TRANSIENT_LOCAL and VOLATILE publishers. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../external_signage/external_signage_core.py | 10 +++++++++- src/signage/launch/signage.launch.xml | 3 +++ src/signage/package.xml | 1 + src/signage/src/signage/parameter_interface.py | 5 +++++ src/signage/src/signage/ros_service_interface.py | 14 ++++++++++++++ src/signage/src/signage/route_handler.py | 16 ++++++++++++++++ 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/external_signage/src/external_signage/external_signage_core.py b/src/external_signage/src/external_signage/external_signage_core.py index 5fa8552..2a1af64 100644 --- a/src/external_signage/src/external_signage/external_signage_core.py +++ b/src/external_signage/src/external_signage/external_signage_core.py @@ -148,10 +148,18 @@ def __init__(self, node): reliability=rclpy.qos.QoSReliabilityPolicy.RELIABLE, durability=rclpy.qos.QoSDurabilityPolicy.TRANSIENT_LOCAL, ) + airport_mode_qos = rclpy.qos.QoSProfile( + history=rclpy.qos.QoSHistoryPolicy.KEEP_LAST, + depth=10, + reliability=rclpy.qos.QoSReliabilityPolicy.RELIABLE, + durability=rclpy.qos.QoSDurabilityPolicy.VOLATILE, + ) node.create_service(SetBool, "/signage/trigger_external", self.trigger_external_signage) node.create_service(SetBool, "/signage/mode_change", self.change_mode) - self._sub_airport_mode = node.create_subscription(Bool, "/signage/airport_mode", self.change_airport_mode, api_qos) + self._sub_airport_mode = node.create_subscription( + Bool, "/signage/airport_mode", self.change_airport_mode, airport_mode_qos + ) self.mode_status_pub_ = node.create_publisher(Bool, "/signage/mode_status", api_qos) self.setting_pub_ = node.create_publisher(String, "/signage/external/settings", api_qos) self._sub_mrm = node.create_subscription( diff --git a/src/signage/launch/signage.launch.xml b/src/signage/launch/signage.launch.xml index 0061216..c6abe20 100755 --- a/src/signage/launch/signage.launch.xml +++ b/src/signage/launch/signage.launch.xml @@ -3,10 +3,13 @@ + + + diff --git a/src/signage/package.xml b/src/signage/package.xml index 04fdff7..349e057 100644 --- a/src/signage/package.xml +++ b/src/signage/package.xml @@ -11,6 +11,7 @@ signage_version diagnostic_updater + external_signage python-pulsectl-pip rclpy signage_fms_client diff --git a/src/signage/src/signage/parameter_interface.py b/src/signage/src/signage/parameter_interface.py index b3b359f..b58f002 100644 --- a/src/signage/src/signage/parameter_interface.py +++ b/src/signage/src/signage/parameter_interface.py @@ -8,6 +8,7 @@ @dataclass class SignageParameter: debug_mode: bool = False + use_external_signage: bool = False signage_stand_alone: bool = False ignore_manual_driving: bool = False ignore_disconnected: bool = False @@ -43,6 +44,7 @@ def __init__(self, node): self.announce_settings = AnnounceParameter() node.declare_parameter("debug_mode", False) + node.declare_parameter("use_external_signage", False) node.declare_parameter("signage_stand_alone", False) node.declare_parameter("ignore_disconnected", False) node.declare_parameter("ignore_manual_driving", False) @@ -60,6 +62,9 @@ def __init__(self, node): self.parameter.debug_mode = ( node.get_parameter("debug_mode").get_parameter_value().bool_value ) + self.parameter.use_external_signage = ( + node.get_parameter("use_external_signage").get_parameter_value().bool_value + ) self.parameter.signage_stand_alone = ( node.get_parameter("signage_stand_alone").get_parameter_value().bool_value ) diff --git a/src/signage/src/signage/ros_service_interface.py b/src/signage/src/signage/ros_service_interface.py index 32c8700..d813c14 100644 --- a/src/signage/src/signage/ros_service_interface.py +++ b/src/signage/src/signage/ros_service_interface.py @@ -16,6 +16,7 @@ import rclpy import time import threading +from std_srvs.srv import SetBool from autoware_adapi_v1_msgs.srv import AcceptStart from std_srvs.srv import Trigger @@ -34,6 +35,10 @@ def __init__(self, node, parameter_interface): self._cli_accept_start = self.__create_client( AcceptStart, "/api/motion/accept_start" ) + if self._parameter.use_external_signage: + self._cli_trigger_external = self.__create_client( + SetBool, "/signage/trigger_external" + ) # service call function def accept_start(self): @@ -43,6 +48,15 @@ def accept_start(self): request = AcceptStart.Request() self.__service_call(self._cli_accept_start, request, True) + # service call function + def trigger_external_signage(self, on): + if self._parameter.debug_mode or not self._parameter.use_external_signage: + return + + request = SetBool.Request() + request.data = on + self.__service_call(self._cli_trigger_external, request, True) + # common denominator def __create_client(self, service_type, service_name): client = self._node.create_client(service_type, service_name) diff --git a/src/signage/src/signage/route_handler.py b/src/signage/src/signage/route_handler.py index 393fc65..d7dd41b 100644 --- a/src/signage/src/signage/route_handler.py +++ b/src/signage/src/signage/route_handler.py @@ -54,6 +54,7 @@ def __init__( self._in_slowing_state = False self._announced_depart = False self._announced_arrive = False + self._trigger_external_signage = False self._processing_thread = False self.process_station_list_from_fms() @@ -297,12 +298,20 @@ def route_checker_callback(self): self._is_stopping = False self._announced_depart = False + if ( + not self._trigger_external_signage + and self._autoware.information.autoware_control + ): + self._service_interface.trigger_external_signage(True) + self._trigger_external_signage = True if ( not self._announce_engage and self._parameter.signage_stand_alone and self._autoware.information.autoware_control ): self._announce_interface.send_announce("engage") + self._service_interface.trigger_external_signage(True) + self._trigger_external_signage = True self._announce_engage = True elif self._autoware.information.route_state in [RouteState.ARRIVED, RouteState.UNSET]: # Check whether the vehicle arrive to goal @@ -312,6 +321,13 @@ def route_checker_callback(self): self._announce_engage = False self._announced_arrive = False + if ( + self._autoware.information.operation_mode != OperationModeState.AUTONOMOUS + or self._autoware.information.autoware_control != True + ) and self._trigger_external_signage: + self._service_interface.trigger_external_signage(False) + self._trigger_external_signage = False + if self._prev_route_state != RouteState.SET: if self._autoware.information.route_state == RouteState.SET: self.process_station_list_from_fms(force_update=True) From 5d5b014e6848741c40aea873d9e8e5b9696e3051 Mon Sep 17 00:00:00 2001 From: yabuta Date: Mon, 27 Apr 2026 11:03:03 +0900 Subject: [PATCH 4/4] refactor: let external_signage decide display state via operation_mode topic Remove the dependency on signage's /signage/trigger_external service by subscribing to /api/operation_mode/state directly in external_signage, mirroring the AutonomousStateDisplay pattern. signage no longer calls or declares the trigger client, and use_external_signage is now a launch-only switch that gates whether the external_signage node starts. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../external_signage/external_signage_core.py | 34 +++++++++++-------- src/signage/launch/signage.launch.xml | 1 - .../src/signage/parameter_interface.py | 5 --- .../src/signage/ros_service_interface.py | 14 -------- src/signage/src/signage/route_handler.py | 16 --------- 5 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/external_signage/src/external_signage/external_signage_core.py b/src/external_signage/src/external_signage/external_signage_core.py index 2a1af64..b5633d6 100644 --- a/src/external_signage/src/external_signage/external_signage_core.py +++ b/src/external_signage/src/external_signage/external_signage_core.py @@ -10,7 +10,7 @@ from std_msgs.msg import Bool from ament_index_python.packages import get_package_share_directory import external_signage.packet_tools as packet_tools -from autoware_adapi_v1_msgs.msg import MrmState +from autoware_adapi_v1_msgs.msg import MrmState, OperationModeState from std_msgs.msg import String from level4_mode_manager_msgs.msg import Level4DrivingStatus @@ -137,9 +137,10 @@ def __init__(self, node): self.node.get_logger().error(str(e)) self.autoware_status = { - "driving": True, + "driving": False, "mrm": False, } + self.is_autoware_launch = False # ros interface api_qos = rclpy.qos.QoSProfile( @@ -155,7 +156,6 @@ def __init__(self, node): durability=rclpy.qos.QoSDurabilityPolicy.VOLATILE, ) - node.create_service(SetBool, "/signage/trigger_external", self.trigger_external_signage) node.create_service(SetBool, "/signage/mode_change", self.change_mode) self._sub_airport_mode = node.create_subscription( Bool, "/signage/airport_mode", self.change_airport_mode, airport_mode_qos @@ -168,6 +168,12 @@ def __init__(self, node): self.sub_mrm_callback, api_qos, ) + self._sub_operation_mode = node.create_subscription( + OperationModeState, + "/api/operation_mode/state", + self.sub_operation_mode_callback, + api_qos, + ) self._sub_is_driving_level = node.create_subscription( Level4DrivingStatus, @@ -233,29 +239,27 @@ def send_data(self, display_key, data_key): sender = DataSender(self.bus, self.parser, self.protocol, self.node.get_logger()) sender.send(data, ack_query_ack, ack_data_chunk) - # Lv4のときは自動運転中かどうかで表示を変更する。Lv2のときは変更しない - def trigger_external_signage(self, request, response): + # operation modeを受け取り、Lv4のときは自動運転中かどうかで表示を変更する。Lv2のときは変更しない + def sub_operation_mode_callback(self, msg): try: - self.autoware_status["driving"] = request.data + self.is_autoware_launch = True + self.autoware_status["driving"] = msg.mode == OperationModeState.AUTONOMOUS if self._settings["in_experiment"]: - return response + return elif self._settings["airport"]: if self.autoware_status["mrm"]: self.display_signage("mrm") + elif self.autoware_status["driving"]: + self.display_signage("auto") else: - if request.data: - self.display_signage("auto") - else: - self.display_signage("null") + self.display_signage("null") else: - if request.data: + if self.autoware_status["driving"]: self.display_signage("auto") else: self.display_signage("null") - response.success = True except Exception as e: - self.node.get_logger().error(str(e)) - return response + self.node.get_logger().error("Unable to get the operation mode, ERROR: " + str(e)) # MRMが発生していて空港モードの場合は「緊急停止中」表示にする def sub_mrm_callback(self, msg): diff --git a/src/signage/launch/signage.launch.xml b/src/signage/launch/signage.launch.xml index c6abe20..a708160 100755 --- a/src/signage/launch/signage.launch.xml +++ b/src/signage/launch/signage.launch.xml @@ -7,7 +7,6 @@ - diff --git a/src/signage/src/signage/parameter_interface.py b/src/signage/src/signage/parameter_interface.py index b58f002..b3b359f 100644 --- a/src/signage/src/signage/parameter_interface.py +++ b/src/signage/src/signage/parameter_interface.py @@ -8,7 +8,6 @@ @dataclass class SignageParameter: debug_mode: bool = False - use_external_signage: bool = False signage_stand_alone: bool = False ignore_manual_driving: bool = False ignore_disconnected: bool = False @@ -44,7 +43,6 @@ def __init__(self, node): self.announce_settings = AnnounceParameter() node.declare_parameter("debug_mode", False) - node.declare_parameter("use_external_signage", False) node.declare_parameter("signage_stand_alone", False) node.declare_parameter("ignore_disconnected", False) node.declare_parameter("ignore_manual_driving", False) @@ -62,9 +60,6 @@ def __init__(self, node): self.parameter.debug_mode = ( node.get_parameter("debug_mode").get_parameter_value().bool_value ) - self.parameter.use_external_signage = ( - node.get_parameter("use_external_signage").get_parameter_value().bool_value - ) self.parameter.signage_stand_alone = ( node.get_parameter("signage_stand_alone").get_parameter_value().bool_value ) diff --git a/src/signage/src/signage/ros_service_interface.py b/src/signage/src/signage/ros_service_interface.py index d813c14..32c8700 100644 --- a/src/signage/src/signage/ros_service_interface.py +++ b/src/signage/src/signage/ros_service_interface.py @@ -16,7 +16,6 @@ import rclpy import time import threading -from std_srvs.srv import SetBool from autoware_adapi_v1_msgs.srv import AcceptStart from std_srvs.srv import Trigger @@ -35,10 +34,6 @@ def __init__(self, node, parameter_interface): self._cli_accept_start = self.__create_client( AcceptStart, "/api/motion/accept_start" ) - if self._parameter.use_external_signage: - self._cli_trigger_external = self.__create_client( - SetBool, "/signage/trigger_external" - ) # service call function def accept_start(self): @@ -48,15 +43,6 @@ def accept_start(self): request = AcceptStart.Request() self.__service_call(self._cli_accept_start, request, True) - # service call function - def trigger_external_signage(self, on): - if self._parameter.debug_mode or not self._parameter.use_external_signage: - return - - request = SetBool.Request() - request.data = on - self.__service_call(self._cli_trigger_external, request, True) - # common denominator def __create_client(self, service_type, service_name): client = self._node.create_client(service_type, service_name) diff --git a/src/signage/src/signage/route_handler.py b/src/signage/src/signage/route_handler.py index d7dd41b..393fc65 100644 --- a/src/signage/src/signage/route_handler.py +++ b/src/signage/src/signage/route_handler.py @@ -54,7 +54,6 @@ def __init__( self._in_slowing_state = False self._announced_depart = False self._announced_arrive = False - self._trigger_external_signage = False self._processing_thread = False self.process_station_list_from_fms() @@ -298,20 +297,12 @@ def route_checker_callback(self): self._is_stopping = False self._announced_depart = False - if ( - not self._trigger_external_signage - and self._autoware.information.autoware_control - ): - self._service_interface.trigger_external_signage(True) - self._trigger_external_signage = True if ( not self._announce_engage and self._parameter.signage_stand_alone and self._autoware.information.autoware_control ): self._announce_interface.send_announce("engage") - self._service_interface.trigger_external_signage(True) - self._trigger_external_signage = True self._announce_engage = True elif self._autoware.information.route_state in [RouteState.ARRIVED, RouteState.UNSET]: # Check whether the vehicle arrive to goal @@ -321,13 +312,6 @@ def route_checker_callback(self): self._announce_engage = False self._announced_arrive = False - if ( - self._autoware.information.operation_mode != OperationModeState.AUTONOMOUS - or self._autoware.information.autoware_control != True - ) and self._trigger_external_signage: - self._service_interface.trigger_external_signage(False) - self._trigger_external_signage = False - if self._prev_route_state != RouteState.SET: if self._autoware.information.route_state == RouteState.SET: self.process_station_list_from_fms(force_update=True)