Пример #1
0
    def test_that_is_aligned_for_multiple_points_where_system_is_rotated_and_poins_are_fuzzy(
            self):
        # Fixture
        origin = (0.0, 0.0, 0.0)
        x_axis = [(-1.0, 0.0 + 0.01, 0.0), (-2.0, 0.0 - 0.02, 0.0)]
        xy_plane = [(2.0, 2.0, 0.0 + 0.02), (-3.0, -2.0, 0.0 + 0.01),
                    (5.0, 0.0, 0.0 - 0.01)]

        # Note: Z of base stations must be positive (above the floor)
        bs_poses = {
            1: Pose.from_rot_vec(t_vec=(1.0, 0.0, 1.0)),
            2: Pose.from_rot_vec(t_vec=(0.0, 1.0, 1.0)),
            3: Pose.from_rot_vec(t_vec=(0.0, 0.0, 1.0)),
        }

        # Test
        actual, transform = LighthouseSystemAligner.align(
            origin, x_axis, xy_plane, bs_poses)

        # Assert
        self.assertVectorsAlmostEqual((-1.0, 0.0, 1.0),
                                      actual[1].translation,
                                      places=1)
        self.assertVectorsAlmostEqual((0.0, -1.0, 1.0),
                                      actual[2].translation,
                                      places=1)
        self.assertVectorsAlmostEqual((0.0, 0.0, 1.0),
                                      actual[3].translation,
                                      places=1)
    def _de_flip_transformation(cls, raw_transformation: Pose,
                                x_axis: list[npt.ArrayLike],
                                bs_poses: dict[int, Pose]) -> Pose:
        """
        Investigats a transformation and flips it if needed. This method assumes that
        1. all base stations are at Z>0
        2. x_axis samples are taken at X>0
        """
        transformation = raw_transformation

        # X-axis poses should be on the positivie X-axis, check that the "mean" of the x-axis points ends up at X>0
        x_axis_mean = np.mean(x_axis, axis=0)
        if raw_transformation.rotate_translate(x_axis_mean)[0] < 0.0:
            flip_around_z_axis = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi))
            transformation = flip_around_z_axis.rotate_translate_pose(
                transformation)

        # Base station poses should be above the floor, check the first one
        bs_pose = list(bs_poses.values())[0]
        if raw_transformation.rotate_translate(bs_pose.translation)[2] < 0.0:
            flip_around_x_axis = Pose.from_rot_vec(R_vec=(np.pi, 0.0, 0.0))
            transformation = flip_around_x_axis.rotate_translate_pose(
                transformation)

        return transformation
Пример #3
0
    def _convert_estimates_to_cf_reference_frame(
            cls, estimates_ref_bs: list[IppeCf.Solution]) -> tuple[Pose, Pose]:
        """
        Convert the two ippe solutions from the base station reference frame to the CF reference frame
        """
        rot_1 = estimates_ref_bs[0].R.transpose()
        t_1 = np.dot(rot_1, -estimates_ref_bs[0].t)

        rot_2 = estimates_ref_bs[1].R.transpose()
        t_2 = np.dot(rot_2, -estimates_ref_bs[1].t)

        return Pose(rot_1, t_1), Pose(rot_2, t_2)
    def test_rotate_translate_pose_and_back(self):
        # Fixture
        transform = Pose.from_rot_vec(R_vec=(1.0, 2.0, 3.0),
                                      t_vec=(0.1, 0.2, 0.3))
        expected = Pose(t_vec=(2.0, 3.0, 4.0))

        # Test
        actual = transform.inv_rotate_translate_pose(
            transform.rotate_translate_pose(expected))

        # Assert
        self.assertPosesAlmostEqual(expected, actual)
    def test_rotate_translate_pose(self):
        # Fixture
        transform = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2),
                                      t_vec=(1.0, 0.0, 0.0))
        pose = Pose(t_vec=(2.0, 0.0, 0.0))
        expected = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2),
                                     t_vec=(1.0, 2.0, 0.0))

        # Test
        actual = transform.rotate_translate_pose(pose)

        # Assert
        self.assertPosesAlmostEqual(expected, actual)
