Beispiel #1
0
 def rf_tree(self, register_rf_tree):
     """"""
     register_rf_tree(
         tc1=(1.0, 0.0, 0.0),
         tc2=(-1.0, 0.0, 0.0),
         rc2=from_euler_angles(yaw=np.pi),
     )
Beispiel #2
0
    def test_qinv(self):
        """"""
        q = from_euler_angles(roll=np.pi / 4, return_quaternion=True)

        assert qinv(q) == 1 / q
        npt.assert_equal(qinv(as_float_array(q)), as_float_array(1 / q))

        q_arr = np.tile(as_float_array(q), (10, 1)).T
        npt.assert_equal(qinv(q_arr, 0)[:, 0], as_float_array(1 / q))
Beispiel #3
0
    def test_transform_quaternions(self, rf_tree):
        """"""
        arr_child2 = (1.0, 0.0, 0.0, 0.0)
        arr_exp = from_euler_angles(yaw=np.pi)

        # tuple
        arr_child1 = rbm.transform_quaternions(arr_child2,
                                               into="child1",
                                               outof="child2")
        npt.assert_almost_equal(arr_child1, arr_exp)
    def test_best_fit_rotation(self, get_rf_tree):
        """"""
        rf_world, rf_child1, _ = get_rf_tree(
            (0.0, 0.0, 0.0), from_euler_angles(roll=np.pi / 2)
        )
        v1 = np.random.randn(10, 3)
        v2 = rf_world.transform_points(v1, rf_child1)

        r = best_fit_rotation(v1, v2)
        _, r_exp, _ = rf_world.lookup_transform(rf_child1)
        npt.assert_allclose(np.abs(r), np.abs(r_exp), rtol=1.0, atol=1e-10)
Beispiel #5
0
    def test_qmean(self):
        """"""
        q = np.hstack((
            from_euler_angles(roll=np.pi / 4, return_quaternion=True),
            from_euler_angles(roll=-np.pi / 4, return_quaternion=True),
            from_euler_angles(pitch=np.pi / 4, return_quaternion=True),
            from_euler_angles(pitch=-np.pi / 4, return_quaternion=True),
            quaternion(1.0, 0.0, 0.0, 0.0),
        ))

        # quaternion dtype
        qm = qmean(q)
        npt.assert_allclose(as_float_array(qm), np.array([1.0, 0.0, 0.0, 0.0]))

        # float dtype
        qm = qmean(as_float_array(q).T, qaxis=0)
        npt.assert_allclose(qm, np.array([1.0, 0.0, 0.0, 0.0]))

        # not all axes
        qm = qmean(np.tile(q, (10, 1)), axis=1)
        npt.assert_allclose(
            as_float_array(qm),
            np.tile(np.array([1.0, 0.0, 0.0, 0.0]), (10, 1)),
        )
Beispiel #6
0
    def test_qmul(self):
        """"""
        q1 = from_euler_angles(roll=np.pi / 4, return_quaternion=True)
        q2 = from_euler_angles(roll=np.pi / 4, return_quaternion=True)

        assert qmul(q1, q1) == q1 * q2
        npt.assert_equal(
            qmul(as_float_array(q1), as_float_array(q2)),
            as_float_array(q1 * q2),
        )

        q_arr = np.tile(as_float_array(q1), (10, 1))
        npt.assert_equal(
            qmul(q_arr, as_float_array(q2)),
            as_float_array(as_quat_array(q_arr) * q2),
        )

        # only one quaternion
        with pytest.raises(ValueError):
            qmul(q1)

        # different dtypes
        with pytest.raises(ValueError):
            qmul(q1, as_float_array(q2))
    def test_best_fit_transform_xr(self, get_rf_tree):
        """"""
        xr = pytest.importorskip("xarray")

        rf_world, rf_child1, _ = get_rf_tree(
            (1.0, 0.0, 0.0), from_euler_angles(roll=np.pi / 2)
        )
        v1 = np.random.randn(10, 3)
        v2 = rf_world.transform_points(v1, rf_child1)

        v1_da = xr.DataArray(
            v1, {"cartesian_axis": ["x", "y", "z"]}, ("time", "cartesian_axis")
        )
        t, r = best_fit_transform(v1_da.T, v2.T, dim="cartesian_axis")

        assert t.dims == ("cartesian_axis",)
        assert r.dims == ("quaternion_axis",)
    def test_iterative_closest_point(self, get_rf_tree):
        """"""
        np.random.seed(42)

        rf_world, rf_child1, _ = get_rf_tree(
            (1.0, 0.0, 0.0), from_euler_angles(yaw=np.pi / 6)
        )
        x, y = np.meshgrid(np.arange(10), np.arange(20))
        v1 = np.column_stack(
            (x.flatten() ** 2, y.flatten() ** 2, np.ones(x.size))
        ) + 0.01 * np.random.randn(x.size, 3)
        v2 = np.random.permutation(
            rf_world.transform_points(v1, rf_child1)[1:]
        )

        t, r = iterative_closest_point(v1, v2)
        t_exp, r_exp, _ = rf_world.lookup_transform(rf_child1)
        npt.assert_allclose(t, t_exp, rtol=1.0, atol=0.01)
        npt.assert_allclose(np.abs(r), np.abs(r_exp), rtol=1.0, atol=1e-4)
