Пример #1
0
    def _prediction_step(self, time_diff_s):
        """Runs the prediction step and returns the transition matrix."""
        # x = A * x + B
        heading_r = math.radians(self.estimated_heading())
        from control.telemetry import Telemetry
        x_delta, y_delta = Telemetry.rotate_radians_clockwise(
            (0.0, time_diff_s),
            heading_r
        )
        speed_m_s = self.estimated_speed()
        transition = numpy.matrix([  # A
            [1.0, 0.0, 0.0, x_delta],
            [0.0, 1.0, 0.0, y_delta],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0]
        ])

        # Update heading estimate based on steering
        new_heading = Telemetry.wrap_degrees(
            self.estimated_heading()
            + self._estimated_turn_rate_d_s * time_diff_s
        )
        self._estimates.itemset(2, new_heading)

        # TODO: Add acceleration values

        self._estimates = transition * self._estimates
        return transition
Пример #2
0
    def run(self):
        """Run in a thread, hands raw telemetry readings to telemetry
        instance.
        """
        from control.telemetry import Telemetry
        # Normally, you'd have a loop that periodically checks for new readings
        # or that blocks until readings are received
        while self._run:
            try:
                time.sleep(self._sleep_time_s)
            except Exception:  # pylint: disable=broad-except
                pass

            speed_m_s = 0.0
            if self._driver is not None:
                speed_m_s = self._driver.get_throttle() * self.MAX_SPEED_M_S
                point_m = (0.0, speed_m_s * self._sleep_time_s)
                offset_m = Telemetry.rotate_radians_clockwise(
                    point_m,
                    math.radians(self._heading_d)
                )
                self._x_m += offset_m[0]
                self._y_m += offset_m[1]

                self._heading_d += \
                    self._driver.get_turn() * self.TURN_D_S * self._sleep_time_s
                self._heading_d = Telemetry.wrap_degrees(self._heading_d)

            self._iterations += 1
            if self._iterations % 5 == 0:
                gps_d = Telemetry.wrap_degrees(
                    self._random.normalvariate(
                        self._heading_d,
                        self.SIGMA_GPS_D
                    )
                )
                self._telemetry.handle_message({
                    'x_m': self._random.normalvariate(self._x_m, self.SIGMA_M),
                    'y_m': self._random.normalvariate(self._y_m, self.SIGMA_M),
                    'x_accuracy_m': self.SIGMA_M,
                    'y_accuracy_m': self.SIGMA_M,
                    'speed_m_s': self._random.normalvariate(
                        speed_m_s,
                        self.SIGMA_M_S
                    ),
                    'gps_d': gps_d,
                    'accelerometer_m_s_s': (0.0, 0.0, 9.8),
                })
            else:
                compass_d = Telemetry.wrap_degrees(
                    self._random.normalvariate(
                        self._heading_d,
                        self.SIGMA_COMPASS_D
                    )
                )
                self._telemetry.handle_message({
                    'compass_d': compass_d,
                    'accelerometer_m_s_s': (0.0, 0.0, 9.8),
                })
