def test_CalculateRMSE_Returns0_IfEstimationsSizeIs0(self):
        estimations = []
        ground_truth = []
        expected_rmse = Matrix([[]])
        expected_rmse.zero(4, 1)

        rmse = self._tools.calculate_rmse(estimations, ground_truth)

        self.assertEqual(rmse.value, expected_rmse.value,
                         "RMSE should be 0 when estimation size is 0.")
    def test_PredictRadarMeasurement_CalculatesZsigZPredAndS_FromXsigPred(
            self):
        n_aug = 7
        n_z = 3

        #radar measurement noise standard deviation radius in m
        self._ukf._std_radr = 0.3

        #radar measurement noise standard deviation angle in rad
        self._ukf._std_radphi = 0.0175

        #radar measurement noise standard deviation radius change in m/s
        self._ukf._std_radrd = 0.1

        #create example matrix with predicted sigma points
        self._ukf._Xsig_pred = Matrix(
            [[
                5.9374, 6.0640, 5.925, 5.9436, 5.9266, 5.9374, 5.9389, 5.9374,
                5.8106, 5.9457, 5.9310, 5.9465, 5.9374, 5.9359, 5.93744
            ],
             [
                 1.48, 1.4436, 1.660, 1.4934, 1.5036, 1.48, 1.4868, 1.48,
                 1.5271, 1.3104, 1.4787, 1.4674, 1.48, 1.4851, 1.486
             ],
             [
                 2.204, 2.2841, 2.2455, 2.2958, 2.204, 2.204, 2.2395, 2.204,
                 2.1256, 2.1642, 2.1139, 2.204, 2.204, 2.1702, 2.2049
             ],
             [
                 0.5367, 0.47338, 0.67809,
                 0.55455, 0.64364, 0.54337, 0.5367, 0.53851, 0.60017, 0.39546,
                 0.51900, 0.42991, 0.530188, 0.5367, 0.535048
             ],
             [
                 0.352, 0.29997, 0.46212, 0.37633, 0.4841, 0.41872, 0.352,
                 0.38744, 0.40562, 0.24347, 0.32926, 0.2214, 0.28687, 0.352,
                 0.318159
             ]])

        Zsig, z_pred, S = self._ukf.predict_radar_measurement()

        expected_z_pred = Matrix([[6.12155], [0.245993], [2.10313]])

        assert_array_almost_equal(z_pred.value,
                                  expected_z_pred.value,
                                  decimal=4)

        #create example matrix for predicted measurement covariance
        expected_S = Matrix([[0.0946171, -0.000139448, 0.00407016],
                             [-0.000139448, 0.000617548, -0.000770652],
                             [0.00407016, -0.000770652, 0.0180917]])

        assert_array_almost_equal(S.value, expected_S.value)
 def __init__(self,
              timestamp=0,
              sensor_type=SensorType.LASER,
              raw_measurements=Matrix([[]])):
     self._timestamp = timestamp
     self._sensor_type = sensor_type
     self._raw_measurements = raw_measurements
    def test_ProcessMeasurement_SetsXAndP_IfFirstMeasurementIsLASER(self):
        meas_package = MeasurementPackage(0,
                                          MeasurementPackage.SensorType.LASER,
                                          Matrix([[1], [1]]))

        self._ukf.process_measurement(meas_package)

        initial_x = Matrix([[1], [1], [0], [0], [0]])

        self.assertEqual(self._ukf._x.value, initial_x.value)

        initial_P = Matrix(
            [[self._ukf._std_laspx * self._ukf._std_laspx, 0, 0, 0, 0],
             [0, self._ukf._std_laspy * self._ukf._std_laspy, 0, 0, 0],
             [0, 0, 9, 0, 0], [0, 0, 0, 2.25, 0], [0, 0, 0, 0, 0.0625]])

        self.assertEqual(self._ukf._P.value, initial_P.value)
    def test_AugmentedSigmaPoints_CalculatesXsigAug_FromXAndP(self):
        n_aug = 7
        self._ukf._std_a = 0.2
        self._ukf._std_yawdd = 0.2

        self._ukf._x = Matrix([[5.7441], [1.3800], [2.2049], [0.5015],
                               [0.3528]])

        self._ukf._P = Matrix([[0.0043, -0.0013, 0.0030, -0.0022, -0.0020],
                               [-0.0013, 0.0077, 0.0011, 0.0071, 0.0060],
                               [0.0030, 0.0011, 0.0054, 0.0007, 0.0008],
                               [-0.0022, 0.0071, 0.0007, 0.0098, 0.0100],
                               [-0.0020, 0.0060, 0.0008, 0.0100, 0.0123]])

        Xsig_aug = self._ukf.augmented_sigma_points()

        expected_Xsig_aug = Matrix(
            [[
                5.7441, 5.85768, 5.7441, 5.7441, 5.7441, 5.7441, 5.7441,
                5.7441, 5.63052, 5.7441, 5.7441, 5.7441, 5.7441, 5.7441, 5.7441
            ],
             [
                 1.38, 1.34566, 1.52806, 1.38, 1.38, 1.38, 1.38, 1.38, 1.41434,
                 1.23194, 1.38, 1.38, 1.38, 1.38, 1.38
             ],
             [
                 2.2049, 2.28414, 2.24557, 2.29582, 2.2049, 2.2049, 2.2049,
                 2.2049, 2.12566, 2.16423, 2.11398, 2.2049, 2.2049, 2.2049,
                 2.2049
             ],
             [
                 0.5015, 0.44339, 0.631886, 0.516923, 0.595227, 0.5015, 0.5015,
                 0.5015, 0.55961, 0.371114, 0.486077, 0.407773, 0.5015, 0.5015,
                 0.5015
             ],
             [
                 0.3528, 0.299973, 0.462123, 0.376339, 0.48417, 0.418721,
                 0.3528, 0.3528, 0.405627, 0.243477, 0.329261, 0.22143,
                 0.286879, 0.3528, 0.3528
             ], [0, 0, 0, 0, 0, 0, 0.34641, 0, 0, 0, 0, 0, 0, -0.34641, 0],
             [0, 0, 0, 0, 0, 0, 0, 0.34641, 0, 0, 0, 0, 0, 0, -0.34641]])

        assert_array_almost_equal(Xsig_aug.value,
                                  expected_Xsig_aug.value,
                                  decimal=4)
