You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

evo

Package Overview
Dependencies
Maintainers
1
Versions
117
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

evo - pypi Package Compare versions

Comparing version
1.30.6
to
1.31.0
+1
-1
evo/__init__.py

@@ -11,3 +11,3 @@ import logging

__version__ = "v1.30.6"
__version__ = "v1.31.0"

@@ -14,0 +14,0 @@

@@ -331,2 +331,23 @@ # -*- coding: UTF8 -*-

def _jumps(self, dist: float) -> np.ndarray:
jumps = np.where(self.distances[1:] - self.distances[:-1] > dist)
if len(jumps[0]) == 0:
return np.array([0, self.num_poses])
return np.concatenate([[0], jumps[0] + 1, [self.num_poses]])
def split_distance_gaps(self,
dist: float) -> typing.Sequence["PosePath3D"]:
"""
Determines translation gaps in the path and splits it into multiple
paths at the gaps.
:param dist: distance threshold for gap detection in meters
"""
if self.num_poses < 2:
return [self]
jumps = self._jumps(dist)
return [
PosePath3D(poses_se3=self.poses_se3[jumps[i]:jumps[i + 1]])
for i in range(len(jumps) - 1)
]
def check(self) -> typing.Tuple[bool, dict]:

@@ -454,2 +475,58 @@ """

def split_time_gaps(self,
dt: float) -> typing.Sequence["PoseTrajectory3D"]:
"""
Determines time gaps in the trajectory and splits it into multiple
trajectories at the gaps.
:param dt: time threshold for gap detection in seconds
"""
if self.num_poses < 2:
return [self]
gaps = np.where(self.timestamps[1:] - self.timestamps[:-1] > dt)[0]
if len(gaps) == 0:
return [self]
gaps = np.concatenate([[0], gaps + 1, [self.num_poses]])
return [
PoseTrajectory3D(timestamps=self.timestamps[gaps[i]:gaps[i + 1]],
poses_se3=self.poses_se3[gaps[i]:gaps[i + 1]])
for i in range(len(gaps) - 1)
]
def split_distance_gaps(
self, dist: float) -> typing.Sequence["PoseTrajectory3D"]:
"""
Determines translation gaps in the path and splits it into multiple
trajectories at the gaps.
:param dist: distance threshold for gap detection in meters
"""
if self.num_poses < 2:
return [self]
jumps = self._jumps(dist)
return [
PoseTrajectory3D(timestamps=self.timestamps[jumps[i]:jumps[i + 1]],
poses_se3=self.poses_se3[jumps[i]:jumps[i + 1]])
for i in range(len(jumps) - 1)
]
def split_speed_outliers(
self, v_max: float) -> typing.Sequence["PoseTrajectory3D"]:
"""
Splits the trajectory into multiple trajectories at speed outliers.
Can be used for example to handle jumps due to tracking loss.
:param v_max: speed threshold for outlier detection in m/s
"""
if self.num_poses < 2:
return [self]
speeds = self.speeds
outliers = np.where(speeds > v_max)[0]
if len(outliers) == 0:
return [self]
jumps = np.concatenate([[0], outliers + 1, [self.num_poses]])
return [
PoseTrajectory3D(timestamps=self.timestamps[jumps[i]:jumps[i + 1]],
poses_se3=self.poses_se3[jumps[i]:jumps[i + 1]])
for i in range(len(jumps) - 1)
]
def check(self) -> typing.Tuple[bool, dict]:

@@ -456,0 +533,0 @@ if self.num_poses == 0:

@@ -98,4 +98,6 @@ #!/usr/bin/env python

if len(topics) == 0:
die("No topics of supported types: {}".format(" ".join(
file_interface.SUPPORTED_ROS_MSGS)))
die("Found no topics of supported types:\n\n- {}"
"\n\nIf you want to load TF trajectories, "
"specify them like: /tf:map.base_link".format(
"\n- ".join(file_interface.SUPPORTED_ROS_MSGS)))
else:

@@ -102,0 +104,0 @@ topics = args.topics

@@ -261,5 +261,7 @@ # -*- coding: UTF8 -*-

def read_bag_trajectory(reader: typing.Union[Rosbag1Reader,
Rosbag2Reader], topic: str,
cache_tf_tree: bool = False) -> PoseTrajectory3D:
def read_bag_trajectory(
reader: typing.Union[Rosbag1Reader, Rosbag2Reader], topic: str,
cache_tf_tree: bool = False,
cache_hash_source: tf_id.HashSource = tf_id.HashSource.READER_INSTANCE
) -> PoseTrajectory3D:
"""