Пример #3
0
    def test_estimate(self):
        """Tests that the estimating of the locations via both is sane."""
        start_coordinates_m = (100.0, 150.0)
        heading_d = 40.0
        location_filter = LocationFilter(start_coordinates_m[0],
                                         start_coordinates_m[1], heading_d)
        speed_m_s = 5.0
        # This would normally naturally get set by running the Kalman filter;
        # we'll just manually set it now
        location_filter._estimates[3].itemset(0, speed_m_s)

        self.assertEqual(location_filter.estimated_location(),
                         start_coordinates_m)

        tick_s = 0.5

        step_m = speed_m_s * tick_s
        step_x_m, step_y_m = Telemetry.rotate_radians_clockwise(
            (0.0, step_m), math.radians(heading_d))

        actual_x_m, actual_y_m = start_coordinates_m

        def check_estimates():
            self.assertLess(
                abs(location_filter.estimated_location()[0] - actual_x_m), 0.5)
            self.assertLess(
                abs(location_filter.estimated_location()[1] - actual_y_m), 0.5)

        for update in range(1, 21):
            actual_x_m += step_x_m
            actual_y_m += step_y_m

            # First update by compass
            measurements = numpy.matrix([0.0, 0.0, heading_d, 0.0]).transpose()
            location_filter._update(measurements,
                                    location_filter.COMPASS_OBSERVER_MATRIX,
                                    location_filter.COMPASS_MEASUREMENT_NOISE,
                                    tick_s)
            check_estimates()

            # Add some approximated GPS readings
            # We'll use very tight standard deviations to try to avoid random
            # test failures
            measurements = numpy.matrix([
                random.normalvariate(actual_x_m, 0.01),
                random.normalvariate(actual_y_m, 0.01),
                random.normalvariate(heading_d, 0.5),
                random.normalvariate(speed_m_s, speed_m_s * 0.1)
            ]).transpose()
            location_filter._update(
                measurements,
                location_filter.GPS_OBSERVER_MATRIX,
                location_filter.GPS_MEASUREMENT_NOISE,
                0.0  # Tick isn't used for GPS
            )
            check_estimates()
    def run(self):
        """Run in a thread, hands raw telemetry readings to telemetry
        instance.
        """
        from control.telemetry import Telemetry
        # Normally, you'd have a loop that periodically checks for new readings
        # or that blocks until readings are received
        while self._run:
            try:
                time.sleep(self._sleep_time_s)
            except Exception:  # pylint: disable=broad-except
                pass

            speed_m_s = 0.0
            if self._driver is not None:
                speed_m_s = self._driver.get_throttle() * self.MAX_SPEED_M_S
                point_m = (0.0, speed_m_s * self._sleep_time_s)
                offset_m = Telemetry.rotate_radians_clockwise(
                    point_m, math.radians(self._heading_d))
                self._x_m += offset_m[0]
                self._y_m += offset_m[1]

                self._heading_d += \
                    self._driver.get_turn() * self.TURN_D_S * self._sleep_time_s
                self._heading_d = Telemetry.wrap_degrees(self._heading_d)

            self._iterations += 1
            if self._iterations % 5 == 0:
                gps_d = Telemetry.wrap_degrees(
                    self._random.normalvariate(self._heading_d,
                                               self.SIGMA_GPS_D))
                self._telemetry.handle_message({
                    'x_m':
                    self._random.normalvariate(self._x_m, self.SIGMA_M),
                    'y_m':
                    self._random.normalvariate(self._y_m, self.SIGMA_M),
                    'x_accuracy_m':
                    self.SIGMA_M,
                    'y_accuracy_m':
                    self.SIGMA_M,
                    'speed_m_s':
                    self._random.normalvariate(speed_m_s, self.SIGMA_M_S),
                    'gps_d':
                    gps_d,
                    'accelerometer_m_s_s': (0.0, 0.0, 9.8),
                })
            else:
                compass_d = Telemetry.wrap_degrees(
                    self._random.normalvariate(self._heading_d,
                                               self.SIGMA_COMPASS_D))
                self._telemetry.handle_message({
                    'compass_d':
                    compass_d,
                    'accelerometer_m_s_s': (0.0, 0.0, 9.8),
                })