def parse_measurement(line):
    row = line.split()
    meas_package = MeasurementPackage()

    i = 0
    # reads first element from the current line
    sensor_type = row[i]
    i += 1

    if(sensor_type == "L"):
        # LASER MEASUREMENT

        # read measurements at this timestamp
        meas_package._sensor_type = MeasurementPackage.SensorType.LASER

        px = float(row[i])
        py = float(row[i+1])
        meas_package._raw_measurements = Matrix([[px], [py]])
        timestamp = int(row[i+2])
        meas_package._timestamp = timestamp
        i += 3
    elif (sensor_type == "R"):
        # RADAR MEASUREMENT

        # read measurements at this timestamp
        meas_package._sensor_type = MeasurementPackage.SensorType.RADAR

        ro = float(row[i])
        theta = float(row[i+1])
        ro_dot = float(row[i+2])
        meas_package._raw_measurements = Matrix([[ro], [theta], [ro_dot]])
        timestamp = int(row[i+3])
        meas_package._timestamp = timestamp
        i += 4

    # read ground truth data to compare later
    x_gt = float(row[i])
    y_gt = float(row[i+1])
    vx_gt = float(row[i+2])
    vy_gt = float(row[i+3])

    gt_values =  Matrix([[x_gt], [y_gt], [vx_gt], [vy_gt]])

    return (meas_package, gt_values)
    def test_PredictMeanAndCovariance_CalculatesXAndP_FromXsigPred(self):
        # create example matrix with predicted sigma points
        self._ukf._Xsig_pred = Matrix(
            [[
                5.9374, 6.0640, 5.925, 5.9436, 5.9266, 5.9374, 5.9389, 5.9374,
                5.8106, 5.9457, 5.9310, 5.9465, 5.9374, 5.9359, 5.93744
            ],
             [
                 1.48, 1.4436, 1.660, 1.4934, 1.5036, 1.48, 1.4868, 1.48,
                 1.5271, 1.3104, 1.4787, 1.4674, 1.48, 1.4851, 1.486
             ],
             [
                 2.204, 2.2841, 2.2455, 2.2958, 2.204, 2.204, 2.2395, 2.204,
                 2.1256, 2.1642, 2.1139, 2.204, 2.204, 2.1702, 2.2049
             ],
             [
                 0.5367, 0.47338, 0.67809,
                 0.55455, 0.64364, 0.54337, 0.5367, 0.53851, 0.60017, 0.39546,
                 0.51900, 0.42991, 0.530188, 0.5367, 0.535048
             ],
             [
                 0.352, 0.29997, 0.46212, 0.37633, 0.4841, 0.41872, 0.352,
                 0.38744, 0.40562, 0.24347, 0.32926, 0.2214, 0.28687, 0.352,
                 0.318159
             ]])

        x, P = self._ukf.predict_mean_and_covariance()

        expected_x = Matrix([[5.93637], [1.49035], [2.20528], [0.536853],
                             [0.353577]])

        assert_array_almost_equal(x.value, expected_x.value, decimal=5)

        expected_P = Matrix(
            [[0.00543425, -0.0024053, 0.00341576, -0.00348196, -0.00299378],
             [-0.0024053, 0.010845, 0.0014923, 0.00980182, 0.00791091],
             [0.00341576, 0.0014923, 0.00580129, 0.000778632, 0.000792973],
             [-0.00348196, 0.00980182, 0.000778632, 0.0119238, 0.0112491],
             [-0.00299378, 0.00791091, 0.000792973, 0.0112491, 0.0126972]])

        assert_array_almost_equal(P.value, expected_P.value)
    def test_ProcessMeasurement_SetsXAndP_IfFirstMeasurementIsRADAR(self):
        ro = 1.0
        theta = 2.0

        meas_package = MeasurementPackage(0,
                                          MeasurementPackage.SensorType.RADAR,
                                          Matrix([[ro], [theta], [0.5]]))

        self._ukf.process_measurement(meas_package)

        initial_x = Matrix([[ro * cos(theta)], [ro * sin(theta)], [0], [0],
                            [0]])

        self.assertEqual(self._ukf._x.value, initial_x.value)

        std_rad = max(self._ukf._std_radr, self._ukf._std_radphi * ro) * 2
        initial_P = Matrix([[std_rad * std_rad, 0, 0, 0, 0],
                            [0, std_rad * std_rad, 0, 0, 0], [0, 0, 9, 0, 0],
                            [0, 0, 0, 2.25, 0], [0, 0, 0, 0, 0.0625]])

        self.assertEqual(self._ukf._P.value, initial_P.value)
    def calculate_rmse(self, estimations, ground_truth):
        """A helper method to calculate RMSE."""
        rmse = Matrix([[]])
        rmse.zero(4, 1)

        # check the validity of the following inputs:
        # * the estimation vector size should not be zero
        # * the estimation vector size should equal ground truth vector size
        if len(estimations) != len(ground_truth) or not estimations:
            print("Invalid estimation or ground_truth data")
            return rmse

        #accumulate squared residuals
        for i in range(len(estimations)):
            residual = estimations[i] - ground_truth[i]

            #coefficient-wise multiplication
            residual = residual.cwise_product(residual)
            rmse += residual

        #calculate the mean
        rmse.value = [[i[0] / len(estimations)] for i in rmse.value]

        #calculate the squared root
        rmse.value = [[sqrt(i[0])] for i in rmse.value]

        #return the result
        return rmse
Example #10
0
def telemetry(sid, data):
    if data:
        j = data

        sensor_measurment = j["sensor_measurement"]

        tokens = sensor_measurment.split()

        i = 0
        # reads first element from the current line
        sensor_type = tokens[i]
        meas_package, gt_values =  parse_measurement(tokens)
        ground_truth.append(gt_values)

        #Call ProcessMeasurment(meas_package) for Kalman filter
        ukf.process_measurement(meas_package)

        #Push the current estimated x,y positon from the Kalman filter's state vector

        p_x = ukf._x.value[0][0]
        p_y = ukf._x.value[1][0]
        v  = ukf._x.value[2][0]
        yaw = ukf._x.value[3][0]

        v1 = cos(yaw)*v
        v2 = sin(yaw)*v

        estimate = Matrix([[p_x], [p_y], [v1], [v2]])

        estimations.append(estimate)
        if(sensor_type == "L") and ukf._use_laser:
            lidar_nis.append(ukf._lidar_nis)
        elif(sensor_type == "R") and ukf._use_radar:
            radar_nis.append(ukf._radar_nis)

        rmse = tools.calculate_rmse(estimations, ground_truth)

        msgJson = {}
        msgJson["estimate_x"] = p_x
        msgJson["estimate_y"] = p_y
        msgJson["rmse_x"] =  rmse.value[0][0]
        msgJson["rmse_y"] =  rmse.value[1][0]
        msgJson["rmse_vx"] = rmse.value[2][0]
        msgJson["rmse_vy"] = rmse.value[3][0]
        #msg = "42[\"estimate_marker\"," + msgJson.dump() + "]"
        # print(msg)
        sio.emit('estimate_marker', data=msgJson, skip_sid=True)
    else:
        # NOTE: DON'T EDIT THIS.
        sio.emit('manual', data={}, skip_sid=True)
    def test_CalculateRMSE_Returns0_IfEstimationsSizeDiffer(self):
        estimations = [Matrix([[1], [1], [0.2], [0.1]])]
        ground_truth = [
            Matrix([[1.1], [1.1], [0.3], [0.2]]),
            Matrix([[2.1], [2.1], [0.4], [0.3]])
        ]
        expected_rmse = Matrix([[]])
        expected_rmse.zero(4, 1)

        rmse = self._tools.calculate_rmse(estimations, ground_truth)

        self.assertEqual(rmse.value, expected_rmse.value,
                         "RMSE should be 0 when estimation size differs.")
    def test_ProcessMeasurement_CallsPredictThenUpdateRadar_ForSubsequentRADARMeasurements(
            self):
        first_measurement = MeasurementPackage(
            1477010443050000, MeasurementPackage.SensorType.RADAR,
            Matrix([[0.898658], [0.617674], [1.7986]]))

        second_measurement = MeasurementPackage(
            1477010443150000, MeasurementPackage.SensorType.RADAR,
            Matrix([[0.910574], [0.610537], [1.46233]]))

        self._ukf.prediction = MagicMock(wraps=self._ukf.prediction)
        self._ukf.update_radar = MagicMock(wraps=self._ukf.update_radar)

        ukf_sequence = Mock()
        ukf_sequence.attach_mock(self._ukf.prediction, 'prediction')
        ukf_sequence.attach_mock(self._ukf.update_radar, 'update_radar')

        self._ukf.process_measurement(first_measurement)
        self._ukf.process_measurement(second_measurement)

        assert ukf_sequence.mock_calls == [
            call.prediction(0.1),
            call.update_radar(second_measurement)
        ]
    def test_ProcessMeasurement_CallsPredictThenUpdateLidar_ForSubsequentLASERMeasurements(
            self):
        first_measurement = MeasurementPackage(
            1477010443000000, MeasurementPackage.SensorType.LASER,
            Matrix([[0.463227], [0.607415]]))

        second_measurement = MeasurementPackage(
            1477010443100000, MeasurementPackage.SensorType.LASER,
            Matrix([[0.968521], [0.40545]]))

        self._ukf.prediction = MagicMock(wraps=self._ukf.prediction)
        self._ukf.update_lidar = MagicMock(wraps=self._ukf.update_lidar)

        ukf_sequence = Mock()
        ukf_sequence.attach_mock(self._ukf.prediction, 'prediction')
        ukf_sequence.attach_mock(self._ukf.update_lidar, 'update_lidar')

        self._ukf.process_measurement(first_measurement)
        self._ukf.process_measurement(second_measurement)

        assert ukf_sequence.mock_calls == [
            call.prediction(0.1),
            call.update_lidar(second_measurement)
        ]
    def update_lidar(self, meas_package):
        """Updates the state and the state covariance matrix using a laser measurement
        
        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Use lidar data to update the belief
        about the object's position. Modify the state vector, self._x, and
        covariance, self._P.
        You can also calculate the lidar NIS, if desired.
        """
        """
        Update Lidar Measurement
        """

        # The mapping from state space to Lidar is linear. Fill this out with
        # appropriate update steps

        z_pred = self._H * self._x
        y = meas_package._raw_measurements - z_pred
        #Ht = H.transpose()
        S = self._H * self._P * self._Ht + self._R
        Si = S.inverse()
        PHt = self._P * self._Ht
        K = PHt * Si

        # new estimate
        self._x = self._x + (K * y)
        x_size = self._x.dimx
        I = Matrix([[]])
        I.identity(x_size)
        self._P = (I - K * self._H) * self._P

        nis = y.transpose() * Si * y
        self._lidar_nis = nis.value[0][0]