@@ -272,2 +274,4 @@ :param reader: opened bag reader (rosbags.rosbag2 or rosbags.rosbag1)

the same reader.
:param cache_hash_source: Determines whether to cache per reader instance
(default) or per bag filename (e.g. if a bag is opened multiple times).
:return: trajectory.PoseTrajectory3D

@@ -284,3 +288,4 @@ """

from evo.tools import tf_cache
tf_tree_cache = (tf_cache.instance(reader.__hash__())
tf_tree_cache = (tf_cache.instance(
tf_id.hash_bag(reader, cache_hash_source))
if cache_tf_tree else tf_cache.TfCache())

@@ -287,0 +292,0 @@ return tf_tree_cache.get_trajectory(reader, identifier=topic)

@@ -686,11 +686,13 @@ # -*- coding: UTF8 -*-

def trajectories(fig: Figure, trajectories: typing.Union[
trajectory.PosePath3D, typing.Sequence[trajectory.PosePath3D],
typing.Dict[str, trajectory.PosePath3D]], plot_mode=PlotMode.xy,
title: str = "", subplot_arg: int = 111,
plot_start_end_markers: bool = False,
def trajectories(fig_or_ax: typing.Union[Figure, Axes],
trajectories: typing.Union[
trajectory.PosePath3D,
typing.Sequence[trajectory.PosePath3D],
typing.Dict[str, trajectory.PosePath3D]],
plot_mode=PlotMode.xy, title: str = "",
subplot_arg: int = 111, plot_start_end_markers: bool = False,
length_unit: Unit = Unit.meters) -> None:
"""
high-level function for plotting multiple trajectories
:param fig: matplotlib figure
:param fig: matplotlib figure, or maptplotlib axes
:param trajectories: instance or container of PosePath3D or derived

@@ -706,3 +708,6 @@ - if it's a dictionary, the keys (names) will be used as labels

"""
ax = prepare_axis(fig, plot_mode, subplot_arg, length_unit)
if isinstance(fig_or_ax, Axes):
ax = fig_or_ax
else:
ax = prepare_axis(fig_or_ax, plot_mode, subplot_arg, length_unit)
if title:

@@ -709,0 +714,0 @@ ax.set_title(title)

@@ -21,4 +21,9 @@ """

import enum
import re
from typing import Union
from rosbags.rosbag1.reader import Reader as Rosbag1Reader
from rosbags.rosbag2.reader import Reader as Rosbag2Reader
from evo import EvoException

@@ -33,2 +38,23 @@

@enum.unique
class HashSource(enum.Enum):
"""
For choosing the source of the hash value when hashing a rosbag reader.
See hash_bag() for usage.
"""
READER_INSTANCE = "reader_instance"
BAG_FILENAME = "filename"
def hash_bag(reader: Union[Rosbag1Reader, Rosbag2Reader],
hash_source: HashSource) -> int:
"""
Convenience function to hash a rosbag reader instance or its filename,
for using it as a key to tf_cache.instance()
"""
if hash_source == HashSource.BAG_FILENAME:
return hash(reader.path)
return hash(reader)
def split_id(identifier: str) -> tuple:

@@ -35,0 +61,0 @@ tf_topic, _, identifier = identifier.partition(":")