Пример #5
0
    def test_estimate_constant_speed(self):
        """Tests that the estimating of the locations via dead reckoning at a
        constant speed is sane.
        """
        # I'm not sure how to independently validate these tests for accuracy.
        # The best I can think of is to do some sanity tests.
        start_coordinates_m = (100.0, 200.0)
        heading_d = 32.0
        location_filter = LocationFilter(
            start_coordinates_m[0],
            start_coordinates_m[1],
            heading_d
        )

        self.assertEqual(
            location_filter.estimated_location(),
            start_coordinates_m
        )

        speed_m_s = 1.8
        # This would normally naturally get set by running the Kalman filter;
        # we'll just manually set it now
        location_filter._estimates[3].itemset(0, speed_m_s)

        self.assertEqual(
            location_filter.estimated_location(),
            start_coordinates_m
        )

        measurements = numpy.matrix(  # pylint: disable=no-member
            [0.0, 0.0, heading_d, 0.0]
        ).transpose()  # z

        seconds = 5
        for _ in range(seconds):
            location_filter._update(
                measurements,
                location_filter.COMPASS_OBSERVER_MATRIX,
                location_filter.COMPASS_MEASUREMENT_NOISE,
                time_diff_s=1.0
            )

        offset = Telemetry.rotate_radians_clockwise(
            (0.0, speed_m_s * seconds),
            math.radians(heading_d)
        )
        new_coordinates = [s + o for s, o in zip(start_coordinates_m, offset)]

        for estimated, expected in zip(
            location_filter.estimated_location(),
            new_coordinates
        ):
            self.assertAlmostEqual(estimated, expected, 2)
    def test_rotate_radians_clockwise(self):
        """Tests rotating a vector radians clockwise."""
        base = (1.0, 0.0)

        not_rotated = Telemetry.rotate_radians_clockwise(base, 0.0)
        self.assertAlmostEqual(not_rotated[0], base[0])
        self.assertAlmostEqual(not_rotated[1], base[1])

        ninety = Telemetry.rotate_radians_clockwise(base, math.radians(90.0))
        self.assertAlmostEqual(ninety[0], 0.0)
        self.assertAlmostEqual(ninety[1], -1.0)

        one_eighty = Telemetry.rotate_radians_clockwise(
            base, math.radians(180.0))
        self.assertAlmostEqual(one_eighty[0], -base[0])
        self.assertAlmostEqual(one_eighty[1], base[1])

        two_seventy = Telemetry.rotate_radians_clockwise(
            base, math.radians(270.0))
        self.assertAlmostEqual(two_seventy[0], 0.0)
        self.assertAlmostEqual(two_seventy[1], 1.0)

        three_sixty = Telemetry.rotate_radians_clockwise(
            base, math.radians(360.0))
        self.assertAlmostEqual(three_sixty[0], base[0])
        self.assertAlmostEqual(three_sixty[1], base[1])

        negative_ninety = Telemetry.rotate_radians_clockwise(
            base, math.radians(-90.0))
        self.assertAlmostEqual(negative_ninety[0], two_seventy[0])
        self.assertAlmostEqual(negative_ninety[1], two_seventy[1])
    def _update_position(self):
        """Updates the position using dead reckoning."""
        self.update_count -= 1

        diff_time_s = 1.0

        if self._throttle > 0.0:
            self._heading += self._turn * 30.0
            self._heading = Telemetry.wrap_degrees(self._heading)

            step_m = diff_time_s * self._throttle * self.MAX_SPEED_M_S
            point = (0, step_m)
            radians = math.radians(self._heading)
            point = Telemetry.rotate_radians_clockwise(point, radians)
            self._x_m += point[0]
            self._y_m += point[1]
Пример #8
0
    def test_estimate_constant_speed(self):
        """Tests that the estimating of the locations via dead reckoning at a
        constant speed is sane.
        """
        # I'm not sure how to independently validate these tests for accuracy.
        # The best I can think of is to do some sanity tests.
        start_coordinates_m = (100.0, 200.0)
        heading_d = 32.0
        location_filter = LocationFilter(start_coordinates_m[0],
                                         start_coordinates_m[1], heading_d)

        self.assertEqual(location_filter.estimated_location(),
                         start_coordinates_m)

        speed_m_s = 1.8
        # This would normally naturally get set by running the Kalman filter;
        # we'll just manually set it now
        location_filter._estimates[3].itemset(0, speed_m_s)

        self.assertEqual(location_filter.estimated_location(),
                         start_coordinates_m)

        measurements = numpy.matrix(  # pylint: disable=no-member
            [0.0, 0.0, heading_d, 0.0]).transpose()  # z

        seconds = 5
        for _ in range(seconds):
            location_filter._update(measurements,
                                    location_filter.COMPASS_OBSERVER_MATRIX,
                                    location_filter.COMPASS_MEASUREMENT_NOISE,
                                    time_diff_s=1.0)

        offset = Telemetry.rotate_radians_clockwise((0.0, speed_m_s * seconds),
                                                    math.radians(heading_d))
        new_coordinates = [s + o for s, o in zip(start_coordinates_m, offset)]

        for estimated, expected in zip(location_filter.estimated_location(),
                                       new_coordinates):
            self.assertAlmostEqual(estimated, expected, 2)