Example #15
0
def run(ukf, p):
    ukf._std_a = p[0]
    ukf._std_yawdd = p[1]
    in_file_name_ = "../data/obj_pose-laser-radar-synthetic-input.txt"

    in_file = open(in_file_name_, newline='')
    data_reader = csv.reader(in_file, delimiter='\t', quotechar='|')


    tools = Tools()

    # used to compute the RMSE later
    estimations = []
    ground_truth =[]

    # prep the measurement packages (each line represents a measurement at a
    # timestamp)
    for row in data_reader:
        meas_package, gt_values =  parse_measurement(row)
        ground_truth.append(gt_values)

        ukf.process_measurement(meas_package)
        p_x = ukf._x.value[0][0]
        p_y = ukf._x.value[1][0]
        v   = ukf._x.value[2][0]
        yaw = ukf._x.value[3][0]

        v1 = cos(yaw)*v
        v2 = sin(yaw)*v

        estimate = Matrix([[p_x], [p_y], [v1], [v2]])

        estimations.append(estimate)

    # compute the accuracy (RMSE)
    rmse = tools.calculate_rmse(estimations, ground_truth)

    in_file.close()

    err = 0
    for i in rmse.value:
        err += i[0]*i[0]
    err = sqrt(err)
    return err
    def test_CalculateRMSE_ReturnsCorrectRMSE_ForTestEstimations(self):
        for estimations, ground_truth, expected_rmse in [([
                Matrix([[1], [1], [0.2], [0.1]]),
                Matrix([[2], [2], [0.3], [0.2]]),
                Matrix([[3], [3], [0.4], [0.3]])
        ], [
                Matrix([[1.1], [1.1], [0.3], [0.2]]),
                Matrix([[2.1], [2.1], [0.4], [0.3]]),
                Matrix([[3.1], [3.1], [0.5], [0.4]])
        ], Matrix([[0.1], [0.1], [0.1], [0.1]]))]:
            with self.subTest():
                rmse = self._tools.calculate_rmse(estimations, ground_truth)

                assert_array_almost_equal(
                    rmse.value,
                    expected_rmse.value,
                    err_msg="RMSE should be correctly calculated.")
    def predict_mean_and_covariance(self):
        #Lesson 7, section 24: Predicted Mean and Covariance Assignment 2

        # create vector for predicted state
        x = Matrix([[]])

        # create covariance matrix for prediction
        P = Matrix([[]])

        # predicted state mean
        x.zero(self._n_x, 1)
        for i in range(2 * self._n_aug + 1):  # iterate over sigma points
            for row in range(x.dimx):
                x.value[row] = [
                    x.value[row][0] +
                    self._weights.value[i][0] * self._Xsig_pred.value[row][i]
                ]

        # predicted state covariance matrix
        P.zero(self._n_x, self._n_x)
        for i in range(2 * self._n_aug + 1):  # iterate over sigma points
            # state difference
            x_diff = Matrix([[]])
            x_diff.zero(self._n_x, 1)
            for row in range(self._n_x):
                x_diff.value[row][0] = self._Xsig_pred.value[row][i]
            x_diff = x_diff - x
            # angle normalization
            x_diff.value[3][0] = fmod(x_diff.value[3][0], pi)

            x_diff2 = x_diff * x_diff.transpose()
            x_diff2.value = [[xv * self._weights.value[i][0] for xv in row]
                             for row in x_diff2.value]
            P = P + x_diff2

        return (x, P)
    def test_UKF_PassesProjectRubric_ForDataSet1(self):
        in_file_name_ = "../data/obj_pose-laser-radar-synthetic-input.txt"

        in_file = open(in_file_name_, newline='')
        data_reader = csv.reader(in_file, delimiter='\t', quotechar='|')

        self.assertFalse(in_file.closed)

        tools = Tools()

        # used to compute the RMSE later
        estimations = []
        ground_truth = []

        # prep the measurement packages (each line represents a measurement at a
        # timestamp)
        for row in data_reader:
            meas_package = MeasurementPackage()

            i = 0
            # reads first element from the current line
            sensor_type = row[i]
            i += 1

            if (sensor_type == "L"):
                # LASER MEASUREMENT

                # read measurements at this timestamp
                meas_package._sensor_type = MeasurementPackage.SensorType.LASER

                px = float(row[i])
                py = float(row[i + 1])
                meas_package._raw_measurements = Matrix([[px], [py]])
                timestamp = int(row[i + 2])
                meas_package._timestamp = timestamp
                i += 3
            elif (sensor_type == "R"):
                # RADAR MEASUREMENT

                # read measurements at this timestamp
                meas_package._sensor_type = MeasurementPackage.SensorType.RADAR

                ro = float(row[i])
                theta = float(row[i + 1])
                ro_dot = float(row[i + 2])
                meas_package._raw_measurements = Matrix([[ro], [theta],
                                                         [ro_dot]])
                timestamp = int(row[i + 3])
                meas_package._timestamp = timestamp
                i += 4

            # read ground truth data to compare later
            x_gt = float(row[i])
            y_gt = float(row[i + 1])
            vx_gt = float(row[i + 2])
            vy_gt = float(row[i + 3])

            gt_values = Matrix([[x_gt], [y_gt], [vx_gt], [vy_gt]])
            ground_truth.append(gt_values)

            self._ukf.process_measurement(meas_package)
            p_x = self._ukf._x.value[0][0]
            p_y = self._ukf._x.value[1][0]
            v = self._ukf._x.value[2][0]
            yaw = self._ukf._x.value[3][0]

            v1 = cos(yaw) * v
            v2 = sin(yaw) * v

            estimate = Matrix([[p_x], [p_y], [v1], [v2]])

            estimations.append(estimate)

        # compute the accuracy (RMSE)
        expected_rmse = Matrix([[0.09], [0.10], [0.40], [0.30]])
        rmse = tools.calculate_rmse(estimations, ground_truth)
        assert_array_less(rmse.value, expected_rmse.value)

        in_file.close()
