Skip to content

Commit dc43ea0

Browse files
authored
Use imu rotation to estimate orientation of odometry (#623)
2 parents 848fadb + ae724a5 commit dc43ea0

10 files changed

Lines changed: 79 additions & 234 deletions

File tree

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@
215215
"future": "cpp",
216216
"*.ipp": "cpp",
217217
"span": "cpp",
218-
"ranges": "cpp"
218+
"ranges": "cpp",
219+
"core": "cpp"
219220
},
220221
// Tell the ROS extension where to find the setup.bash
221222
// This also utilizes the COLCON_WS environment variable, which needs to be set

bitbots_navigation/bitbots_localization/config/config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ bitbots_localization:
33
misc:
44
init_mode: 0
55
percentage_best_particles: 100
6-
max_motion_linear: 0.5
7-
max_motion_angular: 3.14
6+
max_motion_linear: 1.5
7+
max_motion_angular: 7.0
88
ros:
99
line_pointcloud_topic: 'line_mask_relative_pc'
1010
goal_topic: 'goals_simulated'
@@ -26,8 +26,8 @@ bitbots_localization:
2626
rotation_to_direction: 0.0
2727
distance_to_distance: 0.1
2828
rotation_to_distance: 0.2
29-
distance_to_rotation: 0.2
30-
rotation_to_rotation: 1.5
29+
distance_to_rotation: 0.05
30+
rotation_to_rotation: 0.2
3131
max_rotation: 0.45
3232
max_translation: 0.09
3333
weighting:

bitbots_navigation/bitbots_localization/include/bitbots_localization/localization.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ class Localization {
214214
std::shared_ptr<Map> lines_;
215215
std::shared_ptr<Map> goals_;
216216

217+
// Flag that enables or disables observations
218+
bool observe_ = true;
219+
217220
// RNG that is used for the different sampling steps
218221
particle_filter::CRandomNumberGenerator random_number_generator_;
219222

bitbots_navigation/bitbots_localization/src/localization.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ void Localization::run_filter_one_step() {
152152
updateParams();
153153

154154
// Set the measurements in the observation model
155-
updateMeasurements();
155+
if (observe_) {
156+
updateMeasurements();
157+
}
156158

157159
// Get the odometry offset since the last cycle
158160
getMotion();
@@ -204,11 +206,7 @@ void Localization::SetInitialPositionCallback(const gm::msg::PoseWithCovarianceS
204206

205207
bool Localization::set_paused_callback(const std::shared_ptr<bl::srv::SetPaused::Request> req,
206208
std::shared_ptr<bl::srv::SetPaused::Response> res) {
207-
if (req->paused) {
208-
publishing_timer_->cancel();
209-
} else {
210-
publishing_timer_->reset();
211-
}
209+
observe_ = !req->paused;
212210
res->success = true;
213211
return true;
214212
}
Lines changed: 6 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,9 @@
1-
import math
2-
3-
import tf2_ros as tf2
41
from bitbots_localization.srv import ResetFilter
5-
from rclpy.duration import Duration
6-
from rclpy.time import Time
72

83
from bitbots_localization_handler.localization_dsd.actions import AbstractLocalizationActionElement
94

105

11-
class AbstractInitialize(AbstractLocalizationActionElement):
12-
def __init__(self, blackboard, dsd, parameters):
13-
super().__init__(blackboard, dsd, parameters)
14-
15-
# Save the type the instance that called super, so we know what was the last init mode
16-
if not isinstance(self, RedoLastInit):
17-
self.blackboard.last_init_action_type = self.__class__
18-
try:
19-
# only need this in simulation
20-
if self.blackboard.use_sim_time:
21-
self.blackboard.last_init_odom_transform = self.blackboard.tf_buffer.lookup_transform(
22-
self.blackboard.odom_frame,
23-
self.blackboard.base_footprint_frame,
24-
Time(),
25-
Duration(seconds=1.0),
26-
) # wait up to 1 second for odom data
27-
except (tf2.LookupException, tf2.ConnectivityException, tf2.ExtrapolationException) as e:
28-
self.blackboard.node.get_logger().warn(f"Not able to save the odom position due to a tf error: {e}")
29-
self.blackboard.node.get_logger().debug(
30-
f"Set last init action type to {self.blackboard.last_init_action_type}"
31-
)
32-
33-
def perform(self, reevaluate=False):
34-
raise NotImplementedError
35-
36-
37-
class InitOpponentHalf(AbstractInitialize):
6+
class InitOpponentHalf(AbstractLocalizationActionElement):
387
def perform(self, reevaluate=False):
398
self.do_not_reevaluate()
409
self.blackboard.node.get_logger().debug("Initializing on the opponent half")
@@ -44,7 +13,7 @@ def perform(self, reevaluate=False):
4413
return self.pop()
4514

4615

47-
class InitOwnHalf(AbstractInitialize):
16+
class InitOwnHalf(AbstractLocalizationActionElement):
4817
def perform(self, reevaluate=False):
4918
self.do_not_reevaluate()
5019
self.blackboard.node.get_logger().debug("Initializing on our half")
@@ -54,7 +23,7 @@ def perform(self, reevaluate=False):
5423
return self.pop()
5524

5625

57-
class InitPosition(AbstractInitialize):
26+
class InitPosition(AbstractLocalizationActionElement):
5827
def perform(self, reevaluate=False):
5928
self.do_not_reevaluate()
6029
self.blackboard.node.get_logger().debug("Initializing position")
@@ -79,42 +48,7 @@ def perform(self, reevaluate=False):
7948
return self.pop()
8049

8150

82-
class InitPoseAfterFall(AbstractInitialize):
83-
def perform(self, reevaluate=False):
84-
self.do_not_reevaluate()
85-
self.blackboard.node.get_logger().debug("Initializing at pose after fall")
86-
while not self.blackboard.reset_filter_proxy.wait_for_service(timeout_sec=3.0):
87-
self.blackboard.node.get_logger().info("Localization reset service not available, waiting again...")
88-
89-
# Abort if we don't know the current pose
90-
if self.blackboard.robot_pose is None:
91-
self.blackboard.node.get_logger().warn(
92-
"Can't initialize position, because we don't know the current position"
93-
)
94-
return self.pop()
95-
96-
# Calculate the angle of the robot after the fall by adding the yaw difference during the fall
97-
# (estimated by the IMU) to the last known localization yaw
98-
esimated_angle = (
99-
self.blackboard.get_imu_yaw()
100-
- self.blackboard.imu_yaw_before_fall
101-
+ self.blackboard.get_localization_yaw()
102-
+ math.tau
103-
) % math.tau
104-
105-
# Tell the localization to reset to the last position and the estimated angle
106-
self.blackboard.reset_filter_proxy.call_async(
107-
ResetFilter.Request(
108-
init_mode=ResetFilter.Request.POSE,
109-
x=self.blackboard.robot_pose.pose.pose.position.x,
110-
y=self.blackboard.robot_pose.pose.pose.position.y,
111-
angle=esimated_angle,
112-
)
113-
)
114-
return self.pop()
115-
116-
117-
class InitSide(AbstractInitialize):
51+
class InitSide(AbstractLocalizationActionElement):
11852
def perform(self, reevaluate=False):
11953
self.do_not_reevaluate()
12054
self.blackboard.node.get_logger().debug("Initializing on the side lines of our side")
@@ -124,7 +58,7 @@ def perform(self, reevaluate=False):
12458
return self.pop()
12559

12660

127-
class InitGoal(AbstractInitialize):
61+
class InitGoal(AbstractLocalizationActionElement):
12862
def perform(self, reevaluate=False):
12963
self.do_not_reevaluate()
13064
self.blackboard.node.get_logger().debug("Initializing on the side lines of our side")
@@ -136,7 +70,7 @@ def perform(self, reevaluate=False):
13670
return self.pop()
13771

13872

139-
class InitPenaltyKick(AbstractInitialize):
73+
class InitPenaltyKick(AbstractLocalizationActionElement):
14074
def perform(self, reevaluate=False):
14175
self.do_not_reevaluate()
14276
self.blackboard.node.get_logger().debug("Initializing behind penalty mark")
@@ -148,32 +82,3 @@ def perform(self, reevaluate=False):
14882
)
14983
)
15084
return self.pop()
151-
152-
153-
class RedoLastInit(AbstractInitialize):
154-
"""
155-
Executes an action with the type of the last action
156-
"""
157-
158-
def __init__(self, blackboard, dsd, parameters):
159-
super().__init__(blackboard, dsd, parameters)
160-
if self.blackboard.last_init_action_type is None:
161-
raise ValueError("There is no last init action to redo")
162-
163-
# Creates an instance of the last init action
164-
self.sub_action: AbstractInitialize = self.blackboard.last_init_action_type(blackboard, dsd, parameters)
165-
166-
def perform(self, reevaluate=False):
167-
self.blackboard.node.get_logger().debug("Redoing the last init")
168-
return self.sub_action.perform(reevaluate)
169-
170-
171-
class StoreCurrentIMUYaw(AbstractLocalizationActionElement):
172-
def perform(self, reevaluate=False):
173-
self.do_not_reevaluate()
174-
self.blackboard.node.get_logger().debug("Storing current IMU yaw")
175-
# Get yaw component of the current orientation before the fall
176-
# It drifts over time, so this is not an absolute measurement, but it will help to overcome
177-
# The short time during the fall at which we have no odometry
178-
self.blackboard.imu_yaw_before_fall = self.blackboard.get_imu_yaw()
179-
return self.pop()

bitbots_navigation/bitbots_localization_handler/bitbots_localization_handler/localization_dsd/decisions/walk.py

Lines changed: 0 additions & 49 deletions
This file was deleted.

bitbots_navigation/bitbots_localization_handler/bitbots_localization_handler/localization_dsd/localization.dsd

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ $SecondaryStateDecider
99

1010
-->Localization
1111
$GettingUpState
12-
YES --> @StoreCurrentIMUYaw, @LocalizationStop, @DoNothing
13-
GOTUP --> $WalkedSinceLastInit + dist:%walking_moved_distance
14-
YES --> @InitPoseAfterFall, @LocalizationStart, @DoNothing
15-
NO --> @RedoLastInit, @LocalizationStart, @DoNothing
12+
YES --> @LocalizationStop, @DoNothing
13+
GOTUP --> @LocalizationStart, @DoNothing
1614
NO --> $CheckGameStateReceived
1715
NO_GAMESTATE_INIT --> @InitSide, @DoNothing
1816
DO_NOTHING --> @DoNothing

bitbots_navigation/bitbots_localization_handler/bitbots_localization_handler/localization_dsd/localization_blackboard.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
1-
from typing import Optional, Type
2-
31
import numpy as np
4-
import tf2_ros as tf2
52
from bitbots_blackboard.capsules.game_status_capsule import GameStatusCapsule
63
from bitbots_localization.srv import ResetFilter, SetPaused
7-
from bitbots_utils.transforms import quat2euler, xyzw2wxyz
84
from bitbots_utils.utils import get_parameters_from_other_node
95
from geometry_msgs.msg import PoseWithCovarianceStamped, Quaternion
106
from jaxtyping import Float
11-
from rclpy.duration import Duration
127
from rclpy.node import Node
138
from ros2_numpy import numpify
149
from sensor_msgs.msg import Imu
15-
from tf2_geometry_msgs import TransformStamped
1610

1711
from bitbots_msgs.msg import RobotControlState
1812

@@ -22,16 +16,6 @@ def __init__(self, node: Node):
2216
self.node = node
2317

2418
self.initialized = False
25-
self.use_sim_time = self.node.get_parameter("use_sim_time").value
26-
27-
# we only need tf in simulation. don't use it otherwise to safe performance
28-
if self.use_sim_time:
29-
self.tf_buffer = tf2.Buffer(cache_time=Duration(seconds=10))
30-
self.tf_listener = tf2.TransformListener(self.tf_buffer, node)
31-
32-
# Get names of relevant frames
33-
self.odom_frame: str = node.get_parameter("odom_frame").value
34-
self.base_footprint_frame: str = node.get_parameter("base_footprint_frame").value
3519

3620
# Get the length of the field
3721
self.field_length = get_parameters_from_other_node(self.node, "parameter_blackboard", ["field.size.x"])[
@@ -58,17 +42,10 @@ def __init__(self, node: Node):
5842
self.accel: Float[np.ndarray, "3"] = np.array([0.0, 0.0, 0.0])
5943
self.imu_orientation = Quaternion(w=1.0)
6044

61-
# Falling odometry / imu interpolation during falls
62-
self.imu_yaw_before_fall: float = 0.0
63-
6445
# Picked up
6546
self.pickup_accel_buffer: list[Float[np.ndarray, "3"]] = []
6647
self.pickup_accel_buffer_long: list[Float[np.ndarray, "3"]] = []
6748

68-
# Last init action
69-
self.last_init_action_type: Optional[Type] = None
70-
self.last_init_odom_transform: TransformStamped | None = None
71-
7249
def _callback_pose(self, msg: PoseWithCovarianceStamped):
7350
self.robot_pose = msg
7451

@@ -111,14 +88,3 @@ def picked_up(self) -> bool:
11188
absolute_diff = abs(mean_long - mean)
11289

11390
return bool(absolute_diff > 1.0)
114-
115-
def get_imu_yaw(self) -> float:
116-
"""Returns the current yaw of the IMU (this is not an absolute measurement!!! It drifts over time!)"""
117-
118-
return quat2euler(xyzw2wxyz(numpify(self.imu_orientation)), axes="szxy")[0]
119-
120-
def get_localization_yaw(self) -> float:
121-
"""Returns the current yaw of the robot according to the localization. 0 is the x-axis of the field."""
122-
if self.robot_pose is None:
123-
return 0.0
124-
return quat2euler(xyzw2wxyz(numpify(self.robot_pose.pose.pose.orientation)), axes="szxy")[0]

bitbots_navigation/bitbots_odometry/launch/odometry.launch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<param name="odom_frame" value="$(var tf_prefix)odom"/>
1414
<param name="use_sim_time" value="$(var sim)"/>
1515
</node>
16-
16+
1717
<node name="odometry_fuser" pkg="bitbots_odometry" exec="odometry_fuser" launch-prefix="$(var taskset)">
1818
<param name="base_link_frame" value="$(var tf_prefix)base_link"/>
1919
<param name="r_sole_frame" value="$(var tf_prefix)r_sole"/>

0 commit comments

Comments
 (0)