Пример #9
0
    def _prediction_step(self, time_diff_s):
        """Runs the prediction step and returns the transition matrix."""
        # x = A * x + B
        heading_r = math.radians(self.estimated_heading())
        from control.telemetry import Telemetry
        x_delta, y_delta = Telemetry.rotate_radians_clockwise(
            (0.0, time_diff_s), heading_r)
        speed_m_s = self.estimated_speed()
        transition = numpy.matrix([  # A
            [1.0, 0.0, 0.0, x_delta], [0.0, 1.0, 0.0, y_delta],
            [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]
        ])

        # Update heading estimate based on steering
        new_heading = Telemetry.wrap_degrees(self.estimated_heading() +
                                             self._estimated_turn_rate_d_s *
                                             time_diff_s)
        self._estimates.itemset(2, new_heading)

        # TODO: Add acceleration values

        self._estimates = transition * self._estimates
        return transition
Пример #10
0
    def test_rotate_radians_clockwise(self):
        """Tests rotating a vector radians clockwise."""
        base = (1.0, 0.0)

        not_rotated = Telemetry.rotate_radians_clockwise(base, 0.0)
        self.assertAlmostEqual(not_rotated[0], base[0])
        self.assertAlmostEqual(not_rotated[1], base[1])

        ninety = Telemetry.rotate_radians_clockwise(base, math.radians(90.0))
        self.assertAlmostEqual(ninety[0], 0.0)
        self.assertAlmostEqual(ninety[1], -1.0)

        one_eighty = Telemetry.rotate_radians_clockwise(
            base,
            math.radians(180.0)
        )
        self.assertAlmostEqual(one_eighty[0], -base[0])
        self.assertAlmostEqual(one_eighty[1], base[1])

        two_seventy = Telemetry.rotate_radians_clockwise(
            base,
            math.radians(270.0)
        )
        self.assertAlmostEqual(two_seventy[0], 0.0)
        self.assertAlmostEqual(two_seventy[1], 1.0)

        three_sixty = Telemetry.rotate_radians_clockwise(
            base,
            math.radians(360.0)
        )
        self.assertAlmostEqual(three_sixty[0], base[0])
        self.assertAlmostEqual(three_sixty[1], base[1])

        negative_ninety = Telemetry.rotate_radians_clockwise(
            base,
            math.radians(-90.0)
        )
        self.assertAlmostEqual(negative_ninety[0], two_seventy[0])
        self.assertAlmostEqual(negative_ninety[1], two_seventy[1])
Пример #11
0
    def test_estimate(self):
        """Tests that the estimating of the locations via both is sane."""
        start_coordinates_m = (100.0, 150.0)
        heading_d = 40.0
        location_filter = LocationFilter(
            start_coordinates_m[0],
            start_coordinates_m[1],
            heading_d
        )
        speed_m_s = 5.0
        # This would normally naturally get set by running the Kalman filter;
        # we'll just manually set it now
        location_filter._estimates[3].itemset(0, speed_m_s)

        self.assertEqual(
            location_filter.estimated_location(),
            start_coordinates_m
        )

        tick_s = 0.5

        step_m = speed_m_s * tick_s
        step_x_m, step_y_m = Telemetry.rotate_radians_clockwise(
            (0.0, step_m),
            math.radians(heading_d)
        )

        actual_x_m, actual_y_m = start_coordinates_m

        def check_estimates():
            self.assertLess(
                abs(location_filter.estimated_location()[0] - actual_x_m),
                0.5
            )
            self.assertLess(
                abs(location_filter.estimated_location()[1] - actual_y_m),
                0.5
            )

        for update in range(1, 21):
            actual_x_m += step_x_m
            actual_y_m += step_y_m

            # First update by compass
            measurements = numpy.matrix([0.0, 0.0, heading_d, 0.0]).transpose()
            location_filter._update(
                measurements,
                location_filter.COMPASS_OBSERVER_MATRIX,
                location_filter.COMPASS_MEASUREMENT_NOISE,
                tick_s
            )
            check_estimates()

            # Add some approximated GPS readings
            # We'll use very tight standard deviations to try to avoid random
            # test failures
            measurements = numpy.matrix([
                random.normalvariate(actual_x_m, 0.01),
                random.normalvariate(actual_y_m, 0.01),
                random.normalvariate(heading_d, 0.5),
                random.normalvariate(speed_m_s, speed_m_s * 0.1)
            ]).transpose()
            location_filter._update(
                measurements,
                location_filter.GPS_OBSERVER_MATRIX,
                location_filter.GPS_MEASUREMENT_NOISE,
                0.0  # Tick isn't used for GPS
            )
            check_estimates()