class UKF:
    def __init__(self):
        """Initializes Unscented Kalman filter"""
        # initially set to false, set to true in first call of ProcessMeasurement
        self._is_initialized = False

        # predicted sigma points matrix
        self._Xsig_pred = Matrix([[]])

        # time when the state is true, in us
        self._time_us = 0

        # Weights of sigma points
        self._weights = Matrix([[]])

        # State dimension
        self._n_x = 5

        # Augmented state dimension
        self._n_aug = 7

        # Sigma point spreading parameter
        self._lambda = 3 - self._n_aug

        self._lambda_sqrt = sqrt(self._lambda + self._n_aug)

        #if this is false, laser measurements will be ignored (except during init)
        self._use_laser = True

        #if this is false, radar measurements will be ignored (except during init)
        self._use_radar = True

        # initial state vector
        # state vector: [pos1 pos2 vel_abs yaw_angle yaw_rate] in SI units and rad
        self._x = Matrix([[]])
        self._x.zero(5, 1)

        # initial covariance matrix
        self._P = Matrix([[]])
        self._P.zero(5, 5)

        # Process noise standard deviation longitudinal acceleration in m/s^2
        self._std_a = 0.58

        # Process noise standard deviation yaw acceleration in rad/s^2
        self._std_yawdd = 0.57
        """
        DO NOT MODIFY measurement noise values below.
        These are provided by the sensor manufacturer.
        """

        # Laser measurement noise standard deviation position1 in m
        self._std_laspx = 0.15

        # Laser measurement noise standard deviation position2 in m
        self._std_laspy = 0.15

        # Radar measurement noise standard deviation radius in m
        self._std_radr = 0.3

        # Radar measurement noise standard deviation angle in rad
        self._std_radphi = 0.03

        # Radar measurement noise standard deviation radius change in m/s
        self._std_radrd = 0.3
        """
        End DO NOT MODIFY section for measurement noise values 
        """

        #set vector for weights
        self._weights.zero(2 * self._n_aug + 1, 1)

        # set weights
        weight_0 = self._lambda / (self._lambda + self._n_aug)
        weight = 0.5 / (self._n_aug + self._lambda)
        self._weights.value[0] = [weight_0]

        for i in range(1, 2 * self._n_aug + 1):
            self._weights.value[i] = [weight]

        #create matrix with predicted sigma points as columns
        self._Xsig_pred.zero(self._n_x, 2 * self._n_aug + 1)

        # Lidar measurement covariance
        self._R = Matrix([[self._std_laspx * self._std_laspx, 0],
                          [0, self._std_laspy * self._std_laspy]])

        # Lidar measurement matrix
        self._H = Matrix([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0]])

        self._Ht = self._H.transpose()

        self._lidar_nis = 0
        self._radar_nis = 0

    def process_measurement(self, meas_package):
        """ProcessMeasurement

        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Initialization structure similar to EKF project
        """

        if not self._is_initialized:
            #Initialize x_, P_, previous_time, anything else needed.

            if meas_package._sensor_type == MeasurementPackage.SensorType.LASER:
                #Initialize here
                self._x.zero(self._n_x, 1)
                self._x.value[0:2] = meas_package._raw_measurements.value

                self._P = Matrix([
                    [self._std_laspx * self._std_laspx, 0, 0, 0, 0],
                    [0, self._std_laspy * self._std_laspy, 0, 0, 0],
                    [0, 0, 9, 0, 0],  #Assumming 95% of time speed is +/- 6m/s
                    [0, 0, 0, 2.25,
                     0],  #Assuming 95% of the time angle is +/- 3rad
                    [0, 0, 0, 0, 0.0625]
                ])  #Assuming 95% of the time angular speed +/- 0.5 rad/s

            elif meas_package._sensor_type == MeasurementPackage.SensorType.RADAR:
                #Initialize here
                #Convert radar from polar to cartesian coordinates
                #         and initialize state.
                ro = meas_package._raw_measurements.value[0][0]
                theta = meas_package._raw_measurements.value[1][0]
                self._x.zero(self._n_x, 1)
                self._x.value[0:2] = [[ro * cos(theta)], [ro * sin(theta)]]

                #max(standard deviation radius, standard deviation angle*radar distance)
                #a crude estimation of maximum standard deviation in cartesian coordinates
                std_rad = max(self._std_radr, self._std_radphi * ro) * 2
                self._P = Matrix([
                    [std_rad * std_rad, 0, 0, 0, 0],
                    [0, std_rad * std_rad, 0, 0, 0],
                    [0, 0, 9, 0, 0],  #Assumming 95% of time speed is +/- 6m/s
                    [0, 0, 0, 2.25,
                     0],  #Assuming 95% of the time angle is +/- 3rad
                    [0, 0, 0, 0, 0.0625]
                ])  #Assuming 95% of the time angular speed +/- 0.5 rad/s

            #Initialize anything else here
            self._time_us = meas_package._timestamp
            self._is_initialized = True
            return
        """
        Control structure similar to EKF project
        """

        delta_t = (meas_package._timestamp - self._time_us) / 1000000.0
        self.prediction(delta_t)

        if ((meas_package._sensor_type == MeasurementPackage.SensorType.LASER)
                and self._use_laser):
            self.update_lidar(meas_package)

        elif (
            (meas_package._sensor_type == MeasurementPackage.SensorType.RADAR)
                and self._use_radar):
            self.update_radar(meas_package)

        self._time_us = meas_package._timestamp

    def prediction(self, delta_t):
        """Predicts sigma points, the state, and the state covariance matrix
        
        Args:
            delta_t (float): Time between k and k+1 in s
        """
        """
        * Estimate the object's location.
        * Modify the state vector, x_. Predict sigma points, the state,
          and the state covariance matrix.
        """
        """
        Create Augmented Sigma Points
        """

        #create sigma point matrix
        Xsig_aug = self.augmented_sigma_points()
        """
        Predict Sigma Points
        """

        #Lesson 7, section 21: Sigma Point Prediction Assignment 2

        # may be it's very little gain. but to avoid couple of arithmatic operations
        delta_t_2 = delta_t * delta_t

        # predict sigma points
        for i in range(2 * self._n_aug + 1):
            #extract values for better readability
            p_x = Xsig_aug.value[0][i]
            p_y = Xsig_aug.value[1][i]
            v = Xsig_aug.value[2][i]
            yaw = Xsig_aug.value[3][i]
            yawd = Xsig_aug.value[4][i]
            nu_a = Xsig_aug.value[5][i]
            nu_yawdd = Xsig_aug.value[6][i]

            # avoid division by zero
            if (abs(yawd) > 0.001):
                px_p = p_x + v / yawd * (sin(yaw + yawd * delta_t) - sin(yaw))
                py_p = p_y + v / yawd * (cos(yaw) - cos(yaw + yawd * delta_t))
            else:
                px_p = p_x + v * delta_t * cos(yaw)
                py_p = p_y + v * delta_t * sin(yaw)

            v_p = v
            yaw_p = yaw + yawd * delta_t
            yawd_p = yawd

            # add noise
            px_p = px_p + 0.5 * nu_a * delta_t_2 * cos(yaw)
            py_p = py_p + 0.5 * nu_a * delta_t_2 * sin(yaw)
            v_p = v_p + nu_a * delta_t

            yaw_p = yaw_p + 0.5 * nu_yawdd * delta_t_2
            yawd_p = yawd_p + nu_yawdd * delta_t

            # write predicted sigma point into right column
            self._Xsig_pred.value[0][i] = px_p
            self._Xsig_pred.value[1][i] = py_p
            self._Xsig_pred.value[2][i] = v_p
            self._Xsig_pred.value[3][i] = yaw_p
            self._Xsig_pred.value[4][i] = yawd_p
        """
        Predict mean and covariance
        """

        self._x, self._P = self.predict_mean_and_covariance()

    def update_lidar(self, meas_package):
        """Updates the state and the state covariance matrix using a laser measurement
        
        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Use lidar data to update the belief
        about the object's position. Modify the state vector, self._x, and
        covariance, self._P.
        You can also calculate the lidar NIS, if desired.
        """
        """
        Update Lidar Measurement
        """

        # The mapping from state space to Lidar is linear. Fill this out with
        # appropriate update steps

        z_pred = self._H * self._x
        y = meas_package._raw_measurements - z_pred
        #Ht = H.transpose()
        S = self._H * self._P * self._Ht + self._R
        Si = S.inverse()
        PHt = self._P * self._Ht
        K = PHt * Si

        # new estimate
        self._x = self._x + (K * y)
        x_size = self._x.dimx
        I = Matrix([[]])
        I.identity(x_size)
        self._P = (I - K * self._H) * self._P

        nis = y.transpose() * Si * y
        self._lidar_nis = nis.value[0][0]

    def update_radar(self, meas_package):
        """Updates the state and the state covariance matrix using a radar measurement
        
        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Use radar data to update the belief
        about the object's position. Modify the state vector, self._x, and
        covariance, self._P.
        You can also calculate the radar NIS, if desired.
        """
        """
        Predict Radar Sigma Points
        """

        #set measurement dimension, radar can measure r, phi, and r_dot
        n_z = 3

        Zsig, z_pred, S = self.predict_radar_measurement()
        """
        Update Radar
        """

        #Lesson 7, section 30: UKF Update Assignment 2

        # create matrix for cross correlation Tc
        Tc = Matrix([[]])

        # calculate cross correlation matrix
        Tc.zero(self._n_x, n_z)
        for i in range(2 * self._n_aug + 1):  #2n+1 simga points
            # residual
            z_diff = Matrix([[]])
            z_diff.zero(n_z, 1)
            for row in range(n_z):
                z_diff.value[row][0] = Zsig.value[row][i]
            z_diff = z_diff - z_pred
            # angle normalization
            z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

            # state difference
            x_diff = Matrix([[]])
            x_diff.zero(self._n_x, 1)
            for row in range(self._n_x):
                x_diff.value[row][0] = self._Xsig_pred.value[row][i]
            x_diff = x_diff - self._x
            # angle normalization
            x_diff.value[3][0] = fmod(x_diff.value[3][0], pi)

            x_diff_z_diff_t = x_diff * z_diff.transpose()
            x_diff_z_diff_t.value = [[
                xzv * self._weights.value[i][0] for xzv in row
            ] for row in x_diff_z_diff_t.value]
            Tc = Tc + x_diff_z_diff_t

        # Kalman gain K;
        K = Tc * S.inverse()

        # residual
        z_diff = meas_package._raw_measurements - z_pred

        # angle normalization
        z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

        #update state mean and covariance matrix
        self._x = self._x + K * z_diff
        self._P = self._P - K * S * K.transpose()

        nis = z_diff.transpose() * S.inverse() * z_diff
        self._radar_nis = nis.value[0][0]

    """
    Student assignment functions
    """

    def augmented_sigma_points(self):
        #Lesson 7, section 18: Augmentation Assignment 2

        #create augmented mean vector
        x_aug = Matrix([[]])
        x_aug.zero(self._n_aug, 1)

        # create augmented state covariance
        P_aug = Matrix([[]])
        P_aug.zero(self._n_aug, self._n_aug)

        # create sigma point matrix
        Xsig_aug = Matrix([[]])
        Xsig_aug.zero(self._n_aug, 2 * self._n_aug + 1)

        # create augmented mean state, remember mean of noise is zero
        x_aug.value = self._x.value + [[0], [0]]

        # create augmented covariance matrix
        P_aug.value = ([row + [0, 0] for row in self._P.value] +
                       [[0, 0, 0, 0, 0, self._std_a * self._std_a, 0],
                        [0, 0, 0, 0, 0, 0, self._std_yawdd * self._std_yawdd]])

        # create square root matrix
        L = P_aug.Cholesky().transpose()

        # create augmented sigma points
        for row in range(len(x_aug.value)):
            Xsig_aug.value[row][0] = x_aug.value[row][0]
        for i in range(self._n_aug):
            for row in range(len(x_aug.value)):
                Xsig_aug.value[row][i + 1] = (
                    x_aug.value[row][0] + self._lambda_sqrt * L.value[row][i]
                )  #+ sqrt(self._lambda+self._n_aug)*L.value[row][i])
                Xsig_aug.value[row][i + 1 + self._n_aug] = (
                    x_aug.value[row][0] - self._lambda_sqrt * L.value[row][i]
                )  #- sqrt(self._lambda+self._n_aug)*L.value[row][i])

        return Xsig_aug

    def predict_mean_and_covariance(self):
        #Lesson 7, section 24: Predicted Mean and Covariance Assignment 2

        # create vector for predicted state
        x = Matrix([[]])

        # create covariance matrix for prediction
        P = Matrix([[]])

        # predicted state mean
        x.zero(self._n_x, 1)
        for i in range(2 * self._n_aug + 1):  # iterate over sigma points
            for row in range(x.dimx):
                x.value[row] = [
                    x.value[row][0] +
                    self._weights.value[i][0] * self._Xsig_pred.value[row][i]
                ]

        # predicted state covariance matrix
        P.zero(self._n_x, self._n_x)
        for i in range(2 * self._n_aug + 1):  # iterate over sigma points
            # state difference
            x_diff = Matrix([[]])
            x_diff.zero(self._n_x, 1)
            for row in range(self._n_x):
                x_diff.value[row][0] = self._Xsig_pred.value[row][i]
            x_diff = x_diff - x
            # angle normalization
            x_diff.value[3][0] = fmod(x_diff.value[3][0], pi)

            x_diff2 = x_diff * x_diff.transpose()
            x_diff2.value = [[xv * self._weights.value[i][0] for xv in row]
                             for row in x_diff2.value]
            P = P + x_diff2

        return (x, P)

    def predict_radar_measurement(self):
        # Lesson 7, section 27: Predict Radar Measurement Assignment 2

        # set measurement dimension, radar can measure r, phi, and r_dot
        n_z = 3

        # create matrix for sigma points in measurement space
        Zsig = Matrix([[]])
        Zsig.zero(n_z, 2 * self._n_aug + 1)

        # mean predicted measurement
        z_pred = Matrix([[]])

        # measurement covariance matrix S
        S = Matrix([[]])
        S.zero(n_z, n_z)

        # transform sigma points into measurement space
        for i in range(2 * self._n_aug + 1):  # 2n+1 simga points
            # extract values for better readability
            p_x = self._Xsig_pred.value[0][i]
            p_y = self._Xsig_pred.value[1][i]
            v = self._Xsig_pred.value[2][i]
            yaw = self._Xsig_pred.value[3][i]

            v1 = cos(yaw) * v
            v2 = sin(yaw) * v

            # measurement model
            Zsig.value[0][i] = sqrt(p_x * p_x + p_y * p_y)  # r
            Zsig.value[1][i] = atan2(p_y, p_x)  # phi
            Zsig.value[2][i] = (p_x * v1 + p_y * v2) / Zsig.value[0][
                i]  #sqrt(p_x*p_x + p_y*p_y)   # r_dot

        # mean predicted measurement
        z_pred.zero(n_z, 1)
        for i in range(2 * self._n_aug + 1):
            for row in range(n_z):
                z_pred.value[row] = [
                    z_pred.value[row][0] +
                    self._weights.value[i][0] * Zsig.value[row][i]
                ]

        # innovation covariance matrix S
        S.zero(n_z, n_z)
        for i in range(2 * self._n_aug + 1):  # 2n+1 simga points
            # residual
            z_diff = Matrix([[]])
            z_diff.zero(n_z, 1)
            for row in range(n_z):
                z_diff.value[row][0] = Zsig.value[row][i]
            z_diff = z_diff - z_pred

            # angle normalization
            z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

            z_diff2 = z_diff * z_diff.transpose()
            z_diff2.value = [[zv * self._weights.value[i][0] for zv in row]
                             for row in z_diff2.value]
            S = S + z_diff2

        # add measurement noise covariance matrix
        R = Matrix([[self._std_radr * self._std_radr, 0, 0],
                    [0, self._std_radphi * self._std_radphi, 0],
                    [0, 0, self._std_radrd * self._std_radrd]])
        S = S + R

        return (Zsig, z_pred, S)
    def test_UpdateRadar_CallsPredictRadarMeasurementThenCalculatesXAndP_ForGivenMeasurement(
            self):
        #radar measurement noise standard deviation radius in m
        self._ukf._std_radr = 0.3

        #radar measurement noise standard deviation angle in rad
        self._ukf._std_radphi = 0.0175

        #radar measurement noise standard deviation radius change in m/s
        self._ukf._std_radrd = 0.1

        self._ukf._Xsig_pred = Matrix(
            [[
                5.9374, 6.0640, 5.925, 5.9436, 5.9266, 5.9374, 5.9389, 5.9374,
                5.8106, 5.9457, 5.9310, 5.9465, 5.9374, 5.9359, 5.93744
            ],
             [
                 1.48, 1.4436, 1.660, 1.4934, 1.5036, 1.48, 1.4868, 1.48,
                 1.5271, 1.3104, 1.4787, 1.4674, 1.48, 1.4851, 1.486
             ],
             [
                 2.204, 2.2841, 2.2455, 2.2958, 2.204, 2.204, 2.2395, 2.204,
                 2.1256, 2.1642, 2.1139, 2.204, 2.204, 2.1702, 2.2049
             ],
             [
                 0.5367, 0.47338, 0.67809,
                 0.55455, 0.64364, 0.54337, 0.5367, 0.53851, 0.60017, 0.39546,
                 0.51900, 0.42991, 0.530188, 0.5367, 0.535048
             ],
             [
                 0.352, 0.29997, 0.46212, 0.37633, 0.4841, 0.41872, 0.352,
                 0.38744, 0.40562, 0.24347, 0.32926, 0.2214, 0.28687, 0.352,
                 0.318159
             ]])

        #create example vector for predicted state mean
        self._ukf._x = Matrix([[5.93637], [1.49035], [2.20528], [0.536853],
                               [0.353577]])

        #create example matrix for predicted state covariance
        self._ukf._P = Matrix(
            [[0.0054342, -0.002405, 0.0034157, -0.0034819, -0.00299378],
             [-0.002405, 0.01084, 0.001492, 0.0098018, 0.00791091],
             [0.0034157, 0.001492, 0.0058012, 0.00077863, 0.000792973],
             [-0.0034819, 0.0098018, 0.00077863, 0.011923, 0.0112491],
             [-0.0029937, 0.0079109, 0.00079297, 0.011249, 0.0126972]])

        # create example matrix with sigma points in measurement space
        Zsig = Matrix([[
            6.1190, 6.2334, 6.1531, 6.1283, 6.1143, 6.1190, 6.1221, 6.1190,
            6.0079, 6.0883, 6.1125, 6.1248, 6.1190, 6.1188, 6.12057
        ],
                       [
                           0.24428, 0.2337, 0.27316, 0.24616, 0.24846, 0.24428,
                           0.24530, 0.24428, 0.25700, 0.21692, 0.24433,
                           0.24193, 0.24428, 0.24515, 0.245239
                       ],
                       [
                           2.1104, 2.2188, 2.0639, 2.187, 2.0341, 2.1061,
                           2.1450, 2.1092, 2.0016, 2.129, 2.0346, 2.1651,
                           2.1145, 2.0786, 2.11295
                       ]])

        # create example vector for mean predicted measurement
        z_pred = Matrix([[6.12155], [0.245993], [2.10313]])

        # create example matrix for predicted measurement covariance
        S = Matrix([[0.0946171, -0.000139448, 0.00407016],
                    [-0.000139448, 0.000617548, -0.000770652],
                    [0.00407016, -0.000770652, 0.0180917]])

        self._ukf.predict_radar_measurement = MagicMock(return_value=(Zsig,
                                                                      z_pred,
                                                                      S))

        meas_package = MeasurementPackage(
            0,
            MeasurementPackage.SensorType.RADAR,
            Matrix([
                [5.9214],  # rho in m
                [0.2187],  # phi in rad
                [2.0062]
            ]))  # rho_dot in m/s

        self._ukf.update_radar(meas_package)

        # expected result x:
        expected_x = Matrix([[5.92276], [1.41823], [2.15593], [0.489274],
                             [0.321338]])

        assert_array_almost_equal(self._ukf._x.value,
                                  expected_x.value,
                                  decimal=4)

        expected_P = Matrix(
            [[0.00361579, -0.000357881, 0.00208316, -0.000937196, -0.00071727],
             [-0.000357881, 0.00539867, 0.00156846, 0.00455342, 0.00358885],
             [0.00208316, 0.00156846, 0.00410651, 0.00160333, 0.00171811],
             [-0.000937196, 0.00455342, 0.00160333, 0.00652634, 0.00669436],
             [-0.00071719, 0.00358884, 0.00171811, 0.00669426, 0.00881797]])

        assert_array_almost_equal(self._ukf._P.value,
                                  expected_P.value,
                                  decimal=5)
        self.assertTrue(self._ukf.predict_radar_measurement.called)
    def update_radar(self, meas_package):
        """Updates the state and the state covariance matrix using a radar measurement
        
        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Use radar data to update the belief
        about the object's position. Modify the state vector, self._x, and
        covariance, self._P.
        You can also calculate the radar NIS, if desired.
        """
        """
        Predict Radar Sigma Points
        """

        #set measurement dimension, radar can measure r, phi, and r_dot
        n_z = 3

        Zsig, z_pred, S = self.predict_radar_measurement()
        """
        Update Radar
        """

        #Lesson 7, section 30: UKF Update Assignment 2

        # create matrix for cross correlation Tc
        Tc = Matrix([[]])

        # calculate cross correlation matrix
        Tc.zero(self._n_x, n_z)
        for i in range(2 * self._n_aug + 1):  #2n+1 simga points
            # residual
            z_diff = Matrix([[]])
            z_diff.zero(n_z, 1)
            for row in range(n_z):
                z_diff.value[row][0] = Zsig.value[row][i]
            z_diff = z_diff - z_pred
            # angle normalization
            z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

            # state difference
            x_diff = Matrix([[]])
            x_diff.zero(self._n_x, 1)
            for row in range(self._n_x):
                x_diff.value[row][0] = self._Xsig_pred.value[row][i]
            x_diff = x_diff - self._x
            # angle normalization
            x_diff.value[3][0] = fmod(x_diff.value[3][0], pi)

            x_diff_z_diff_t = x_diff * z_diff.transpose()
            x_diff_z_diff_t.value = [[
                xzv * self._weights.value[i][0] for xzv in row
            ] for row in x_diff_z_diff_t.value]
            Tc = Tc + x_diff_z_diff_t

        # Kalman gain K;
        K = Tc * S.inverse()

        # residual
        z_diff = meas_package._raw_measurements - z_pred

        # angle normalization
        z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

        #update state mean and covariance matrix
        self._x = self._x + K * z_diff
        self._P = self._P - K * S * K.transpose()

        nis = z_diff.transpose() * S.inverse() * z_diff
        self._radar_nis = nis.value[0][0]
    def augmented_sigma_points(self):
        #Lesson 7, section 18: Augmentation Assignment 2

        #create augmented mean vector
        x_aug = Matrix([[]])
        x_aug.zero(self._n_aug, 1)

        # create augmented state covariance
        P_aug = Matrix([[]])
        P_aug.zero(self._n_aug, self._n_aug)

        # create sigma point matrix
        Xsig_aug = Matrix([[]])
        Xsig_aug.zero(self._n_aug, 2 * self._n_aug + 1)

        # create augmented mean state, remember mean of noise is zero
        x_aug.value = self._x.value + [[0], [0]]

        # create augmented covariance matrix
        P_aug.value = ([row + [0, 0] for row in self._P.value] +
                       [[0, 0, 0, 0, 0, self._std_a * self._std_a, 0],
                        [0, 0, 0, 0, 0, 0, self._std_yawdd * self._std_yawdd]])

        # create square root matrix
        L = P_aug.Cholesky().transpose()

        # create augmented sigma points
        for row in range(len(x_aug.value)):
            Xsig_aug.value[row][0] = x_aug.value[row][0]
        for i in range(self._n_aug):
            for row in range(len(x_aug.value)):
                Xsig_aug.value[row][i + 1] = (
                    x_aug.value[row][0] + self._lambda_sqrt * L.value[row][i]
                )  #+ sqrt(self._lambda+self._n_aug)*L.value[row][i])
                Xsig_aug.value[row][i + 1 + self._n_aug] = (
                    x_aug.value[row][0] - self._lambda_sqrt * L.value[row][i]
                )  #- sqrt(self._lambda+self._n_aug)*L.value[row][i])

        return Xsig_aug
    def test_Prediction_CallsAugmentedSigmaPointsThenUpdateXsigPredAndCallsPredictMeanAndCovariance_ForGivenDeltaT(
            self):
        self._ukf._std_a = 0.2
        self._ukf._std_yawdd = 0.2
        self._ukf._x = Matrix([[5.7441], [1.3800], [2.2049], [0.5015],
                               [0.3528]])

        self._ukf._P = Matrix([[0.0043, -0.0013, 0.0030, -0.0022, -0.0020],
                               [-0.0013, 0.0077, 0.0011, 0.0071, 0.0060],
                               [0.0030, 0.0011, 0.0054, 0.0007, 0.0008],
                               [-0.0022, 0.0071, 0.0007, 0.0098, 0.0100],
                               [-0.0020, 0.0060, 0.0008, 0.0100, 0.0123]])

        self._ukf.augmented_sigma_points = MagicMock(
            wraps=self._ukf.augmented_sigma_points)
        self._ukf.predict_mean_and_covariance = MagicMock(
            wraps=self._ukf.predict_mean_and_covariance)

        ukf_sequence = Mock()
        ukf_sequence.attach_mock(self._ukf.augmented_sigma_points,
                                 'augmented_sigma_points')
        ukf_sequence.attach_mock(self._ukf.predict_mean_and_covariance,
                                 'predict_mean_and_covariance')

        delta_t = 0.1  #time diff in sec
        self._ukf.prediction(delta_t)

        expected_Xsig_pred = Matrix(
            [[
                5.93553, 6.06251, 5.92217, 5.9415, 5.92361, 5.93516, 5.93705,
                5.93553, 5.80832, 5.94481, 5.92935, 5.94553, 5.93589, 5.93401,
                5.93553
            ],
             [
                 1.48939, 1.44673, 1.66484, 1.49719, 1.508, 1.49001, 1.49022,
                 1.48939, 1.5308, 1.31287, 1.48182, 1.46967, 1.48876, 1.48855,
                 1.48939
             ],
             [
                 2.2049, 2.28414, 2.24557, 2.29582, 2.2049, 2.2049, 2.23954,
                 2.2049, 2.12566, 2.16423, 2.11398, 2.2049, 2.2049, 2.17026,
                 2.2049
             ],
             [
                 0.53678, 0.473387, 0.678098, 0.554557, 0.643644, 0.543372,
                 0.53678, 0.538512, 0.600173, 0.395462, 0.519003, 0.429916,
                 0.530188, 0.53678, 0.535048
             ],
             [
                 0.3528, 0.299973, 0.462123, 0.376339, 0.48417, 0.418721,
                 0.3528, 0.387441, 0.405627, 0.243477, 0.329261, 0.22143,
                 0.286879, 0.3528, 0.318159
             ]])

        assert_array_almost_equal(self._ukf._Xsig_pred.value,
                                  expected_Xsig_pred.value,
                                  decimal=4)
        assert ukf_sequence.mock_calls == [
            call.augmented_sigma_points(),
            call.predict_mean_and_covariance()
        ]
    def __init__(self):
        """Initializes Unscented Kalman filter"""
        # initially set to false, set to true in first call of ProcessMeasurement
        self._is_initialized = False

        # predicted sigma points matrix
        self._Xsig_pred = Matrix([[]])

        # time when the state is true, in us
        self._time_us = 0

        # Weights of sigma points
        self._weights = Matrix([[]])

        # State dimension
        self._n_x = 5

        # Augmented state dimension
        self._n_aug = 7

        # Sigma point spreading parameter
        self._lambda = 3 - self._n_aug

        self._lambda_sqrt = sqrt(self._lambda + self._n_aug)

        #if this is false, laser measurements will be ignored (except during init)
        self._use_laser = True

        #if this is false, radar measurements will be ignored (except during init)
        self._use_radar = True

        # initial state vector
        # state vector: [pos1 pos2 vel_abs yaw_angle yaw_rate] in SI units and rad
        self._x = Matrix([[]])
        self._x.zero(5, 1)

        # initial covariance matrix
        self._P = Matrix([[]])
        self._P.zero(5, 5)

        # Process noise standard deviation longitudinal acceleration in m/s^2
        self._std_a = 0.58

        # Process noise standard deviation yaw acceleration in rad/s^2
        self._std_yawdd = 0.57
        """
        DO NOT MODIFY measurement noise values below.
        These are provided by the sensor manufacturer.
        """

        # Laser measurement noise standard deviation position1 in m
        self._std_laspx = 0.15

        # Laser measurement noise standard deviation position2 in m
        self._std_laspy = 0.15

        # Radar measurement noise standard deviation radius in m
        self._std_radr = 0.3

        # Radar measurement noise standard deviation angle in rad
        self._std_radphi = 0.03

        # Radar measurement noise standard deviation radius change in m/s
        self._std_radrd = 0.3
        """
        End DO NOT MODIFY section for measurement noise values 
        """

        #set vector for weights
        self._weights.zero(2 * self._n_aug + 1, 1)

        # set weights
        weight_0 = self._lambda / (self._lambda + self._n_aug)
        weight = 0.5 / (self._n_aug + self._lambda)
        self._weights.value[0] = [weight_0]

        for i in range(1, 2 * self._n_aug + 1):
            self._weights.value[i] = [weight]

        #create matrix with predicted sigma points as columns
        self._Xsig_pred.zero(self._n_x, 2 * self._n_aug + 1)

        # Lidar measurement covariance
        self._R = Matrix([[self._std_laspx * self._std_laspx, 0],
                          [0, self._std_laspy * self._std_laspy]])

        # Lidar measurement matrix
        self._H = Matrix([[1, 0, 0, 0, 0], [0, 1, 0, 0, 0]])

        self._Ht = self._H.transpose()

        self._lidar_nis = 0
        self._radar_nis = 0