Пример #6
0
    def _condense_results(cls, lsq_result,
                          solution: LighthouseGeometrySolution,
                          matched_samples: list[LhCfPoseSample]) -> None:
        bss, cf_poses = cls._params_to_struct(lsq_result.x, solution)

        # Extract CF pose estimates
        # First pose (origin) is not in the parameter list
        solution.cf_poses.append(Pose())
        for i in range(len(matched_samples) - 1):
            solution.cf_poses.append(cls._params_to_pose(
                cf_poses[i], solution))

        # Extract base station pose estimates
        for index, pose in enumerate(bss):
            bs_id = solution.bs_index_to_id[index]
            solution.bs_poses[bs_id] = cls._params_to_pose(pose, solution)

        solution.success = lsq_result.success

        # Extract the error for each CF pose
        residuals = lsq_result.fun
        i = 0
        for sample in matched_samples:
            sample_errors = {}
            for bs_id in sorted(sample.angles_calibrated.keys()):
                sample_errors[bs_id] = np.linalg.norm(residuals[i:i + 2])
                i += solution.n_sensors * 2
            solution.estimated_errors.append(sample_errors)

        solution.error_info = cls._aggregate_error_info(
            solution.estimated_errors)
Пример #7
0
    def test_that_solution_is_de_flipped(self):
        # Fixture
        origin = (0.0, 0.0, 0.0)
        x_axis = [(-1.0, 0.0, 0.0)]
        xy_plane = [(2.0, 1.0, 0.0)]

        bs_id = 7
        bs_poses = {bs_id: Pose.from_rot_vec(t_vec=(0.0, 0.0, 1.0))}
        expected = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi),
                                     t_vec=(0.0, 0.0, 1.0))

        # Test
        actual, transform = LighthouseSystemAligner.align(
            origin, x_axis, xy_plane, bs_poses)

        # Assert
        self.assertPosesAlmostEqual(expected, actual[bs_id])
Пример #8
0
    def test_that_base_stations_are_rotated(self):
        # Fixture
        origin = (1.0, 0.0, 0.0)
        x_axis = [(1.0, 1.0, 0.0)]
        xy_plane = [(2.0, 1.0, 0.0)]

        bs_id = 7
        bs_poses = {bs_id: Pose.from_rot_vec(t_vec=(1.0, 0.0, 1.0))}

        # Test
        actual, transform = LighthouseSystemAligner.align(
            origin, x_axis, xy_plane, bs_poses)

        # Assert
        self.assertPosesAlmostEqual(
            Pose.from_rot_vec(R_vec=(0.0, 0.0, -np.pi / 2),
                              t_vec=(0.0, 0.0, 1.0)), actual[bs_id])
Пример #9
0
 def _params_to_pose(cls, params: npt.ArrayLike,
                     defs: LighthouseGeometrySolution) -> Pose:
     """
     Convert from the array format used in the solver to Pose
     """
     r_vec = params[:defs.len_rot_vec]
     t = params[defs.len_rot_vec:defs.len_pose]
     return Pose.from_rot_vec(R_vec=r_vec, t_vec=t)
    def test_default_rot_vec_constructor(self):
        # Fixture
        # Test
        actual = Pose.from_rot_vec()

        # Assert
        self.assertEqual(0.0, np.linalg.norm(actual.translation))
        self.assertEqual(0.0,
                         np.linalg.norm(actual.rot_matrix - np.identity(3)))
Пример #11
0
    def _map_pose_to_ref_frame(cls, pose1_ref1: Pose, pose1_ref2: Pose,
                               pose2_ref2: Pose) -> Pose:
        """
        Express pose2 in reference system 1, given pose1 in both reference system 1 and 2
        """
        R_o2_in_1, t_o2_in_1 = cls._map_cf_pos_to_cf_pos(
            pose1_ref1, pose1_ref2).matrix_vec

        t = np.dot(R_o2_in_1, pose2_ref2.translation) + t_o2_in_1
        R = np.dot(R_o2_in_1, pose2_ref2.rot_matrix)

        return Pose(R, t)