Beispiel #9
0
    def test_transform_quaternions_xr(self, rf_tree):
        """"""
        xr = pytest.importorskip("xarray")

        arr_child2 = (1.0, 0.0, 0.0, 0.0)
        arr_exp = from_euler_angles(yaw=np.pi)

        da_child2 = xr.DataArray(
            np.tile(arr_child2, (10, 1)),
            {"time": np.arange(10)},
            ("time", "quaternion_axis"),
            attrs={"reference_frame": "child2"},
        )
        da_child1 = rbm.transform_quaternions(
            da_child2,
            into="child1",
            dim="quaternion_axis",
            timestamps="time",
        )
        assert da_child1.shape == (10, 4)
        assert da_child1.attrs["reference_frame"] == "child1"
        assert da_child1.attrs["representation_frame"] == "child1"
        npt.assert_almost_equal(da_child1[0], arr_exp)

        # multi-dimensional vectors
        da_child2 = xr.DataArray(
            np.tile(arr_child2, (5, 10, 1)),
            {"time": np.arange(10)},
            ("extra_dim", "time", "quaternion_axis"),
        )
        da_child1 = rbm.transform_quaternions(
            da_child2,
            into="child1",
            outof="child2",
            dim="quaternion_axis",
            timestamps="time",
        )
        assert da_child1.shape == (5, 10, 4)
        assert da_child1.dims == ("extra_dim", "time", "quaternion_axis")
        npt.assert_almost_equal(da_child1[0, 0], arr_exp)
Beispiel #10
0
    def test_rotate_vectors(self):
        """"""
        v = np.ones((10, 3))
        q = np.tile(from_euler_angles(yaw=np.pi / 4, return_quaternion=True),
                    10)
        vr = np.vstack((np.zeros(10), np.sqrt(2) * np.ones(10), np.ones(10))).T

        # single quaternion, single vector
        vr_act = rotate_vectors(q[0], v[0])
        np.testing.assert_allclose(vr[0], vr_act, rtol=1.0)

        # single quaternion, multiple vectors
        vr_act = rotate_vectors(q[0], v)
        np.testing.assert_allclose(vr, vr_act, rtol=1.0)

        # single quaternion, explicit axis
        vr_act = rotate_vectors(q[0], v, axis=1)
        np.testing.assert_allclose(vr, vr_act, rtol=1.0)

        # multiple quaternions, multiple vectors
        vr_act = rotate_vectors(q, v)
        np.testing.assert_allclose(vr, vr_act)

        # different axis
        vr_act = rotate_vectors(q, v.T, axis=0)
        np.testing.assert_allclose(vr.T, vr_act)

        # singleton expansion
        vr_act = rotate_vectors(q[:, None], v[None, ...])
        np.testing.assert_allclose(np.tile(vr, (10, 1, 1)), vr_act)

        # float dtype
        vr_act = rotate_vectors(as_float_array(q[0]), v[0])
        np.testing.assert_allclose(vr[0], vr_act, rtol=1.0)

        with pytest.raises(ValueError):
            rotate_vectors(q, v.T)

        with pytest.raises(ValueError):
            rotate_vectors(q, np.ones((10, 4)))
    def test_best_fit_transform(self, get_rf_tree, icp_test_data):
        """"""
        rf_world, rf_child1, _ = get_rf_tree(
            (1.0, 0.0, 0.0), from_euler_angles(roll=np.pi / 2)
        )
        v1 = np.random.randn(10, 3)
        v2 = rf_world.transform_points(v1, rf_child1)

        t, r = best_fit_transform(v1, v2)
        t_exp, r_exp, _ = rf_world.lookup_transform(rf_child1)
        npt.assert_allclose(t, t_exp, rtol=1.0, atol=1e-10)
        npt.assert_allclose(np.abs(r), np.abs(r_exp), rtol=1.0, atol=1e-10)

        # real data
        v1 = icp_test_data["t265"]
        v2 = icp_test_data["vicon"]
        t, r = best_fit_transform(v1, v2)
        npt.assert_allclose(t, [-1.89872037, 0.61755277, 0.95930489])
        npt.assert_allclose(
            r,
            [-6.87968663e-01, 4.73801246e-04, 9.12595868e-03, 7.25682859e-01],
        )