def parse_vector(line):
    return Matrix([[float(i)] for i in line.split()])
    def predict_radar_measurement(self):
        # Lesson 7, section 27: Predict Radar Measurement Assignment 2

        # set measurement dimension, radar can measure r, phi, and r_dot
        n_z = 3

        # create matrix for sigma points in measurement space
        Zsig = Matrix([[]])
        Zsig.zero(n_z, 2 * self._n_aug + 1)

        # mean predicted measurement
        z_pred = Matrix([[]])

        # measurement covariance matrix S
        S = Matrix([[]])
        S.zero(n_z, n_z)

        # transform sigma points into measurement space
        for i in range(2 * self._n_aug + 1):  # 2n+1 simga points
            # extract values for better readability
            p_x = self._Xsig_pred.value[0][i]
            p_y = self._Xsig_pred.value[1][i]
            v = self._Xsig_pred.value[2][i]
            yaw = self._Xsig_pred.value[3][i]

            v1 = cos(yaw) * v
            v2 = sin(yaw) * v

            # measurement model
            Zsig.value[0][i] = sqrt(p_x * p_x + p_y * p_y)  # r
            Zsig.value[1][i] = atan2(p_y, p_x)  # phi
            Zsig.value[2][i] = (p_x * v1 + p_y * v2) / Zsig.value[0][
                i]  #sqrt(p_x*p_x + p_y*p_y)   # r_dot

        # mean predicted measurement
        z_pred.zero(n_z, 1)
        for i in range(2 * self._n_aug + 1):
            for row in range(n_z):
                z_pred.value[row] = [
                    z_pred.value[row][0] +
                    self._weights.value[i][0] * Zsig.value[row][i]
                ]

        # innovation covariance matrix S
        S.zero(n_z, n_z)
        for i in range(2 * self._n_aug + 1):  # 2n+1 simga points
            # residual
            z_diff = Matrix([[]])
            z_diff.zero(n_z, 1)
            for row in range(n_z):
                z_diff.value[row][0] = Zsig.value[row][i]
            z_diff = z_diff - z_pred

            # angle normalization
            z_diff.value[1][0] = fmod(z_diff.value[1][0], pi)

            z_diff2 = z_diff * z_diff.transpose()
            z_diff2.value = [[zv * self._weights.value[i][0] for zv in row]
                             for row in z_diff2.value]
            S = S + z_diff2

        # add measurement noise covariance matrix
        R = Matrix([[self._std_radr * self._std_radr, 0, 0],
                    [0, self._std_radphi * self._std_radphi, 0],
                    [0, 0, self._std_radrd * self._std_radrd]])
        S = S + R

        return (Zsig, z_pred, S)