Пример #12
0
    def _map_cf_pos_to_cf_pos(cls, pose1_ref1: Pose, pose1_ref2: Pose) -> Pose:
        """
        Find the rotation/translation from ref1 to ref2 given a pose,
        that is the returned Pose will tell us where the origin in ref2 is,
        expressed in ref1
        """

        R_inv_ref2 = np.matrix.transpose(pose1_ref2.rot_matrix)
        R = np.dot(pose1_ref1.rot_matrix, R_inv_ref2)
        t = pose1_ref1.translation - np.dot(R, pose1_ref2.translation)

        return Pose(R, t)
    def test_rotate_translate(self):
        # Fixture
        transform = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2),
                                      t_vec=(1.0, 0.0, 0.0))
        point = (2.0, 0.0, 0.0)

        # Test
        actual = transform.rotate_translate(point)

        # Assert
        self.assertAlmostEqual(1.0, actual[0])
        self.assertAlmostEqual(2.0, actual[1])
        self.assertAlmostEqual(0.0, actual[2])
    def test_rotate_translate_and_back(self):
        # Fixture
        transform = Pose.from_rot_vec(R_vec=(1.0, 2.0, 3.0),
                                      t_vec=(0.1, 0.2, 0.3))
        expected = (2.0, 3.0, 4.0)

        # Test
        actual = transform.inv_rotate_translate(
            transform.rotate_translate(expected))

        # Assert
        self.assertAlmostEqual(expected[0], actual[0])
        self.assertAlmostEqual(expected[1], actual[1])
        self.assertAlmostEqual(expected[2], actual[2])
Пример #15
0
    def test_that_the_global_ref_frame_is_used(self):
        # Fixture
        # CF2 is used in the first sample and will define the global reference frame
        bs_id0 = 3
        bs_id1 = 1
        bs_id2 = 2
        samples = [
            LhCfPoseSample(
                angles_calibrated={
                    bs_id0: self.fixtures.angles_cf2_bs0,
                    bs_id1: self.fixtures.angles_cf2_bs1,
                }),
            LhCfPoseSample(
                angles_calibrated={
                    bs_id1: self.fixtures.angles_cf1_bs1,
                    bs_id2: self.fixtures.angles_cf1_bs2,
                }),
        ]

        # Test
        actual = LighthouseInitialEstimator.estimate(
            samples, LhDeck4SensorPositions.positions)

        # Assert
        self.assertPosesAlmostEqual(Pose.from_rot_vec(R_vec=(0.0, 0.0,
                                                             -np.pi / 2),
                                                      t_vec=(1.0, 3.0, 3.0)),
                                    actual.bs_poses[bs_id0],
                                    places=3)
        self.assertPosesAlmostEqual(Pose.from_rot_vec(R_vec=(0.0, 0.0, 0.0),
                                                      t_vec=(-2.0, 1.0, 3.0)),
                                    actual.bs_poses[bs_id1],
                                    places=3)
        self.assertPosesAlmostEqual(Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi),
                                                      t_vec=(2.0, 1.0, 3.0)),
                                    actual.bs_poses[bs_id2],
                                    places=3)
Пример #16
0
    def _avarage_poses(cls, poses: list[Pose]) -> Pose:
        """
        Averaging of quaternions to get the "average" orientation of multiple samples.
        From https://stackoverflow.com/a/61013769
        """
        def q_average(Q, W=None):
            if W is not None:
                Q *= W[:, None]
            eigvals, eigvecs = np.linalg.eig(Q.T @ Q)
            return eigvecs[:, eigvals.argmax()]

        positions = map(lambda x: x.translation, poses)
        average_pos = np.average(np.array(list(positions)), axis=0)

        quats = map(lambda x: x.rot_quat, poses)
        average_quaternion = q_average(np.array(list(quats)))

        return Pose.from_quat(R_quat=average_quaternion, t_vec=average_pos)
 def _Pose_from_params(cls, params: npt.ArrayLike) -> Pose:
     return Pose.from_rot_vec(R_vec=params[:3], t_vec=params[3:])