From f92eedbf783699e24c5045c089ca5ca086bd4906 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 16:38:08 +0000
Subject: [PATCH 1/7] Initial plan
From bd454befa32fb2e4f38e7dfb78d747d7920b2786 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 16:50:02 +0000
Subject: [PATCH 2/7] Migrate bitbots_vision to use generate_parameter_library
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
.gitignore | 3 +
bitbots_vision/bitbots_vision/__init__.py | 10 ++
bitbots_vision/bitbots_vision/params.py | 111 ----------------
bitbots_vision/bitbots_vision/vision.py | 81 +++++++++---
bitbots_vision/config/vision_parameters.yaml | 126 +++++++++++++++++++
bitbots_vision/package.xml | 1 +
bitbots_vision/setup.py | 6 +
7 files changed, 207 insertions(+), 131 deletions(-)
delete mode 100755 bitbots_vision/bitbots_vision/params.py
create mode 100644 bitbots_vision/config/vision_parameters.yaml
diff --git a/.gitignore b/.gitignore
index 8c79b505c..5f4185ba9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -215,6 +215,9 @@ log/
.pyenv2/*
.pyenv3/*
+# Generated parameter library files
+**/vision_parameters/
+
ansible_robots/*
doc_internal/*
doku/*
diff --git a/bitbots_vision/bitbots_vision/__init__.py b/bitbots_vision/bitbots_vision/__init__.py
index 4d2e15007..4cefb79b8 100644
--- a/bitbots_vision/bitbots_vision/__init__.py
+++ b/bitbots_vision/bitbots_vision/__init__.py
@@ -1,4 +1,14 @@
# Setting up runtime type checking for this package
from beartype.claw import beartype_this_package
+from rclpy.node import Node
+
+from bitbots_vision.vision_parameters import bitbots_vision as parameters
beartype_this_package()
+
+
+class NodeWithConfig(Node):
+ def __init__(self, name: str) -> None:
+ super().__init__(name)
+ self.param_listener = parameters.ParamListener(self)
+ self.config = self.param_listener.get_params()
diff --git a/bitbots_vision/bitbots_vision/params.py b/bitbots_vision/bitbots_vision/params.py
deleted file mode 100755
index 3fc386011..000000000
--- a/bitbots_vision/bitbots_vision/params.py
+++ /dev/null
@@ -1,111 +0,0 @@
-from rcl_interfaces.msg import FloatingPointRange, IntegerRange, ParameterDescriptor, ParameterType
-
-
-class ParameterGenerator: # TODO own file
- def __init__(self):
- self.param_cache = []
-
- def declare_params(self, node):
- for param in self.param_cache:
- node.declare_parameter(*param)
-
- def add(self, param_name, param_type=None, default=None, description=None, min=None, max=None, step=None):
- describtor = ParameterDescriptor()
- describtor.name = param_name
- if description is None:
- describtor.description = param_name
- else:
- describtor.description = description
-
- if param_type is None and default is not None:
- param_type = type(default)
-
- py2ros_param_type = {
- None: ParameterType.PARAMETER_NOT_SET,
- bool: ParameterType.PARAMETER_BOOL,
- int: ParameterType.PARAMETER_INTEGER,
- float: ParameterType.PARAMETER_DOUBLE,
- str: ParameterType.PARAMETER_STRING,
- }
-
- param_type = py2ros_param_type.get(param_type, param_type)
-
- describtor.type = param_type
-
- if param_type == ParameterType.PARAMETER_INTEGER:
- if step is None:
- step = 1
- if all(x is not None or isinstance(x, int) for x in [min, max, step]):
- param_range = IntegerRange()
- param_range.from_value = min
- param_range.to_value = max
- param_range.step = step
- describtor.integer_range = [param_range]
-
- if param_type == ParameterType.PARAMETER_DOUBLE:
- if step is None:
- step = 0.01
- if all(x is not None for x in [min, max]):
- param_range = FloatingPointRange()
- param_range.from_value = float(min)
- param_range.to_value = float(max)
- param_range.step = float(step)
- describtor.floating_point_range = [param_range]
-
- type2default_default = {
- ParameterType.PARAMETER_NOT_SET: 0,
- ParameterType.PARAMETER_BOOL: False,
- ParameterType.PARAMETER_INTEGER: 0,
- ParameterType.PARAMETER_DOUBLE: 0.0,
- ParameterType.PARAMETER_STRING: "",
- }
-
- if default is None:
- default = type2default_default[param_type]
-
- self.param_cache.append((param_name, default, describtor))
-
-
-gen = ParameterGenerator()
-
-##########
-# Params #
-##########
-
-gen.add("component_ball_detection_active", bool, description="Activate/Deactivate the ball detection component")
-gen.add("component_debug_image_active", bool, description="Activate/Deactivate the debug image component")
-gen.add("component_field_detection_active", bool, description="Activate/Deactivate the field detection component")
-gen.add("component_goalpost_detection_active", bool, description="Activate/Deactivate the goalpost detection component")
-gen.add("component_line_detection_active", bool, description="Activate/Deactivate the line detection component")
-gen.add("component_robot_detection_active", bool, description="Activate/Deactivate the robot detection component")
-
-gen.add("ROS_img_msg_topic", str, description="ROS topic of the image message")
-gen.add("ROS_ball_msg_topic", str, description="ROS topic of the ball message")
-gen.add("ROS_goal_posts_msg_topic", str, description="ROS topic of the goal posts message")
-gen.add("ROS_robot_msg_topic", str, description="ROS topic of the robots message")
-gen.add("ROS_line_msg_topic", str, description="ROS topic of the line message")
-gen.add("ROS_line_mask_msg_topic", str, description="ROS topic of the line mask message")
-gen.add("ROS_debug_image_msg_topic", str, description="ROS topic of the debug image message")
-gen.add("ROS_field_mask_image_msg_topic", str, description="ROS topic of the field mask debug image message")
-
-gen.add("yoeo_model_path", str, description="Name of YOEO model")
-gen.add("yoeo_nms_threshold", float, description="YOEO Non-maximum suppression threshold", min=0.0, max=1.0)
-gen.add("yoeo_conf_threshold", float, description="YOEO confidence threshold", min=0.0, max=1.0)
-gen.add(
- "yoeo_framework",
- str,
- description="The neural network framework that should be used ['pytorch', 'openvino', 'onnx', 'tvm']",
-)
-
-gen.add(
- "ball_candidate_rating_threshold",
- float,
- description="A threshold for the minimum candidate rating",
- min=0.0,
- max=1.0,
-)
-gen.add(
- "ball_candidate_max_count", int, description="The maximum number of balls that should be published", min=0, max=50
-)
-
-gen.add("caching", bool, description="Used to deactivate caching for profiling reasons")
diff --git a/bitbots_vision/bitbots_vision/vision.py b/bitbots_vision/bitbots_vision/vision.py
index 9cc31131a..ea665603f 100755
--- a/bitbots_vision/bitbots_vision/vision.py
+++ b/bitbots_vision/bitbots_vision/vision.py
@@ -1,5 +1,4 @@
#! /usr/bin/env python3
-from copy import deepcopy
from typing import Optional
import rclpy
@@ -7,13 +6,11 @@
from cv_bridge import CvBridge
from rcl_interfaces.msg import SetParametersResult
from rclpy.experimental.events_executor import EventsExecutor
-from rclpy.node import Node
from sensor_msgs.msg import Image
+from bitbots_vision import NodeWithConfig
from bitbots_vision.vision_modules import debug, ros_utils, yoeo
-from .params import gen
-
logger = rclpy.logging.get_logger("bitbots_vision")
try:
@@ -26,7 +23,7 @@ def profile(func):
logger.info("No Profiling available")
-class YOEOVision(Node):
+class YOEOVision(NodeWithConfig):
"""
The Vision is the main ROS-node for handling all tasks related to image processing.
@@ -43,7 +40,6 @@ def __init__(self) -> None:
yoeo.YOEOObjectManager.set_package_directory(self._package_path)
- self._config: dict = {}
self._cv_bridge = CvBridge()
self._sub_image = None
@@ -51,8 +47,7 @@ def __init__(self) -> None:
self._vision_components: list[yoeo.AbstractVisionComponent] = []
self._debug_image: Optional[debug.DebugImage] = None
- # Setup reconfiguration
- gen.declare_params(self)
+ # Setup reconfiguration - now we use the generated parameter listener
self.add_on_set_parameters_callback(self._dynamic_reconfigure_callback)
# Add general params
@@ -61,28 +56,70 @@ def __init__(self) -> None:
# Update team color
ros_utils.update_own_team_color(self)
- self._dynamic_reconfigure_callback(self.get_parameters_by_prefix("").values())
+ # Configure vision with initial parameters
+ self._configure_vision_from_config()
logger.debug(f"Leaving {self.__class__.__name__} constructor")
+ def _config_to_dict(self) -> dict:
+ """
+ Convert the generated parameter config object to a dictionary for compatibility
+ with existing code that expects dictionary access.
+ """
+ return {
+ # Component activation parameters
+ "component_ball_detection_active": self.config.component_ball_detection_active,
+ "component_debug_image_active": self.config.component_debug_image_active,
+ "component_field_detection_active": self.config.component_field_detection_active,
+ "component_goalpost_detection_active": self.config.component_goalpost_detection_active,
+ "component_line_detection_active": self.config.component_line_detection_active,
+ "component_robot_detection_active": self.config.component_robot_detection_active,
+
+ # ROS topic parameters
+ "ROS_img_msg_topic": self.config.ROS_img_msg_topic,
+ "ROS_ball_msg_topic": self.config.ROS_ball_msg_topic,
+ "ROS_goal_posts_msg_topic": self.config.ROS_goal_posts_msg_topic,
+ "ROS_robot_msg_topic": self.config.ROS_robot_msg_topic,
+ "ROS_line_msg_topic": self.config.ROS_line_msg_topic,
+ "ROS_line_mask_msg_topic": self.config.ROS_line_mask_msg_topic,
+ "ROS_debug_image_msg_topic": self.config.ROS_debug_image_msg_topic,
+ "ROS_field_mask_image_msg_topic": self.config.ROS_field_mask_image_msg_topic,
+
+ # YOEO model parameters
+ "yoeo_model_path": self.config.yoeo_model_path,
+ "yoeo_nms_threshold": self.config.yoeo_nms_threshold,
+ "yoeo_conf_threshold": self.config.yoeo_conf_threshold,
+ "yoeo_framework": self.config.yoeo_framework,
+
+ # Ball detection parameters
+ "ball_candidate_rating_threshold": self.config.ball_candidate_rating_threshold,
+ "ball_candidate_max_count": self.config.ball_candidate_max_count,
+
+ # Caching parameter
+ "caching": self.config.caching,
+ }
+
+ def _configure_vision_from_config(self) -> None:
+ """
+ Configure vision components using the current config.
+ """
+ config_dict = self._config_to_dict()
+ self._configure_vision(config_dict)
+
def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
"""
Callback for the dynamic reconfigure configuration.
- :param dict params: new config
+ :param params: list of changed parameters
"""
- new_config = self._get_updated_config_with(params)
- self._configure_vision(new_config)
- self._config = new_config
+ # Update the config from the parameter listener
+ self.config = self.param_listener.get_params()
+
+ # Configure vision with the updated config
+ self._configure_vision_from_config()
return SetParametersResult(successful=True)
- def _get_updated_config_with(self, params) -> dict:
- new_config = deepcopy(self._config)
- for param in params:
- new_config[param.name] = param.value
- return new_config
-
def _configure_vision(self, new_config: dict) -> None:
yoeo.YOEOObjectManager.configure(new_config)
@@ -120,15 +157,19 @@ def make_vision_component(
if new_config["component_debug_image_active"]:
self._vision_components.append(make_vision_component(yoeo.DebugImageComponent))
+ # For the subscriber update, we'll pass the last config dict or None for the first time
+ old_config_dict = getattr(self, '_last_config_dict', None)
self._sub_image = ros_utils.create_or_update_subscriber(
self,
- self._config,
+ old_config_dict,
new_config,
self._sub_image,
"ROS_img_msg_topic",
Image,
callback=self._run_vision_pipeline,
)
+ # Remember this config for next time
+ self._last_config_dict = new_config.copy()
@profile
def _run_vision_pipeline(self, image_msg: Image) -> None:
diff --git a/bitbots_vision/config/vision_parameters.yaml b/bitbots_vision/config/vision_parameters.yaml
new file mode 100644
index 000000000..dc5b98732
--- /dev/null
+++ b/bitbots_vision/config/vision_parameters.yaml
@@ -0,0 +1,126 @@
+bitbots_vision:
+ # Component activation parameters
+ component_ball_detection_active:
+ type: bool
+ default_value: true
+ description: "Activate/Deactivate the ball detection component"
+
+ component_debug_image_active:
+ type: bool
+ default_value: false
+ description: "Activate/Deactivate the debug image component"
+
+ component_field_detection_active:
+ type: bool
+ default_value: true
+ description: "Activate/Deactivate the field detection component"
+
+ component_goalpost_detection_active:
+ type: bool
+ default_value: false
+ description: "Activate/Deactivate the goalpost detection component"
+
+ component_line_detection_active:
+ type: bool
+ default_value: true
+ description: "Activate/Deactivate the line detection component"
+
+ component_robot_detection_active:
+ type: bool
+ default_value: true
+ description: "Activate/Deactivate the robot detection component"
+
+ # ROS topic parameters
+ ROS_img_msg_topic:
+ type: string
+ default_value: "camera/image_proc"
+ description: "ROS topic of the image message"
+ read_only: true
+
+ ROS_ball_msg_topic:
+ type: string
+ default_value: "balls_in_image"
+ description: "ROS topic of the ball message"
+ read_only: true
+
+ ROS_goal_posts_msg_topic:
+ type: string
+ default_value: "goal_posts_in_image"
+ description: "ROS topic of the goal posts message"
+ read_only: true
+
+ ROS_robot_msg_topic:
+ type: string
+ default_value: "robots_in_image"
+ description: "ROS topic of the robots message"
+ read_only: true
+
+ ROS_line_msg_topic:
+ type: string
+ default_value: "line_in_image"
+ description: "ROS topic of the line message"
+ read_only: true
+
+ ROS_line_mask_msg_topic:
+ type: string
+ default_value: "line_mask_in_image"
+ description: "ROS topic of the line mask message"
+ read_only: true
+
+ ROS_debug_image_msg_topic:
+ type: string
+ default_value: "debug_image"
+ description: "ROS topic of the debug image message"
+ read_only: true
+
+ ROS_field_mask_image_msg_topic:
+ type: string
+ default_value: "field_mask"
+ description: "ROS topic of the field mask debug image message"
+ read_only: true
+
+ # YOEO model parameters
+ yoeo_model_path:
+ type: string
+ default_value: "2022_10_07_flo_torso21_yoeox"
+ description: "Name of YOEO model"
+
+ yoeo_nms_threshold:
+ type: double
+ default_value: 0.4
+ description: "YOEO Non-maximum suppression threshold"
+ validation:
+ bounds<>: [0.0, 1.0]
+
+ yoeo_conf_threshold:
+ type: double
+ default_value: 0.5
+ description: "YOEO confidence threshold"
+ validation:
+ bounds<>: [0.0, 1.0]
+
+ yoeo_framework:
+ type: string
+ default_value: "tvm"
+ description: "The neural network framework that should be used ['pytorch', 'openvino', 'onnx', 'tvm']"
+
+ # Ball detection parameters
+ ball_candidate_rating_threshold:
+ type: double
+ default_value: 0.5
+ description: "A threshold for the minimum candidate rating"
+ validation:
+ bounds<>: [0.0, 1.0]
+
+ ball_candidate_max_count:
+ type: int
+ default_value: 1
+ description: "The maximum number of balls that should be published"
+ validation:
+ bounds<>: [0, 50]
+
+ # Caching parameter
+ caching:
+ type: bool
+ default_value: true
+ description: "Used to deactivate caching for profiling reasons"
\ No newline at end of file
diff --git a/bitbots_vision/package.xml b/bitbots_vision/package.xml
index 6f5b75f5e..794cb8a77 100644
--- a/bitbots_vision/package.xml
+++ b/bitbots_vision/package.xml
@@ -29,6 +29,7 @@
rosidl_default_runtime
bitbots_utils
game_controller_hl_interfaces
+ generate_parameter_library
geometry_msgs
image_transport
python3-numpy
diff --git a/bitbots_vision/setup.py b/bitbots_vision/setup.py
index 96b053d04..d7288708e 100755
--- a/bitbots_vision/setup.py
+++ b/bitbots_vision/setup.py
@@ -1,8 +1,14 @@
import glob
import os
+from generate_parameter_library_py.setup_helper import generate_parameter_module
from setuptools import find_packages, setup
+generate_parameter_module(
+ "vision_parameters", # python module name for parameter library
+ "config/vision_parameters.yaml", # path to input yaml file
+)
+
package_name = "bitbots_vision"
From d930b118e79ad9f7d6ca6a491aefe67096769334 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 16:54:37 +0000
Subject: [PATCH 3/7] Complete bitbots_vision migration: test and finalize
generate_parameter_library integration
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
bitbots_vision/bitbots_vision/__init__.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/bitbots_vision/bitbots_vision/__init__.py b/bitbots_vision/bitbots_vision/__init__.py
index 4cefb79b8..591fcb6d2 100644
--- a/bitbots_vision/bitbots_vision/__init__.py
+++ b/bitbots_vision/bitbots_vision/__init__.py
@@ -1,11 +1,15 @@
# Setting up runtime type checking for this package
-from beartype.claw import beartype_this_package
+try:
+ from beartype.claw import beartype_this_package
+ beartype_this_package()
+except ImportError:
+ # beartype not available, skip type checking
+ pass
+
from rclpy.node import Node
from bitbots_vision.vision_parameters import bitbots_vision as parameters
-beartype_this_package()
-
class NodeWithConfig(Node):
def __init__(self, name: str) -> None:
From 8395c2eb49d368f35ed8fbdeed1d162d48f231f9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 17:04:28 +0000
Subject: [PATCH 4/7] Fix parameter library integration based on code review
feedback
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
.gitignore | 2 +-
bitbots_vision/bitbots_vision/__init__.py | 18 +----
bitbots_vision/bitbots_vision/vision.py | 96 ++++++++++-------------
3 files changed, 43 insertions(+), 73 deletions(-)
diff --git a/.gitignore b/.gitignore
index 5f4185ba9..95d6ccf58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -216,7 +216,7 @@ log/
.pyenv3/*
# Generated parameter library files
-**/vision_parameters/
+**/vision_parameters.py
ansible_robots/*
doc_internal/*
diff --git a/bitbots_vision/bitbots_vision/__init__.py b/bitbots_vision/bitbots_vision/__init__.py
index 591fcb6d2..4d2e15007 100644
--- a/bitbots_vision/bitbots_vision/__init__.py
+++ b/bitbots_vision/bitbots_vision/__init__.py
@@ -1,18 +1,4 @@
# Setting up runtime type checking for this package
-try:
- from beartype.claw import beartype_this_package
- beartype_this_package()
-except ImportError:
- # beartype not available, skip type checking
- pass
+from beartype.claw import beartype_this_package
-from rclpy.node import Node
-
-from bitbots_vision.vision_parameters import bitbots_vision as parameters
-
-
-class NodeWithConfig(Node):
- def __init__(self, name: str) -> None:
- super().__init__(name)
- self.param_listener = parameters.ParamListener(self)
- self.config = self.param_listener.get_params()
+beartype_this_package()
diff --git a/bitbots_vision/bitbots_vision/vision.py b/bitbots_vision/bitbots_vision/vision.py
index ea665603f..1a8253ad4 100755
--- a/bitbots_vision/bitbots_vision/vision.py
+++ b/bitbots_vision/bitbots_vision/vision.py
@@ -5,11 +5,11 @@
from ament_index_python.packages import get_package_share_directory
from cv_bridge import CvBridge
from rcl_interfaces.msg import SetParametersResult
-from rclpy.experimental.events_executor import EventsExecutor
+from rclpy.node import Node
from sensor_msgs.msg import Image
-from bitbots_vision import NodeWithConfig
from bitbots_vision.vision_modules import debug, ros_utils, yoeo
+from bitbots_vision.vision_parameters import bitbots_vision as parameters
logger = rclpy.logging.get_logger("bitbots_vision")
@@ -23,7 +23,7 @@ def profile(func):
logger.info("No Profiling available")
-class YOEOVision(NodeWithConfig):
+class YOEOVision(Node):
"""
The Vision is the main ROS-node for handling all tasks related to image processing.
@@ -36,6 +36,10 @@ def __init__(self) -> None:
logger.debug(f"Entering {self.__class__.__name__} constructor")
+ # Setup parameter listener directly
+ self.param_listener = parameters.ParamListener(self)
+ self.config = self.param_listener.get_params()
+
self._package_path = get_package_share_directory("bitbots_vision")
yoeo.YOEOObjectManager.set_package_directory(self._package_path)
@@ -47,7 +51,7 @@ def __init__(self) -> None:
self._vision_components: list[yoeo.AbstractVisionComponent] = []
self._debug_image: Optional[debug.DebugImage] = None
- # Setup reconfiguration - now we use the generated parameter listener
+ # Setup reconfiguration callback
self.add_on_set_parameters_callback(self._dynamic_reconfigure_callback)
# Add general params
@@ -61,50 +65,11 @@ def __init__(self) -> None:
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def _config_to_dict(self) -> dict:
- """
- Convert the generated parameter config object to a dictionary for compatibility
- with existing code that expects dictionary access.
- """
- return {
- # Component activation parameters
- "component_ball_detection_active": self.config.component_ball_detection_active,
- "component_debug_image_active": self.config.component_debug_image_active,
- "component_field_detection_active": self.config.component_field_detection_active,
- "component_goalpost_detection_active": self.config.component_goalpost_detection_active,
- "component_line_detection_active": self.config.component_line_detection_active,
- "component_robot_detection_active": self.config.component_robot_detection_active,
-
- # ROS topic parameters
- "ROS_img_msg_topic": self.config.ROS_img_msg_topic,
- "ROS_ball_msg_topic": self.config.ROS_ball_msg_topic,
- "ROS_goal_posts_msg_topic": self.config.ROS_goal_posts_msg_topic,
- "ROS_robot_msg_topic": self.config.ROS_robot_msg_topic,
- "ROS_line_msg_topic": self.config.ROS_line_msg_topic,
- "ROS_line_mask_msg_topic": self.config.ROS_line_mask_msg_topic,
- "ROS_debug_image_msg_topic": self.config.ROS_debug_image_msg_topic,
- "ROS_field_mask_image_msg_topic": self.config.ROS_field_mask_image_msg_topic,
-
- # YOEO model parameters
- "yoeo_model_path": self.config.yoeo_model_path,
- "yoeo_nms_threshold": self.config.yoeo_nms_threshold,
- "yoeo_conf_threshold": self.config.yoeo_conf_threshold,
- "yoeo_framework": self.config.yoeo_framework,
-
- # Ball detection parameters
- "ball_candidate_rating_threshold": self.config.ball_candidate_rating_threshold,
- "ball_candidate_max_count": self.config.ball_candidate_max_count,
-
- # Caching parameter
- "caching": self.config.caching,
- }
-
def _configure_vision_from_config(self) -> None:
"""
Configure vision components using the current config.
"""
- config_dict = self._config_to_dict()
- self._configure_vision(config_dict)
+ self._configure_vision(self.config)
def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
"""
@@ -120,10 +85,29 @@ def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
return SetParametersResult(successful=True)
- def _configure_vision(self, new_config: dict) -> None:
- yoeo.YOEOObjectManager.configure(new_config)
+ def _configure_vision(self, config) -> None:
+ # Create a minimal config dict for compatibility with existing subsystems
+ # TODO: Update subsystems to use dot notation and remove this compatibility layer
+ config_dict = {
+ "component_ball_detection_active": config.component_ball_detection_active,
+ "component_debug_image_active": config.component_debug_image_active,
+ "component_field_detection_active": config.component_field_detection_active,
+ "component_goalpost_detection_active": config.component_goalpost_detection_active,
+ "component_line_detection_active": config.component_line_detection_active,
+ "component_robot_detection_active": config.component_robot_detection_active,
+ "ROS_img_msg_topic": config.ROS_img_msg_topic,
+ "yoeo_model_path": config.yoeo_model_path,
+ "yoeo_nms_threshold": config.yoeo_nms_threshold,
+ "yoeo_conf_threshold": config.yoeo_conf_threshold,
+ "yoeo_framework": config.yoeo_framework,
+ "ball_candidate_rating_threshold": config.ball_candidate_rating_threshold,
+ "ball_candidate_max_count": config.ball_candidate_max_count,
+ "caching": config.caching,
+ }
+
+ yoeo.YOEOObjectManager.configure(config_dict)
- debug_image = debug.DebugImage(new_config["component_debug_image_active"])
+ debug_image = debug.DebugImage(config.component_debug_image_active)
self._debug_image = debug_image
def make_vision_component(
@@ -133,28 +117,28 @@ def make_vision_component(
node=self,
yoeo_handler=yoeo.YOEOObjectManager.get(),
debug_image=debug_image,
- config=new_config,
+ config=config_dict, # Still passing dict for compatibility
**kwargs,
)
self._vision_components = [make_vision_component(yoeo.YOEOComponent)]
- if new_config["component_ball_detection_active"]:
+ if config.component_ball_detection_active:
self._vision_components.append(make_vision_component(yoeo.BallDetectionComponent))
- if new_config["component_robot_detection_active"]:
+ if config.component_robot_detection_active:
self._vision_components.append(
make_vision_component(
yoeo.RobotDetectionComponent,
team_color_detection_supported=yoeo.YOEOObjectManager.is_team_color_detection_supported(),
)
)
- if new_config["component_goalpost_detection_active"]:
+ if config.component_goalpost_detection_active:
self._vision_components.append(make_vision_component(yoeo.GoalpostDetectionComponent))
- if new_config["component_line_detection_active"]:
+ if config.component_line_detection_active:
self._vision_components.append(make_vision_component(yoeo.LineDetectionComponent))
- if new_config["component_field_detection_active"]:
+ if config.component_field_detection_active:
self._vision_components.append(make_vision_component(yoeo.FieldDetectionComponent))
- if new_config["component_debug_image_active"]:
+ if config.component_debug_image_active:
self._vision_components.append(make_vision_component(yoeo.DebugImageComponent))
# For the subscriber update, we'll pass the last config dict or None for the first time
@@ -162,14 +146,14 @@ def make_vision_component(
self._sub_image = ros_utils.create_or_update_subscriber(
self,
old_config_dict,
- new_config,
+ config_dict,
self._sub_image,
"ROS_img_msg_topic",
Image,
callback=self._run_vision_pipeline,
)
# Remember this config for next time
- self._last_config_dict = new_config.copy()
+ self._last_config_dict = config_dict.copy()
@profile
def _run_vision_pipeline(self, image_msg: Image) -> None:
From 06cce1a425cc98432fdc28a7e52cd82be53ad216 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 17:14:07 +0000
Subject: [PATCH 5/7] Update subsystems to use dot notation for config access
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
bitbots_vision/bitbots_vision/vision.py | 51 ++++++----------
.../vision_modules/yoeo/object_manager.py | 24 +++++---
.../vision_modules/yoeo/vision_components.py | 16 ++---
.../vision_modules/yoeo/yoeo_handlers.py | 58 +++++++++----------
4 files changed, 71 insertions(+), 78 deletions(-)
diff --git a/bitbots_vision/bitbots_vision/vision.py b/bitbots_vision/bitbots_vision/vision.py
index 1a8253ad4..2bf6667fd 100755
--- a/bitbots_vision/bitbots_vision/vision.py
+++ b/bitbots_vision/bitbots_vision/vision.py
@@ -86,26 +86,7 @@ def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
return SetParametersResult(successful=True)
def _configure_vision(self, config) -> None:
- # Create a minimal config dict for compatibility with existing subsystems
- # TODO: Update subsystems to use dot notation and remove this compatibility layer
- config_dict = {
- "component_ball_detection_active": config.component_ball_detection_active,
- "component_debug_image_active": config.component_debug_image_active,
- "component_field_detection_active": config.component_field_detection_active,
- "component_goalpost_detection_active": config.component_goalpost_detection_active,
- "component_line_detection_active": config.component_line_detection_active,
- "component_robot_detection_active": config.component_robot_detection_active,
- "ROS_img_msg_topic": config.ROS_img_msg_topic,
- "yoeo_model_path": config.yoeo_model_path,
- "yoeo_nms_threshold": config.yoeo_nms_threshold,
- "yoeo_conf_threshold": config.yoeo_conf_threshold,
- "yoeo_framework": config.yoeo_framework,
- "ball_candidate_rating_threshold": config.ball_candidate_rating_threshold,
- "ball_candidate_max_count": config.ball_candidate_max_count,
- "caching": config.caching,
- }
-
- yoeo.YOEOObjectManager.configure(config_dict)
+ yoeo.YOEOObjectManager.configure(config)
debug_image = debug.DebugImage(config.component_debug_image_active)
self._debug_image = debug_image
@@ -117,7 +98,7 @@ def make_vision_component(
node=self,
yoeo_handler=yoeo.YOEOObjectManager.get(),
debug_image=debug_image,
- config=config_dict, # Still passing dict for compatibility
+ config=config, # Now passing config object directly
**kwargs,
)
@@ -141,19 +122,21 @@ def make_vision_component(
if config.component_debug_image_active:
self._vision_components.append(make_vision_component(yoeo.DebugImageComponent))
- # For the subscriber update, we'll pass the last config dict or None for the first time
- old_config_dict = getattr(self, '_last_config_dict', None)
- self._sub_image = ros_utils.create_or_update_subscriber(
- self,
- old_config_dict,
- config_dict,
- self._sub_image,
- "ROS_img_msg_topic",
- Image,
- callback=self._run_vision_pipeline,
- )
- # Remember this config for next time
- self._last_config_dict = config_dict.copy()
+ # For the subscriber update, handle the topic name directly
+ old_topic = getattr(self, '_last_img_topic', None)
+ current_topic = config.ROS_img_msg_topic
+
+ if old_topic != current_topic:
+ self._sub_image = self.create_subscription(
+ Image,
+ current_topic,
+ self._run_vision_pipeline,
+ 1
+ )
+ logger.debug(f"Registered new subscriber at {current_topic}")
+
+ # Remember this topic for next time
+ self._last_img_topic = current_topic
@profile
def _run_vision_pipeline(self, image_msg: Image) -> None:
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
index 3aadfb974..7749ffffe 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
@@ -72,14 +72,14 @@ def is_team_color_detection_supported(cls) -> bool:
return cls._model_config.team_colors_are_provided()
@classmethod
- def configure(cls, config: dict) -> None:
+ def configure(cls, config) -> None:
if not cls._package_directory_set:
logger.error("Package directory not set!")
- framework = config["yoeo_framework"]
+ framework = config.yoeo_framework
cls._verify_framework_parameter(framework)
- model_path = cls._get_full_model_path(config["yoeo_model_path"])
+ model_path = cls._get_full_model_path(config.yoeo_model_path)
cls._verify_required_neural_network_files_exist(framework, model_path)
cls._configure_yoeo_instance(config, framework, model_path)
@@ -107,7 +107,7 @@ def _model_files_exist(cls, framework: str, model_path: str) -> bool:
return cls._HANDLERS_BY_NAME[framework].model_files_exist(model_path)
@classmethod
- def _configure_yoeo_instance(cls, config: dict, framework: str, model_path: str) -> None:
+ def _configure_yoeo_instance(cls, config, framework: str, model_path: str) -> None:
if cls._new_yoeo_handler_is_needed(framework, model_path):
cls._load_model_config(model_path)
cls._instantiate_new_yoeo_handler(config, framework, model_path)
@@ -124,7 +124,7 @@ def _load_model_config(cls, model_path: str) -> None:
cls._model_config = ModelConfigLoader.load_from(model_path)
@classmethod
- def _instantiate_new_yoeo_handler(cls, config: dict, framework: str, model_path: str) -> None:
+ def _instantiate_new_yoeo_handler(cls, config, framework: str, model_path: str) -> None:
cls._yoeo_instance = cls._HANDLERS_BY_NAME[framework](
config,
model_path,
@@ -135,5 +135,15 @@ def _instantiate_new_yoeo_handler(cls, config: dict, framework: str, model_path:
logger.info(f"Using {cls._yoeo_instance.__class__.__name__}")
@classmethod
- def _yoeo_parameters_have_changed(cls, new_config: dict) -> bool:
- return ros_utils.config_param_change(cls._config, new_config, r"yoeo_")
+ def _yoeo_parameters_have_changed(cls, new_config) -> bool:
+ if cls._config is None:
+ return True
+
+ # Compare YOEO-related parameters using direct attribute access
+ return (
+ cls._config.yoeo_framework != new_config.yoeo_framework or
+ cls._config.yoeo_model_path != new_config.yoeo_model_path or
+ cls._config.yoeo_nms_threshold != new_config.yoeo_nms_threshold or
+ cls._config.yoeo_conf_threshold != new_config.yoeo_conf_threshold or
+ cls._config.caching != new_config.caching
+ )
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/vision_components.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/vision_components.py
index 2fd5d0a1f..74cc7663a 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/vision_components.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/vision_components.py
@@ -57,7 +57,7 @@ def __init__(
):
super().__init__(node, yoeo_handler, debug_image, config)
- self._publisher = self._node.create_publisher(BallArray, self._config["ROS_ball_msg_topic"], qos_profile=1)
+ self._publisher = self._node.create_publisher(BallArray, self._config.ROS_ball_msg_topic, qos_profile=1)
def run(self, image: np.ndarray, header: Header) -> None:
# Get all ball candidates from YOEO
@@ -65,9 +65,9 @@ def run(self, image: np.ndarray, header: Header) -> None:
# Filter candidates by rating and count
candidates = candidate.Candidate.sort_candidates(candidates)
- top_candidates = candidates[: self._config["ball_candidate_max_count"]]
+ top_candidates = candidates[: self._config.ball_candidate_max_count]
final_candidates = candidate.Candidate.rating_threshold(
- top_candidates, self._config["ball_candidate_rating_threshold"]
+ top_candidates, self._config.ball_candidate_rating_threshold
)
# Publish ball candidates
@@ -95,7 +95,7 @@ def __init__(
super().__init__(node, yoeo_handler, debug_image, config)
self._publisher = self._node.create_publisher(
- GoalpostArray, self._config["ROS_goal_posts_msg_topic"], qos_profile=1
+ GoalpostArray, self._config.ROS_goal_posts_msg_topic, qos_profile=1
)
def run(self, image: np.ndarray, header: Header) -> None:
@@ -125,7 +125,7 @@ def __init__(
self, node: Node, yoeo_handler: yoeo_handlers.IYOEOHandler, debug_image: debug.DebugImage, config: dict
):
super().__init__(node, yoeo_handler, debug_image, config)
- self._publisher = self._node.create_publisher(Image, self._config["ROS_line_mask_msg_topic"], qos_profile=1)
+ self._publisher = self._node.create_publisher(Image, self._config.ROS_line_mask_msg_topic, qos_profile=1)
def run(self, image: np.ndarray, header: Header) -> None:
# Get line mask from YOEO
@@ -153,7 +153,7 @@ def __init__(
):
super().__init__(node, yoeo_handler, debug_image, config)
self._publisher = self._node.create_publisher(
- Image, self._config["ROS_field_mask_image_msg_topic"], qos_profile=1
+ Image, self._config.ROS_field_mask_image_msg_topic, qos_profile=1
)
def run(self, image: np.ndarray, header: Header) -> None:
@@ -185,7 +185,7 @@ def __init__(
super().__init__(node, yoeo_handler, debug_image, config)
self._team_color_detection_supported = team_color_detection_supported
- self._publisher = self._node.create_publisher(RobotArray, self._config["ROS_robot_msg_topic"], qos_profile=1)
+ self._publisher = self._node.create_publisher(RobotArray, self._config.ROS_robot_msg_topic, qos_profile=1)
def run(self, image: np.ndarray, header: Header) -> None:
robot_msgs: list[Robot] = []
@@ -282,7 +282,7 @@ def __init__(
):
super().__init__(node, yoeo_handler, debug_image, config)
- self._publisher = self._node.create_publisher(Image, self._config["ROS_debug_image_msg_topic"], qos_profile=1)
+ self._publisher = self._node.create_publisher(Image, self._config.ROS_debug_image_msg_topic, qos_profile=1)
def run(self, image: np.ndarray, header: Header) -> None:
debug_image_msg = ros_utils.build_image_msg(header, self._debug_image.get_image(), "bgr8")
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
index 9409dc3ea..7facb40df 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
@@ -22,7 +22,7 @@ class IYOEOHandler(ABC):
"""
@abstractmethod
- def configure(self, config: dict) -> None:
+ def configure(self, config) -> None:
"""
Allows to (re-) configure the YOEO handler.
"""
@@ -98,7 +98,7 @@ class YOEOHandlerTemplate(IYOEOHandler):
def __init__(
self,
- config: dict,
+ config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -117,12 +117,12 @@ def __init__(
self._seg_class_names: list[str] = seg_class_names
self._seg_masks: dict = dict()
- self._use_caching: bool = config["caching"]
+ self._use_caching: bool = config.caching
logger.debug("Leaving YOEOHandlerTemplate constructor")
- def configure(self, config: dict) -> None:
- self._use_caching = config["caching"]
+ def configure(self, config) -> None:
+ self._use_caching = config.caching
def get_available_detection_class_names(self) -> list[str]:
return self._det_class_names
@@ -211,7 +211,7 @@ class YOEOHandlerONNX(YOEOHandlerTemplate):
def __init__(
self,
- config: dict,
+ config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -238,8 +238,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -248,13 +248,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config: dict) -> None:
+ def configure(self, config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -284,7 +284,7 @@ class YOEOHandlerOpenVino(YOEOHandlerTemplate):
def __init__(
self,
- config: dict,
+ config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -320,8 +320,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -337,13 +337,13 @@ def _select_device(self) -> str:
device = "CPU"
return device
- def configure(self, config: dict) -> None:
+ def configure(self, config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -372,7 +372,7 @@ class YOEOHandlerPytorch(YOEOHandlerTemplate):
def __init__(
self,
- config: dict,
+ config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -398,8 +398,8 @@ def __init__(
logger.debug(f"Loading files...\n\t{config_path}\n\t{weights_path}")
self._model = torch_models.load_model(config_path, weights_path)
- self._conf_thresh: float = config["yoeo_conf_threshold"]
- self._nms_thresh: float = config["yoeo_nms_threshold"]
+ self._conf_thresh: float = config.yoeo_conf_threshold
+ self._nms_thresh: float = config.yoeo_nms_threshold
self._group_config: torch_GroupConfig = self._update_group_config()
logger.debug(f"Leaving {self.__class__.__name__} constructor")
@@ -409,10 +409,10 @@ def _update_group_config(self):
return self.torch_group_config(group_ids=robot_class_ids, surrogate_id=robot_class_ids[0])
- def configure(self, config: dict) -> None:
+ def configure(self, config) -> None:
super().configure(config)
- self._conf_thresh = config["yoeo_conf_threshold"]
- self._nms_thresh = config["yoeo_nms_threshold"]
+ self._conf_thresh = config.yoeo_conf_threshold
+ self._nms_thresh = config.yoeo_nms_threshold
self._group_config = self._update_group_config()
@staticmethod
@@ -446,7 +446,7 @@ class YOEOHandlerTVM(YOEOHandlerTemplate):
def __init__(
self,
- config: dict,
+ config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -485,8 +485,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -495,13 +495,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config: dict) -> None:
+ def configure(self, config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config["yoeo_conf_threshold"],
- nms_thresh=config["yoeo_nms_threshold"],
+ conf_thresh=config.yoeo_conf_threshold,
+ nms_thresh=config.yoeo_nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
From 0a56fc6c740aee4f2647a8bb618c79e78e9421c9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 17:38:58 +0000
Subject: [PATCH 6/7] Address code review feedback: Add parameter hierarchy,
type hints, and rework ros_utils
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
bitbots_vision/bitbots_vision/vision.py | 28 ++++------
.../vision_modules/ros_utils.py | 26 +++++++++
.../vision_modules/yoeo/object_manager.py | 23 +++-----
.../vision_modules/yoeo/yoeo_handlers.py | 56 ++++++++++---------
bitbots_vision/config/vision_parameters.yaml | 51 +++++++++--------
5 files changed, 102 insertions(+), 82 deletions(-)
diff --git a/bitbots_vision/bitbots_vision/vision.py b/bitbots_vision/bitbots_vision/vision.py
index 2bf6667fd..bf246c0bb 100755
--- a/bitbots_vision/bitbots_vision/vision.py
+++ b/bitbots_vision/bitbots_vision/vision.py
@@ -61,16 +61,10 @@ def __init__(self) -> None:
ros_utils.update_own_team_color(self)
# Configure vision with initial parameters
- self._configure_vision_from_config()
+ self._configure_vision(self.config)
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def _configure_vision_from_config(self) -> None:
- """
- Configure vision components using the current config.
- """
- self._configure_vision(self.config)
-
def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
"""
Callback for the dynamic reconfigure configuration.
@@ -81,7 +75,7 @@ def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
self.config = self.param_listener.get_params()
# Configure vision with the updated config
- self._configure_vision_from_config()
+ self._configure_vision(self.config)
return SetParametersResult(successful=True)
@@ -122,18 +116,18 @@ def make_vision_component(
if config.component_debug_image_active:
self._vision_components.append(make_vision_component(yoeo.DebugImageComponent))
- # For the subscriber update, handle the topic name directly
+ # For the subscriber update, use the improved ros_utils function
old_topic = getattr(self, '_last_img_topic', None)
current_topic = config.ROS_img_msg_topic
- if old_topic != current_topic:
- self._sub_image = self.create_subscription(
- Image,
- current_topic,
- self._run_vision_pipeline,
- 1
- )
- logger.debug(f"Registered new subscriber at {current_topic}")
+ self._sub_image = ros_utils.create_or_update_subscriber_with_config(
+ self,
+ old_topic,
+ current_topic,
+ self._sub_image,
+ Image,
+ self._run_vision_pipeline,
+ )
# Remember this topic for next time
self._last_img_topic = current_topic
diff --git a/bitbots_vision/bitbots_vision/vision_modules/ros_utils.py b/bitbots_vision/bitbots_vision/vision_modules/ros_utils.py
index 53a41fcad..5ee1b3b3c 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/ros_utils.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/ros_utils.py
@@ -46,6 +46,32 @@ class RobotColor(Enum):
own_team_color: RobotColor = RobotColor.UNKNOWN
+def create_or_update_subscriber_with_config(
+ node, old_topic, new_topic, subscriber_object, data_class, callback, qos_profile=1, callback_group=None
+):
+ """
+ Creates or updates a subscriber using direct topic names instead of config dicts
+
+ :param node: ROS node to which the publisher is bound
+ :param old_topic: Previous topic name
+ :param new_topic: New topic name
+ :param subscriber_object: The python object, that represents the subscriber
+ :param data_class: Data type class for ROS messages of the topic we want to subscribe
+ :param callback: The subscriber callback function
+ :param qos_profile: A QoSProfile or a history depth to apply to the subscription.
+ :param callback_group: The callback group for the subscription.
+ :return: adjusted subscriber object
+ """
+ # Check if topic has changed
+ if old_topic != new_topic:
+ # Create the new subscriber
+ subscriber_object = node.create_subscription(
+ data_class, new_topic, callback, qos_profile, callback_group=callback_group
+ )
+ logger.debug("Registered new subscriber at " + str(new_topic))
+ return subscriber_object
+
+
def create_or_update_subscriber(
node, old_config, new_config, subscriber_object, topic_key, data_class, callback, qos_profile=1, callback_group=None
):
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
index 7749ffffe..72dbda40d 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
@@ -4,6 +4,7 @@
import rclpy
from bitbots_vision.vision_modules import ros_utils
+from bitbots_vision.vision_parameters import bitbots_vision as parameters
from . import yoeo_handlers
from .model_config import ModelConfig, ModelConfigLoader
@@ -72,14 +73,14 @@ def is_team_color_detection_supported(cls) -> bool:
return cls._model_config.team_colors_are_provided()
@classmethod
- def configure(cls, config) -> None:
+ def configure(cls, config: parameters.Params) -> None:
if not cls._package_directory_set:
logger.error("Package directory not set!")
- framework = config.yoeo_framework
+ framework = config.yoeo.framework
cls._verify_framework_parameter(framework)
- model_path = cls._get_full_model_path(config.yoeo_model_path)
+ model_path = cls._get_full_model_path(config.yoeo.model_path)
cls._verify_required_neural_network_files_exist(framework, model_path)
cls._configure_yoeo_instance(config, framework, model_path)
@@ -107,7 +108,7 @@ def _model_files_exist(cls, framework: str, model_path: str) -> bool:
return cls._HANDLERS_BY_NAME[framework].model_files_exist(model_path)
@classmethod
- def _configure_yoeo_instance(cls, config, framework: str, model_path: str) -> None:
+ def _configure_yoeo_instance(cls, config: parameters.Params, framework: str, model_path: str) -> None:
if cls._new_yoeo_handler_is_needed(framework, model_path):
cls._load_model_config(model_path)
cls._instantiate_new_yoeo_handler(config, framework, model_path)
@@ -124,7 +125,7 @@ def _load_model_config(cls, model_path: str) -> None:
cls._model_config = ModelConfigLoader.load_from(model_path)
@classmethod
- def _instantiate_new_yoeo_handler(cls, config, framework: str, model_path: str) -> None:
+ def _instantiate_new_yoeo_handler(cls, config: parameters.Params, framework: str, model_path: str) -> None:
cls._yoeo_instance = cls._HANDLERS_BY_NAME[framework](
config,
model_path,
@@ -135,15 +136,9 @@ def _instantiate_new_yoeo_handler(cls, config, framework: str, model_path: str)
logger.info(f"Using {cls._yoeo_instance.__class__.__name__}")
@classmethod
- def _yoeo_parameters_have_changed(cls, new_config) -> bool:
+ def _yoeo_parameters_have_changed(cls, new_config: parameters.Params) -> bool:
if cls._config is None:
return True
- # Compare YOEO-related parameters using direct attribute access
- return (
- cls._config.yoeo_framework != new_config.yoeo_framework or
- cls._config.yoeo_model_path != new_config.yoeo_model_path or
- cls._config.yoeo_nms_threshold != new_config.yoeo_nms_threshold or
- cls._config.yoeo_conf_threshold != new_config.yoeo_conf_threshold or
- cls._config.caching != new_config.caching
- )
+ # Compare YOEO parameters using the hierarchical structure
+ return cls._config.yoeo != new_config.yoeo
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
index 7facb40df..735a13266 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
@@ -9,6 +9,8 @@
import numpy as np
import rclpy
+from bitbots_vision.vision_parameters import bitbots_vision as parameters
+
from bitbots_vision.vision_modules.candidate import Candidate
from . import utils
@@ -22,7 +24,7 @@ class IYOEOHandler(ABC):
"""
@abstractmethod
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
"""
Allows to (re-) configure the YOEO handler.
"""
@@ -98,7 +100,7 @@ class YOEOHandlerTemplate(IYOEOHandler):
def __init__(
self,
- config,
+ config: parameters.Params,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -121,7 +123,7 @@ def __init__(
logger.debug("Leaving YOEOHandlerTemplate constructor")
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
self._use_caching = config.caching
def get_available_detection_class_names(self) -> list[str]:
@@ -211,7 +213,7 @@ class YOEOHandlerONNX(YOEOHandlerTemplate):
def __init__(
self,
- config,
+ config: parameters.Params,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -238,8 +240,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -248,13 +250,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -284,7 +286,7 @@ class YOEOHandlerOpenVino(YOEOHandlerTemplate):
def __init__(
self,
- config,
+ config: parameters.Params,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -320,8 +322,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -337,13 +339,13 @@ def _select_device(self) -> str:
device = "CPU"
return device
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -372,7 +374,7 @@ class YOEOHandlerPytorch(YOEOHandlerTemplate):
def __init__(
self,
- config,
+ config: parameters.Params,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -398,8 +400,8 @@ def __init__(
logger.debug(f"Loading files...\n\t{config_path}\n\t{weights_path}")
self._model = torch_models.load_model(config_path, weights_path)
- self._conf_thresh: float = config.yoeo_conf_threshold
- self._nms_thresh: float = config.yoeo_nms_threshold
+ self._conf_thresh: float = config.yoeo.conf_threshold
+ self._nms_thresh: float = config.yoeo.nms_threshold
self._group_config: torch_GroupConfig = self._update_group_config()
logger.debug(f"Leaving {self.__class__.__name__} constructor")
@@ -409,10 +411,10 @@ def _update_group_config(self):
return self.torch_group_config(group_ids=robot_class_ids, surrogate_id=robot_class_ids[0])
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
super().configure(config)
- self._conf_thresh = config.yoeo_conf_threshold
- self._nms_thresh = config.yoeo_nms_threshold
+ self._conf_thresh = config.yoeo.conf_threshold
+ self._nms_thresh = config.yoeo.nms_threshold
self._group_config = self._update_group_config()
@staticmethod
@@ -446,7 +448,7 @@ class YOEOHandlerTVM(YOEOHandlerTemplate):
def __init__(
self,
- config,
+ config: parameters.Params,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -485,8 +487,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -495,13 +497,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config) -> None:
+ def configure(self, config: parameters.Params) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config.yoeo_conf_threshold,
- nms_thresh=config.yoeo_nms_threshold,
+ conf_thresh=config.yoeo.conf_threshold,
+ nms_thresh=config.yoeo.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
diff --git a/bitbots_vision/config/vision_parameters.yaml b/bitbots_vision/config/vision_parameters.yaml
index dc5b98732..afecfc2f2 100644
--- a/bitbots_vision/config/vision_parameters.yaml
+++ b/bitbots_vision/config/vision_parameters.yaml
@@ -79,30 +79,33 @@ bitbots_vision:
description: "ROS topic of the field mask debug image message"
read_only: true
- # YOEO model parameters
- yoeo_model_path:
- type: string
- default_value: "2022_10_07_flo_torso21_yoeox"
- description: "Name of YOEO model"
-
- yoeo_nms_threshold:
- type: double
- default_value: 0.4
- description: "YOEO Non-maximum suppression threshold"
- validation:
- bounds<>: [0.0, 1.0]
-
- yoeo_conf_threshold:
- type: double
- default_value: 0.5
- description: "YOEO confidence threshold"
- validation:
- bounds<>: [0.0, 1.0]
-
- yoeo_framework:
- type: string
- default_value: "tvm"
- description: "The neural network framework that should be used ['pytorch', 'openvino', 'onnx', 'tvm']"
+ # YOEO model parameters with hierarchy
+ yoeo:
+ model_path:
+ type: string
+ default_value: "2022_10_07_flo_torso21_yoeox"
+ description: "Name of YOEO model"
+
+ nms_threshold:
+ type: double
+ default_value: 0.4
+ description: "YOEO Non-maximum suppression threshold"
+ validation:
+ bounds<>: [0.0, 1.0]
+
+ conf_threshold:
+ type: double
+ default_value: 0.5
+ description: "YOEO confidence threshold"
+ validation:
+ bounds<>: [0.0, 1.0]
+
+ framework:
+ type: string
+ default_value: "tvm"
+ description: "The neural network framework that should be used"
+ validation:
+ one_of<>: [["pytorch", "openvino", "onnx", "tvm"]]
# Ball detection parameters
ball_candidate_rating_threshold:
From 538a4012359601aeee8ab514d60f28acfd86838a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 24 Sep 2025 17:49:15 +0000
Subject: [PATCH 7/7] Pass yoeo substruct directly to YOEO components instead
of full config
Co-authored-by: Flova <15075613+Flova@users.noreply.github.com>
---
bitbots_vision/bitbots_vision/vision.py | 2 +-
.../vision_modules/yoeo/object_manager.py | 24 ++++----
.../vision_modules/yoeo/yoeo_handlers.py | 58 +++++++++----------
bitbots_vision/config/vision_parameters.yaml | 13 ++---
4 files changed, 48 insertions(+), 49 deletions(-)
diff --git a/bitbots_vision/bitbots_vision/vision.py b/bitbots_vision/bitbots_vision/vision.py
index bf246c0bb..cb4a26b56 100755
--- a/bitbots_vision/bitbots_vision/vision.py
+++ b/bitbots_vision/bitbots_vision/vision.py
@@ -80,7 +80,7 @@ def _dynamic_reconfigure_callback(self, params) -> SetParametersResult:
return SetParametersResult(successful=True)
def _configure_vision(self, config) -> None:
- yoeo.YOEOObjectManager.configure(config)
+ yoeo.YOEOObjectManager.configure(config.yoeo)
debug_image = debug.DebugImage(config.component_debug_image_active)
self._debug_image = debug_image
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
index 72dbda40d..cca167f8f 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/object_manager.py
@@ -73,19 +73,19 @@ def is_team_color_detection_supported(cls) -> bool:
return cls._model_config.team_colors_are_provided()
@classmethod
- def configure(cls, config: parameters.Params) -> None:
+ def configure(cls, yoeo_config) -> None:
if not cls._package_directory_set:
logger.error("Package directory not set!")
- framework = config.yoeo.framework
+ framework = yoeo_config.framework
cls._verify_framework_parameter(framework)
- model_path = cls._get_full_model_path(config.yoeo.model_path)
+ model_path = cls._get_full_model_path(yoeo_config.model_path)
cls._verify_required_neural_network_files_exist(framework, model_path)
- cls._configure_yoeo_instance(config, framework, model_path)
+ cls._configure_yoeo_instance(yoeo_config, framework, model_path)
- cls._config = config
+ cls._config = yoeo_config
cls._framework = framework
cls._model_path = model_path
@@ -108,13 +108,13 @@ def _model_files_exist(cls, framework: str, model_path: str) -> bool:
return cls._HANDLERS_BY_NAME[framework].model_files_exist(model_path)
@classmethod
- def _configure_yoeo_instance(cls, config: parameters.Params, framework: str, model_path: str) -> None:
+ def _configure_yoeo_instance(cls, yoeo_config, framework: str, model_path: str) -> None:
if cls._new_yoeo_handler_is_needed(framework, model_path):
cls._load_model_config(model_path)
cls._instantiate_new_yoeo_handler(config, framework, model_path)
- elif cls._yoeo_parameters_have_changed(config):
+ elif cls._yoeo_parameters_have_changed(yoeo_config):
assert cls._yoeo_instance is not None, "YOEO handler instance not set!"
- cls._yoeo_instance.configure(config)
+ cls._yoeo_instance.configure(yoeo_config)
@classmethod
def _new_yoeo_handler_is_needed(cls, framework: str, model_path: str) -> bool:
@@ -125,9 +125,9 @@ def _load_model_config(cls, model_path: str) -> None:
cls._model_config = ModelConfigLoader.load_from(model_path)
@classmethod
- def _instantiate_new_yoeo_handler(cls, config: parameters.Params, framework: str, model_path: str) -> None:
+ def _instantiate_new_yoeo_handler(cls, yoeo_config, framework: str, model_path: str) -> None:
cls._yoeo_instance = cls._HANDLERS_BY_NAME[framework](
- config,
+ yoeo_config,
model_path,
cls._model_config.get_detection_classes(),
cls._model_config.get_robot_class_ids(),
@@ -136,9 +136,9 @@ def _instantiate_new_yoeo_handler(cls, config: parameters.Params, framework: str
logger.info(f"Using {cls._yoeo_instance.__class__.__name__}")
@classmethod
- def _yoeo_parameters_have_changed(cls, new_config: parameters.Params) -> bool:
+ def _yoeo_parameters_have_changed(cls, new_yoeo_config) -> bool:
if cls._config is None:
return True
# Compare YOEO parameters using the hierarchical structure
- return cls._config.yoeo != new_config.yoeo
+ return cls._config != new_yoeo_config
diff --git a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
index 735a13266..50061f285 100644
--- a/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
+++ b/bitbots_vision/bitbots_vision/vision_modules/yoeo/yoeo_handlers.py
@@ -24,7 +24,7 @@ class IYOEOHandler(ABC):
"""
@abstractmethod
- def configure(self, config: parameters.Params) -> None:
+ def configure(self, yoeo_config) -> None:
"""
Allows to (re-) configure the YOEO handler.
"""
@@ -100,7 +100,7 @@ class YOEOHandlerTemplate(IYOEOHandler):
def __init__(
self,
- config: parameters.Params,
+ yoeo_config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -119,12 +119,12 @@ def __init__(
self._seg_class_names: list[str] = seg_class_names
self._seg_masks: dict = dict()
- self._use_caching: bool = config.caching
+ self._use_caching: bool = yoeo_config.caching
logger.debug("Leaving YOEOHandlerTemplate constructor")
- def configure(self, config: parameters.Params) -> None:
- self._use_caching = config.caching
+ def configure(self, yoeo_config) -> None:
+ self._use_caching = yoeo_config.caching
def get_available_detection_class_names(self) -> list[str]:
return self._det_class_names
@@ -213,7 +213,7 @@ class YOEOHandlerONNX(YOEOHandlerTemplate):
def __init__(
self,
- config: parameters.Params,
+ yoeo_config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -240,8 +240,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -250,13 +250,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config: parameters.Params) -> None:
+ def configure(self, yoeo_config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -286,7 +286,7 @@ class YOEOHandlerOpenVino(YOEOHandlerTemplate):
def __init__(
self,
- config: parameters.Params,
+ yoeo_config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -322,8 +322,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -339,13 +339,13 @@ def _select_device(self) -> str:
device = "CPU"
return device
- def configure(self, config: parameters.Params) -> None:
+ def configure(self, yoeo_config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer.shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
@@ -374,7 +374,7 @@ class YOEOHandlerPytorch(YOEOHandlerTemplate):
def __init__(
self,
- config: parameters.Params,
+ yoeo_config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -400,8 +400,8 @@ def __init__(
logger.debug(f"Loading files...\n\t{config_path}\n\t{weights_path}")
self._model = torch_models.load_model(config_path, weights_path)
- self._conf_thresh: float = config.yoeo.conf_threshold
- self._nms_thresh: float = config.yoeo.nms_threshold
+ self._conf_thresh: float = yoeo_config.conf_threshold
+ self._nms_thresh: float = yoeo_config.nms_threshold
self._group_config: torch_GroupConfig = self._update_group_config()
logger.debug(f"Leaving {self.__class__.__name__} constructor")
@@ -411,10 +411,10 @@ def _update_group_config(self):
return self.torch_group_config(group_ids=robot_class_ids, surrogate_id=robot_class_ids[0])
- def configure(self, config: parameters.Params) -> None:
+ def configure(self, yoeo_config) -> None:
super().configure(config)
- self._conf_thresh = config.yoeo.conf_threshold
- self._nms_thresh = config.yoeo.nms_threshold
+ self._conf_thresh = yoeo_config.conf_threshold
+ self._nms_thresh = yoeo_config.nms_threshold
self._group_config = self._update_group_config()
@staticmethod
@@ -448,7 +448,7 @@ class YOEOHandlerTVM(YOEOHandlerTemplate):
def __init__(
self,
- config: parameters.Params,
+ yoeo_config,
model_directory: str,
det_class_names: list[str],
det_robot_class_ids: list[int],
@@ -487,8 +487,8 @@ def __init__(
self._det_postprocessor: utils.IDetectionPostProcessor = utils.DefaultDetectionPostProcessor(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
self._seg_postprocessor: utils.ISegmentationPostProcessor = utils.DefaultSegmentationPostProcessor(
@@ -497,13 +497,13 @@ def __init__(
logger.debug(f"Leaving {self.__class__.__name__} constructor")
- def configure(self, config: parameters.Params) -> None:
+ def configure(self, yoeo_config) -> None:
super().configure(config)
self._det_postprocessor.configure(
image_preprocessor=self._img_preprocessor,
output_img_size=self._input_layer_shape[2],
- conf_thresh=config.yoeo.conf_threshold,
- nms_thresh=config.yoeo.nms_threshold,
+ conf_thresh=yoeo_config.conf_threshold,
+ nms_thresh=yoeo_config.nms_threshold,
robot_class_ids=self.get_robot_class_ids(),
)
diff --git a/bitbots_vision/config/vision_parameters.yaml b/bitbots_vision/config/vision_parameters.yaml
index afecfc2f2..e6106bc7d 100644
--- a/bitbots_vision/config/vision_parameters.yaml
+++ b/bitbots_vision/config/vision_parameters.yaml
@@ -106,6 +106,11 @@ bitbots_vision:
description: "The neural network framework that should be used"
validation:
one_of<>: [["pytorch", "openvino", "onnx", "tvm"]]
+
+ caching:
+ type: bool
+ default_value: true
+ description: "Used to deactivate caching for profiling reasons"
# Ball detection parameters
ball_candidate_rating_threshold:
@@ -120,10 +125,4 @@ bitbots_vision:
default_value: 1
description: "The maximum number of balls that should be published"
validation:
- bounds<>: [0, 50]
-
- # Caching parameter
- caching:
- type: bool
- default_value: true
- description: "Used to deactivate caching for profiling reasons"
\ No newline at end of file
+ bounds<>: [0, 50]
\ No newline at end of file