def get_detection_based_goal_position_uv(self):
        """
        returns the position of the goal relative to the robot.
        if only a single post is detected, the position of the post is returned.
        else, it is the point between the posts
        :return:
        """
        left = PointStamped(self.goal_odom.header, self.goal_odom.left_post)
        right = PointStamped(self.goal_odom.header, self.goal_odom.right_post)
        left.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        right.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        try:
            left_bfp = self.tf_buffer.transform(
                left, self.base_footprint_frame,
                timeout=Duration(seconds=0.2)).point
            right_bfp = self.tf_buffer.transform(
                right,
                self.base_footprint_frame,
                timeout=Duration(seconds=0.2)).point
        except tf2.ExtrapolationException as e:
            self.get_logger().warn(e)
            self.get_logger().error(
                'Severe transformation problem concerning the goal!')
            return None

        return (left_bfp.x + right_bfp.x / 2.0), \
               (left_bfp.y + right_bfp.y / 2.0)
    def forget_ball(self, own=True, team=True, reset_ball_filter=True):
        """
        Forget that we and the best teammate saw a ball, optionally reset the ball filter
        :param own: Forget the ball recognized by the own robot, defaults to True
        :type own: bool, optional
        :param team: Forget the ball received from the team, defaults to True
        :type team: bool, optional
        :param reset_ball_filter: Reset the ball filter, defaults to True
        :type reset_ball_filter: bool, optional
        """
        if own:  # Forget own ball
            self.ball_seen_time = rclpy.Time(seconds=0, nanoseconds=0)
            self.ball = PointStamped()

        if team:  # Forget team ball
            self.ball_seen_time_teammate = rclpy.Time(seconds=0, nanoseconds=0)
            self.ball_teammate = PointStamped()

        if reset_ball_filter:  # Reset the ball filter
            result = self.reset_ball_filter()
            if result.success:
                self.get_logger().info(
                    f"Received message from ball filter: '{result.message}'",
                    logger_name='bitbots_blackboard')
            else:
                self.get_logger().warn(
                    f"Ball filter reset failed with: '{result.message}'",
                    logger_name='bitbots_blackboard')
    def ball_filtered_callback(self, msg: PoseWithCovarianceStamped):
        self.ball_filtered = msg

        # When the precision is not sufficient, the ball ages.
        x_sdev = msg.pose.covariance[0]  # position 0,0 in a 6x6-matrix
        y_sdev = msg.pose.covariance[7]  # position 1,1 in a 6x6-matrix
        if x_sdev > self.body_config['ball_position_precision_threshold']['x_sdev'] or \
                y_sdev > self.body_config['ball_position_precision_threshold']['y_sdev']:
            self.forget_ball(own=True, team=False, reset_ball_filter=False)
            return

        ball_buffer = PointStamped(msg.header, msg.pose.pose.position)
        try:
            self.ball = self.tf_buffer.transform(ball_buffer,
                                                 self.base_footprint_frame,
                                                 timeout=Duration(seconds=0.3))
            self.ball_odom = self.tf_buffer.transform(
                ball_buffer, self.odom_frame, timeout=Duration(seconds=0.3))
            self.ball_map = self.tf_buffer.transform(
                ball_buffer, self.map_frame, timeout=Duration(seconds=0.3))
            # Set timestamps to zero to get the newest transform when this is transformed later
            self.ball_odom.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
            self.ball_map.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
            self.ball_seen_time = self.get_clock().now()
            self.ball_publisher.publish(self.ball)
            self.ball_seen = True

        except (tf2.ConnectivityException, tf2.LookupException,
                tf2.ExtrapolationException) as e:
            self.get_logger().warn(e)
    def get_cost_of_kick_relative(self, x, y, direction, kick_length,
                                  angular_range):
        if self.costmap is None:
            return 0.0

        pose = PoseStamped()
        pose.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        pose.header.frame_id = self.base_footprint_frame
        pose.pose.position.x = x
        pose.pose.position.y = y

        pose.pose.orientation = Quaternion(
            *quaternion_from_euler(0, 0, direction))
        try:
            # Transform point of interest to the map
            pose = self.tf_buffer.transform(pose,
                                            self.map_frame,
                                            timeout=Duration(seconds=0.3))

        except (tf2.ConnectivityException, tf2.LookupException,
                tf2.ExtrapolationException) as e:
            self.get_logger().warn(e)
            return 0.0
        d = euler_from_quaternion([
            pose.pose.orientation.x, pose.pose.orientation.y,
            pose.pose.orientation.z, pose.pose.orientation.w
        ])[2]
        return self.get_cost_of_kick(pose.pose.position.x,
                                     pose.pose.position.y, d, kick_length,
                                     angular_range)