def parse_matrix(text):
    return Matrix([[float(i) for i in line.split()] for line in text.splitlines()])
    def process_measurement(self, meas_package):
        """ProcessMeasurement

        Args:
            meas_package (:obj:`MeasurementPackage`): The measurement at k+1
        """
        """
        Initialization structure similar to EKF project
        """

        if not self._is_initialized:
            #Initialize x_, P_, previous_time, anything else needed.

            if meas_package._sensor_type == MeasurementPackage.SensorType.LASER:
                #Initialize here
                self._x.zero(self._n_x, 1)
                self._x.value[0:2] = meas_package._raw_measurements.value

                self._P = Matrix([
                    [self._std_laspx * self._std_laspx, 0, 0, 0, 0],
                    [0, self._std_laspy * self._std_laspy, 0, 0, 0],
                    [0, 0, 9, 0, 0],  #Assumming 95% of time speed is +/- 6m/s
                    [0, 0, 0, 2.25,
                     0],  #Assuming 95% of the time angle is +/- 3rad
                    [0, 0, 0, 0, 0.0625]
                ])  #Assuming 95% of the time angular speed +/- 0.5 rad/s

            elif meas_package._sensor_type == MeasurementPackage.SensorType.RADAR:
                #Initialize here
                #Convert radar from polar to cartesian coordinates
                #         and initialize state.
                ro = meas_package._raw_measurements.value[0][0]
                theta = meas_package._raw_measurements.value[1][0]
                self._x.zero(self._n_x, 1)
                self._x.value[0:2] = [[ro * cos(theta)], [ro * sin(theta)]]

                #max(standard deviation radius, standard deviation angle*radar distance)
                #a crude estimation of maximum standard deviation in cartesian coordinates
                std_rad = max(self._std_radr, self._std_radphi * ro) * 2
                self._P = Matrix([
                    [std_rad * std_rad, 0, 0, 0, 0],
                    [0, std_rad * std_rad, 0, 0, 0],
                    [0, 0, 9, 0, 0],  #Assumming 95% of time speed is +/- 6m/s
                    [0, 0, 0, 2.25,
                     0],  #Assuming 95% of the time angle is +/- 3rad
                    [0, 0, 0, 0, 0.0625]
                ])  #Assuming 95% of the time angular speed +/- 0.5 rad/s

            #Initialize anything else here
            self._time_us = meas_package._timestamp
            self._is_initialized = True
            return
        """
        Control structure similar to EKF project
        """

        delta_t = (meas_package._timestamp - self._time_us) / 1000000.0
        self.prediction(delta_t)

        if ((meas_package._sensor_type == MeasurementPackage.SensorType.LASER)
                and self._use_laser):
            self.update_lidar(meas_package)

        elif (
            (meas_package._sensor_type == MeasurementPackage.SensorType.RADAR)
                and self._use_radar):
            self.update_radar(meas_package)

        self._time_us = meas_package._timestamp