Skip to content

Commit 4c26a9c

Browse files
committed
Merge branch 'main' into feature/throw_in
2 parents bb86806 + 29f0423 commit 4c26a9c

File tree

201 files changed

+1816
-2291
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

201 files changed

+1816
-2291
lines changed

.github/workflows/pre-commit.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
pre-commit:
10-
runs-on: ubuntu-latest
10+
runs-on: ubuntu-22.04
1111
steps:
1212
- uses: actions/checkout@v4
1313
- uses: actions/setup-python@v4

.vscode/settings.json

+2
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,11 @@
222222
"ros.distro": "iron",
223223
"search.useIgnoreFiles": false,
224224
"python.autoComplete.extraPaths": [
225+
"/opt/openrobots/lib/python3.10/site-packages",
225226
"/opt/ros/iron/lib/python3.10/site-packages"
226227
],
227228
"python.analysis.extraPaths": [
229+
"/opt/openrobots/lib/python3.10/site-packages",
228230
"/opt/ros/iron/lib/python3.10/site-packages"
229231
],
230232
"cmake.configureOnOpen": false,

bitbots_behavior/bitbots_blackboard/CMakeLists.txt

+6-70
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,16 @@
11
cmake_minimum_required(VERSION 3.5)
22
project(bitbots_blackboard)
33

4-
# Add support for C++17
5-
if(NOT CMAKE_CXX_STANDARD)
6-
set(CMAKE_CXX_STANDARD 17)
7-
endif()
8-
9-
find_package(bio_ik_msgs REQUIRED)
10-
find_package(ament_cmake REQUIRED)
11-
find_package(sensor_msgs REQUIRED)
12-
find_package(rclpy REQUIRED)
13-
find_package(tf2 REQUIRED)
14-
find_package(bitbots_msgs REQUIRED)
15-
find_package(std_msgs REQUIRED)
16-
find_package(tf2_geometry_msgs REQUIRED)
17-
find_package(std_srvs REQUIRED)
18-
find_package(geometry_msgs REQUIRED)
194
find_package(bitbots_docs REQUIRED)
5+
find_package(ament_cmake REQUIRED)
6+
find_package(ament_cmake_python REQUIRED)
207

21-
set(INCLUDE_DIRS
22-
${bio_ik_msgs_INCLUDE_DIRS}
23-
${ament_cmake_INCLUDE_DIRS}
24-
${sensor_msgs_INCLUDE_DIRS}
25-
${rclpy_INCLUDE_DIRS}
26-
${tf2_INCLUDE_DIRS}
27-
${bitbots_msgs_INCLUDE_DIRS}
28-
${std_msgs_INCLUDE_DIRS}
29-
${tf2_geometry_msgs_INCLUDE_DIRS}
30-
${std_srvs_INCLUDE_DIRS}
31-
${geometry_msgs_INCLUDE_DIRS}
32-
${bitbots_docs_INCLUDE_DIRS})
33-
include_directories(${INCLUDE_DIRS})
34-
35-
set(LIBRARY_DIRS
36-
${bio_ik_msgs_LIBRARY_DIRS}
37-
${ament_cmake_LIBRARY_DIRS}
38-
${sensor_msgs_LIBRARY_DIRS}
39-
${rclpy_LIBRARY_DIRS}
40-
${tf2_LIBRARY_DIRS}
41-
${bitbots_msgs_LIBRARY_DIRS}
42-
${std_msgs_LIBRARY_DIRS}
43-
${tf2_geometry_msgs_LIBRARY_DIRS}
44-
${std_srvs_LIBRARY_DIRS}
45-
${geometry_msgs_LIBRARY_DIRS}
46-
${bitbots_docs_LIBRARY_DIRS})
47-
48-
link_directories(${LIBRARY_DIRS})
49-
50-
set(LIBS
51-
${bio_ik_msgs_LIBRARIES}
52-
${ament_cmake_LIBRARIES}
53-
${sensor_msgs_LIBRARIES}
54-
${rclpy_LIBRARIES}
55-
${tf2_LIBRARIES}
56-
${bitbots_msgs_LIBRARIES}
57-
${std_msgs_LIBRARIES}
58-
${tf2_geometry_msgs_LIBRARIES}
59-
${std_srvs_LIBRARIES}
60-
${geometry_msgs_LIBRARIES}
61-
${bitbots_docs_LIBRARIES})
62-
63-
include(${CMAKE_BINARY_DIR}/../bitbots_docs/enable_bitbots_docs.cmake)
648
enable_bitbots_docs()
659