Beispiel #5
0
    def perform(self, reevaluate=False):
        """
        Check if the time we last saw the ball is now.
        "Now"-Tolerance can be configured through ball_lost_time.

        :param reevaluate: Has no effect
        """

        ball_last_seen = self.blackboard.world_model.ball_last_seen()
        if ball_last_seen != rclpy.Time(0) and self.get_clock().now() - ball_last_seen < self.ball_lost_time:
            return 'YES'
        return 'NO'
 def localization_pose_current(self) -> bool:
     """
     Returns whether we can transform into and from the map frame.
     """
     # if we can do this, we should be able to transform the ball
     # (unless the localization dies in the next 0.2 seconds)
     try:
         t = self.get_clock().now() - Duration(seconds=0.3)
     except TypeError as e:
         self.get_logger().error(e)
         t = rclpy.Time(seconds=0, nanoseconds=0)
     return self.tf_buffer.can_transform(self.base_footprint_frame,
                                         self.map_frame, t)
 def get_current_position_transform(self) -> TransformStamped:
     """
     Returns the current position as determined by the localization as a TransformStamped
     """
     try:
         # get the most recent transform
         transform = self.tf_buffer.lookup_transform(
             self.map_frame, self.base_footprint_frame,
             rclpy.Time(seconds=0, nanoseconds=0))
     except (tf2.LookupException, tf2.ConnectivityException,
             tf2.ExtrapolationException) as e:
         self.get_logger().warn(e)
         return None
     return transform