Beispiel #12
0
def load_optitrack(filepath, import_format="numpy"):
    """ Load rigid body poses from OptiTrack csv export.

    Parameters
    ----------
    filepath: str or path-like
        Path to csv file.

    import_format: {"numpy", "pandas", "xarray"}, default "numpy"
        Import format for rigid body poses. "numpy" returns a (position,
        orientation, timestamps) tuple for each body, "pandas" returns a
        DataFrame and "xarray" returns a Dataset.

    Returns
    -------
    data_dict: dict
        Dictionary with one entry for each rigid body. See ``import_format``
        for the format of each entry.
    """
    try:
        import pandas as pd
    except ImportError:
        raise ModuleNotFoundError("Install pandas to use optitrack import")

    # parse header with metadata
    header = pd.read_csv(filepath, nrows=1, header=None).values
    meta = {k: v for k, v in zip(header[0][0::2], header[0][1::2])}

    # parse body with poses of rigid bodies and marker positions
    df = pd.read_csv(filepath,
                     header=[2, 3, 5, 6],
                     index_col=0,
                     skip_blank_lines=False)

    # compute timestamps
    timestamps = pd.to_datetime(
        meta["Capture Start Time"],
        format="%Y-%m-%d %I.%M.%S.%f %p") + pd.to_timedelta(df.values[:, 0],
                                                            unit="s")

    # only import rigid bodies, not markers
    df = df["Rigid Body"]

    # set index to timestamps and multi-index levels
    df = df.set_index(timestamps).rename_axis("time")
    df.columns = df.columns.set_names(["body ID", "motion type", "axis"])

    # split dataframe into dict with one entry for each rigid body
    data_dict = {
        idx: gp.xs(idx, level=0, axis=1)
        for idx, gp in df.groupby(level=0, axis=1)
    }

    # convert cm to m (if applicable)
    if meta.get("Length Units", None) == "Centimeters":
        for name in data_dict:
            data_dict[name]["Position"] /= 100

    # convert orientation to quaternions
    for name in data_dict:
        rpy = np.deg2rad(data_dict[name]["Rotation"].values)
        order = (meta.get("Rotation Type",
                          "XYZ").replace("X",
                                         "r").replace("Y",
                                                      "p").replace("Z", "y"))
        q = from_euler_angles(rpy, order=order)

        position = data_dict[name]["Position"]
        position.columns = position.columns.str.lower()
        orientation = pd.DataFrame(q,
                                   index=position.index,
                                   columns=["w", "x", "y", "z"])
        data_dict[name] = pd.concat(
            {
                "position": position,
                "orientation": orientation
            },
            axis=1,
        )

    # return in requested format
    if import_format == "pandas":
        return data_dict

    elif import_format == "numpy":
        return {
            k: (v["position"].values, v["orientation"].values, v.index.values)
            for k, v in data_dict.items()
        }

    elif import_format == "xarray":
        import xarray as xr

        for name in data_dict:
            position = xr.DataArray(
                data_dict[name]["position"],
                name="position",
                dims=("time", "cartesian_axis"),
            )
            orientation = xr.DataArray(
                data_dict[name].orientation,
                name="orientation",
                dims=("time", "quaternion_axis"),
            )
            data_dict[name] = xr.merge((position, orientation))

        return data_dict

    else:
        raise ValueError(f"Unsupported import format: {import_format}")
Beispiel #13
0
    def test_from_euler_angles(self):
        """"""
        rpy_r = (np.pi / 2, 0, 0)
        q_r = (np.sqrt(2) / 2, np.sqrt(2) / 2, 0, 0)
        q_rp = (0.5, 0.5, 0.5, 0.5)
        q_ypr = (np.sqrt(2) / 2, 0, np.sqrt(2) / 2, 0)

        # single rpy
        q = from_euler_angles(rpy_r)
        np.testing.assert_almost_equal(q, q_r)

        # rpy array
        q = from_euler_angles(np.tile(rpy_r, (10, 1)))
        np.testing.assert_almost_equal(q, np.tile(q_r, (10, 1)))

        # different axis
        q = from_euler_angles(np.tile(rpy_r, (10, 1)).T, axis=0)
        np.testing.assert_almost_equal(q, np.tile(q_r, (10, 1)).T)

        # single roll
        q = from_euler_angles(roll=np.pi / 2)
        np.testing.assert_almost_equal(q, q_r)

        # single roll, pitch & yaw
        q = from_euler_angles(roll=np.pi / 2, pitch=np.pi / 2)
        np.testing.assert_almost_equal(q, q_rp)

        # single roll, pitch, yaw + different order
        q = from_euler_angles(roll=np.pi / 2,
                              pitch=np.pi / 2,
                              yaw=np.pi / 2,
                              order="ypr")
        np.testing.assert_almost_equal(q, q_ypr)

        # no input
        with pytest.raises(ValueError):
            from_euler_angles()

        # rpy and roll together
        with pytest.raises(ValueError):
            from_euler_angles(rpy_r, roll=np.pi / 2)

        # inconsistent shapes
        with pytest.raises(ValueError):
            from_euler_angles(roll=np.ones(10), pitch=1)