66-
ament_export_dependencies(bio_ik_msgs)
67-
ament_export_dependencies(ament_cmake)
68-
ament_export_dependencies(sensor_msgs)
69-
ament_export_dependencies(rclpy)
70-
ament_export_dependencies(tf2)
71-
ament_export_dependencies(bitbots_msgs)
72-
ament_export_dependencies(std_msgs)
73-
ament_export_dependencies(tf2_geometry_msgs)
74-
ament_export_dependencies(std_srvs)
75-
ament_export_dependencies(geometry_msgs)
76-
ament_export_dependencies(bitbots_docs)
77-
ament_export_include_directories(${INCLUDE_DIRS})
10+
if(BUILD_TESTING)
11+
find_package(ament_cmake_mypy REQUIRED)
12+
ament_mypy(CONFIG_FILE "${CMAKE_CURRENT_LIST_DIR}/mypy.ini")
13+
endif()
7814

7915
ament_python_install_package(${PROJECT_NAME})
8016

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Setting up runtime type checking for this package
2+
from beartype.claw import beartype_this_package
3+
4+
beartype_this_package()

bitbots_behavior/bitbots_blackboard/bitbots_blackboard/body_blackboard.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
class BodyBlackboard:
16-
def __init__(self, node: Node, tf_buffer: tf2.Buffer):
16+
def __init__(self, node: Node, tf_buffer: tf2.BufferInterface):
1717
# References
1818
self.node = node
1919
self.tf_buffer = tf_buffer
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
from typing import TYPE_CHECKING, Optional
1+
from typing import TYPE_CHECKING
22

3+
from bitbots_utils.utils import nobeartype
34
from rclpy.node import Node
45

56
if TYPE_CHECKING:
@@ -9,6 +10,7 @@
910
class AbstractBlackboardCapsule:
1011
"""Abstract class for blackboard capsules."""
1112

12-
def __init__(self, node: Node, blackboard: Optional["BodyBlackboard"] = None):
13+
@nobeartype
14+
def __init__(self, node: Node, blackboard: "BodyBlackboard"):
1315
self._node = node
1416
self._blackboard = blackboard

bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py

+22-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import math
2-
from typing import List, Optional, Tuple
2+
from typing import Optional
33

44
import numpy as np
55
import tf2_ros as tf2
@@ -53,7 +53,7 @@ def __init__(self, node, blackboard):
5353
self.calc_base_costmap()
5454
self.calc_gradients()
5555

56-
def robot_callback(self, msg: RobotArray):
56+
def robot_callback(self, msg: RobotArray) -> None:
5757
"""
5858
Callback with new robot detections
5959
"""
@@ -77,10 +77,11 @@ def robot_callback(self, msg: RobotArray):
7777
# Publish debug costmap
7878
self.publish_costmap()
7979

80-
def publish_costmap(self):
80+
def publish_costmap(self) -> None:
8181
"""
8282
Publishes the costmap for rviz
8383
"""
84+
assert self.costmap is not None, "Costmap is not initialized"
8485
# Normalize costmap to match the rviz color scheme in a good way
8586
normalized_costmap = (
8687
(255 - ((self.costmap - np.min(self.costmap)) / (np.max(self.costmap) - np.min(self.costmap))) * 255 / 2.1)
@@ -131,7 +132,7 @@ def get_pass_regions(self) -> np.ndarray:
131132
# Smooth obstacle map
132133
return gaussian_filter(costmap, pass_smooth)
133134

134-
def field_2_costmap_coord(self, x: float, y: float) -> Tuple[float, float]:
135+
def field_2_costmap_coord(self, x: float, y: float) -> tuple[int, int]:
135136
"""
136137
Converts a field position to the corresponding indices for the costmap.
137138
@@ -153,10 +154,11 @@ def field_2_costmap_coord(self, x: float, y: float) -> Tuple[float, float]:
153154
)
154155
return idx_x, idx_y
155156

156-
def calc_gradients(self):
157+
def calc_gradients(self) -> None:
157158
"""
158159
Recalculates the gradient map based on the current costmap.
159160
"""
161+
assert self.base_costmap is not None, "Base costmap is not initialized"
160162
gradient = np.gradient(self.base_costmap)
161163
norms = np.linalg.norm(gradient, axis=0)
162164

@@ -186,7 +188,7 @@ def cost_at_relative_xy(self, x: float, y: float) -> float:
186188

187189
return self.get_cost_at_field_position(point.point.x, point.point.y)
188190

189-
def calc_base_costmap(self):
191+
def calc_base_costmap(self) -> None:
190192
"""
191193
Builds the base costmap based on the behavior parameters.
192194
This costmap includes a gradient towards the enemy goal and high costs outside the playable area
@@ -203,11 +205,11 @@ def calc_base_costmap(self):
203205

204206
# Create Grid
205207
grid_x, grid_y = np.mgrid[
206-
0 : self.field_length + self.map_margin * 2 : (self.field_length + self.map_margin * 2) * 10j,
207-
0 : self.field_width + self.map_margin * 2 : (self.field_width + self.map_margin * 2) * 10j,
208+
0 : self.field_length + self.map_margin * 2 : (self.field_length + self.map_margin * 2) * 10j, # type: ignore[misc]
209+
0 : self.field_width + self.map_margin * 2 : (self.field_width + self.map_margin * 2) * 10j, # type: ignore[misc]
208210
]
209211

210-
fix_points: List[Tuple[Tuple[float, float], float]] = []
212+
fix_points: list[tuple[tuple[float, float], float]] = []
211213

212214
# Add base points
213215
fix_points.extend(
@@ -278,15 +280,17 @@ def calc_base_costmap(self):
278280
)
279281

280282
# Smooth the costmap to get more continuous gradients
281-
self.base_costmap = gaussian_filter(interpolated, self.body_config["base_costmap_smoothing_sigma"])
283+
base_costmap: np.ndarray = gaussian_filter(interpolated, self.body_config["base_costmap_smoothing_sigma"])
284+
self.base_costmap = base_costmap
282285
self.costmap = self.base_costmap.copy()
283286

284-
def get_gradient_at_field_position(self, x: float, y: float) -> Tuple[float, float]:
287+
def get_gradient_at_field_position(self, x: float, y: float) -> tuple[float, float]:
285288
"""
286289
Gets the gradient tuple at a given field position
287290
:param x: Field coordinate in the x direction
288291
:param y: Field coordinate in the y direction
289292
"""
293+
assert self.gradient_map is not None, "Gradient map is not initialized"
290294
idx_x, idx_y = self.field_2_costmap_coord(x, y)
291295
return -self.gradient_map[0][idx_x, idx_y], -self.gradient_map[1][idx_x, idx_y]
292296

@@ -296,10 +300,11 @@ def get_cost_at_field_position(self, x: float, y: float) -> float:
296300
:param x: Field coordinate in the x direction
297301
:param y: Field coordinate in the y direction
298302
"""
303+
assert self.costmap is not None, "Costmap is not initialized"
299304
idx_x, idx_y = self.field_2_costmap_coord(x, y)
300305
return self.costmap[idx_x, idx_y]
301306

302-
def get_gradient_direction_at_field_position(self, x: float, y: float):
307+
def get_gradient_direction_at_field_position(self, x: float, y: float) -> float:
303308
"""
304309
Returns the gradient direction at the given position
305310
:param x: Field coordinate in the x direction
@@ -318,7 +323,9 @@ def get_gradient_direction_at_field_position(self, x: float, y: float):
318323
grad = self.get_gradient_at_field_position(x, y)
319324
return math.atan2(grad[1], grad[0])
320325

321-
def get_cost_of_kick_relative(self, x: float, y: float, direction: float, kick_length: float, angular_range: float):
326+
def get_cost_of_kick_relative(
327+
self, x: float, y: float, direction: float, kick_length: float, angular_range: float
328+
) -> float:
322329
"""
323330
Returns the cost of a kick at the given position and direction in base footprint frame
324331
:param x: Field coordinate in the x direction
@@ -356,6 +363,7 @@ def get_cost_of_kick(self, x: float, y: float, direction: float, kick_length: fl
356363
:param kick_length: The length of the kick
357364
:param angular_range: The angular range of the kick
358365
"""
366+
assert self.costmap is not None, "Costmap is not initialized"
359367

360368
# create a mask in the size of the costmap consisting of 8-bit values initialized as 0
361369
mask = Image.new("L", (self.costmap.shape[1], self.costmap.shape[0]))
@@ -386,7 +394,7 @@ def get_cost_of_kick(self, x: float, y: float, direction: float, kick_length: fl
386394
# This should contribute way less than the max and should have an impact if the max values are similar in all directions.
387395
return masked_costmap.max() * 0.75 + masked_costmap.min() * 0.25
388396

389-
def get_current_cost_of_kick(self, direction: float, kick_length: float, angular_range: float):
397+
def get_current_cost_of_kick(self, direction: float, kick_length: float, angular_range: float) -> float:
390398
"""
391399
Returns the cost of the kick at the current position
392400
:param direction: The direction of the kick

0 commit comments

Comments
 (0)