Beispiel #8
0
 def transform_goal_to_map(self, msg):
     # type: (PoseStamped) -> PoseStamped
     # transform local goal to goal in map frame
     if msg.header.frame_id == self.map_frame:
         return msg
     else:
         try:
             msg.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
             map_goal = self.tf_buffer.transform(
                 msg, self.map_frame, timeout=Duration(seconds=0.5))
             e = euler_from_quaternion(
                 (map_goal.pose.orientation.x, map_goal.pose.orientation.y,
                  map_goal.pose.orientation.z, map_goal.pose.orientation.w))
             q = quaternion_from_euler(0, 0, e[2])
             map_goal.pose.orientation.x = q[0]
             map_goal.pose.orientation.y = q[1]
             map_goal.pose.orientation.z = q[2]
             map_goal.pose.orientation.w = q[3]
             map_goal.pose.position.z = 0
             return map_goal
         except Exception as e:
             self.get_logger().warn(e)
             return
    def cost_at_relative_xy(self, x, y):
        """
        Returns cost at relative position to the base footprint.
        """
        if self.costmap is None:
            return 0.0

        point = PointStamped()
        point.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        point.header.frame_id = self.base_footprint_frame
        point.point.x = x
        point.point.y = y

        try:
            # Transform point of interest to the map
            point = self.tf_buffer.transform(point,
                                             self.map_frame,
                                             timeout=Duration(seconds=0.3))
        except (tf2.ConnectivityException, tf2.LookupException,
                tf2.ExtrapolationException) as e:
            self.get_logger().warn(e)
            return 0.0

        return self.get_cost_at_field_position(point.point.x, point.point.y)
    def __init__(self, blackboard, node: Node):
        self.node = node
        self._blackboard = blackboard
        self.body_config = self.node.get_parameter(
            "behavior/body").get_parameter_value().string_value
        # This pose is not supposed to be used as robot pose. Just as precision measurement for the TF position.
        self.pose = PoseWithCovarianceStamped()
        self.tf_buffer = tf2.Buffer(cache_time=Duration(seconds=30))
        self.tf_listener = tf2.TransformListener(self.tf_buffer)

        self.odom_frame = self.node.get_parameter(
            '~odom_frame').get_parameter_value().string_value
        self.map_frame = self.node.get_parameter(
            '~map_frame').get_parameter_value().string_value
        self.ball_frame = self.node.get_parameter(
            '~ball_frame').get_parameter_value().string_value
        self.base_footprint_frame = self.node.get_parameter(
            '~base_footprint_frame').get_parameter_value().string_value

        self.ball = PointStamped()  # The ball in the base footprint frame
        self.ball_odom = PointStamped(
        )  # The ball in the odom frame (when localization is not usable)
        self.ball_odom.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        self.ball_odom.header.frame_id = self.odom_frame
        self.ball_map = PointStamped(
        )  # The ball in the map frame (when localization is usable)
        self.ball_map.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        self.ball_map.header.frame_id = self.map_frame
        self.ball_teammate = PointStamped()
        self.ball_teammate.header.stamp = rclpy.Time(seconds=0, nanoseconds=0)
        self.ball_teammate.header.frame_id = self.map_frame
        self.ball_lost_time = Duration(seconds=self.node.get_parameter(
            'behavior/body/ball_lost_time').get_parameter_value().double_value)
        self.ball_twist_map = None
        self.ball_filtered = None
        self.ball_twist_lost_time = Duration(seconds=self.node.get_parameter(
            'behavior/body/ball_twist_lost_time').get_parameter_value().
                                             double_value)
        self.ball_twist_precision_threshold = self.node.get_parameter(
            'behavior/body/ball_twist_precision_threshold'
        ).get_parameter_value().double_value
        self.reset_ball_filter = self.node.create_client(
            Trigger, 'ball_filter_reset')

        self.goal = GoalRelative()  # The goal in the base footprint frame
        self.goal_odom = GoalRelative()
        self.goal_odom.header.stamp = self.get_clock().now()
        self.goal_odom.header.frame_id = self.odom_frame

        self.my_data = dict()
        self.counter = 0
        self.ball_seen_time = rclpy.Time(seconds=0, nanoseconds=0)
        self.ball_seen_time_teammate = rclpy.Time(seconds=0, nanoseconds=0)
        self.goal_seen_time = rclpy.Time(seconds=0, nanoseconds=0)
        self.ball_seen = False
        self.ball_seen_teammate = False
        self.field_length = self.node.get_parameter(
            'field_length').get_parameter_value().double_value
        self.field_width = self.node.get_parameter(
            'field_width').get_parameter_value().double_value
        self.goal_width = self.node.get_parameter(
            'goal_width').get_parameter_value().double_value
        self.map_margin = self.node.get_parameter(
            'behavior/body/map_margin').get_parameter_value().double_value
        self.obstacle_costmap_smoothing_sigma = self.node.get_parameter(
            '"behavior/body/obstacle_costmap_smoothing_sigma"'
        ).get_parameter_value().double_value
        self.obstacle_cost = self.node.get_parameter(
            'behavior/body/obstacle_cost').get_parameter_value().double_value

        self.use_localization = self.node.get_parameter(
            'behavior/body/use_localization').get_parameter_value(
            ).double_value

        self.pose_precision_threshold = self.node.get_parameter(
            'behavior/body/pose_precision_threshold').get_parameter_value(
            ).double_value

        # Publisher for visualization in RViZ
        self.ball_publisher = self.create_publisher(PointStamped,
                                                    'debug/viz_ball', 1)
        self.goal_publisher = self.create_publisher(PoseWithCertaintyArray,
                                                    'debug/viz_goal', 1)
        self.ball_twist_publisher = self.create_publisher(
            TwistStamped, 'debug/ball_twist', 1)
        self.used_ball_pub = self.create_publisher(PointStamped,
                                                   'debug/used_ball', 1)
        self.which_ball_pub = self.create_publisher(
            Header, 'debug/which_ball_is_used', 1)
        self.costmap_publisher = self.create_publisher(OccupancyGrid,
                                                       'debug/costmap', 1)

        self.base_costmap = None  # generated once in constructor field features
        self.costmap = None  # updated on the fly based on the base_costmap
        self.gradient_map = None  # global heading map (static) only dependent on field structure

        # Calculates the base costmap and gradient map based on it
        self.calc_base_costmap()
        self.calc_gradients()