Metadata-Version: 2.4
Name: evo
Version: 1.30.6
Version: 1.31.0
Summary: Python package for the evaluation of odometry and SLAM

@@ -5,0 +5,0 @@ Project-URL: Homepage, https://github.com/MichaelGrupp/evo

@@ -195,3 +195,27 @@ #!/usr/bin/env python

def test_split_distance_gaps(self):
"""
Checks if the path can be split correctly at a distance threshold.
"""
path = PosePath3D(poses_se3=[
lie.se3(r=np.eye(3), t=np.array([0, 0, 0])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 1])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 2])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 4])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 7])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 8])),
])
splits = path.split_distance_gaps(1.5)
self.assertEqual(len(splits), 3)
self.assertEqual(splits[0].num_poses, 3)
self.assertEqual(splits[0].path_length, 2.)
self.assertEqual(splits[1].num_poses, 1)
self.assertEqual(splits[1].path_length, 0.)
self.assertEqual(splits[2].num_poses, 2)
self.assertEqual(splits[2].path_length, 1.)
splits = path.split_distance_gaps(999.)
self.assertEqual(len(splits), 1)
self.assertEqual(splits[0].num_poses, path.num_poses)
class TestPoseTrajectory3D(unittest.TestCase):

@@ -249,3 +273,31 @@ def test_equals(self):

def test_split_time_gaps(self):
"""
Checks if the trajectory can be split correctly with a time threshold.
"""
traj = helpers.fake_trajectory(5, 1.)
traj.timestamps[2:5] += 10.
traj.timestamps[4] += 10.
splits = traj.split_time_gaps(5.)
self.assertEqual(len(splits), 3)
self.assertEqual(splits[0].num_poses, 2)
self.assertEqual(splits[1].num_poses, 2)
self.assertEqual(splits[2].num_poses, 1)
def test_split_speed_outliers(self):
"""
Checks if the trajectory can be split correctly with a speed threshold.
"""
traj = PoseTrajectory3D(
timestamps=np.array([0, 1, 2]), poses_se3=[
lie.se3(r=np.eye(3), t=np.array([0, 0, 0])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 1])),
lie.se3(r=np.eye(3), t=np.array([0, 0, 4])),
])
splits = traj.split_speed_outliers(2.)
self.assertEqual(len(splits), 2)
self.assertEqual(splits[0].num_poses, 2)
self.assertEqual(splits[1].num_poses, 1)
class TestTrajectoryAlignment(unittest.TestCase):

@@ -252,0 +304,0 @@ def test_se3_alignment(self):

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
# Fixing random state for reproducibility
np.random.seed(19680801)
mpl.rcParams.update({
# NOTE: don't call tight_layout manually anymore. See warning here:
# https://matplotlib.org/stable/users/explain/axes/constrainedlayout_guide.html
"figure.constrained_layout.use": True,
#"savefig.bbox": "tight",
})
def randrange(n, vmin, vmax):
"""
Helper function to make an array of random numbers having shape (n, )
with each number distributed Uniform(vmin, vmax).
"""
return (vmax - vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
n = 100
# For each set of style and range settings, plot n random points in the box
# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh].
for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.savefig("bla.png")
#plt.show()

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

import matplotlib.pyplot as plt
import numpy as np
# Fixing random state for reproducibility
np.random.seed(19680801)
def randrange(n, vmin, vmax):
"""
Helper function to make an array of random numbers having shape (n, )
with each number distributed Uniform(vmin, vmax).
"""
return (vmax - vmin)*np.random.rand(n) + vmin
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
n = 100
# For each set of style and range settings, plot n random points in the box
# defined by x in [23, 32], y in [0, 100], z in [zlow, zhigh].
for m, zlow, zhigh in [('o', -50, -25), ('^', -30, -5)]:
xs = randrange(n, 23, 32)
ys = randrange(n, 0, 100)
zs = randrange(n, zlow, zhigh)
ax.scatter(xs, ys, zs, marker=m)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.show()

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet