diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..da44a304f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +rpg_trajectory_evaluation.egg-info +__pycache__ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index a6bf3d0f6..000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.8.3) -project(rpg_trajectory_evaluation) - -find_package(catkin_simple REQUIRED) -catkin_simple() - -catkin_python_setup() - -cs_install() -cs_export() diff --git a/README.md b/README.md index 42173ad14..c34bef679 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ Zichao Zhang, Davide Scaramuzza: A Tutorial on Quantitative Trajectory Evaluatio 6. [Credits](#credits) ## Install -The package is written in python and tested in Ubuntu 16.04 and 18.04. -Currently only `python2` is supported. -The package can be used as a ROS package as well as a standalone tool. -To use it as a ROS package, simply clone it into your workspace. -It only depends on [`catkin_simple`](https://github.com/catkin/catkin_simple) to build. + +Install the package: +``` +pip install -e . +``` **Dependencies**: You will need install the following: @@ -100,22 +100,21 @@ Currently `eval_cfg.yaml` specifies two parameters for trajectory alignment (use **If this file does not exist, analysis be done for all the poses in `stamped_traj_estimate.txt`.** - ## Run the Evaluation We can run the evaluation on a single estimate result or for multiple algorithms and datasets. ### Single trajectory estimate -As a ROS package, run +To evaluate a single trajectory estimate, run: ``` -rosrun rpg_trajectory_evaluation analyze_trajectory_single.py +python3 scripts/analyze_trajectory_single.pyy ``` -or as a standalone package, run +or ``` -python2 analyze_trajectory_single.py +analyze_trajectory_single.pyy ``` `` should contain the groundtruth, trajectory estimate and optionally the evaluation configuration as mentioned above. @@ -132,7 +131,7 @@ For multiple trials, the result for trial `n` will have the corresponding suffix As an example, after executing: ``` -python2 scripts/analyze_trajectory_single.py results/euroc_mono_stereo/laptop/vio_mono/laptop_vio_mono_MH_05 +python3 scripts/analyze_trajectory_single.py results/euroc_mono_stereo/laptop/vio_mono/laptop_vio_mono_MH_05 ``` you will find the following in `plots`: @@ -155,16 +154,10 @@ The mapping from the `est_type` to file names (i.e., `stamped_*.txt`) is defined ### Multiple trajectory estimates -Similar to the case of single trajectory evaluation, for ROS, run - -``` -rosrun rpg_trajectory_evaluation analyze_trajectories.py \ - euroc_vislam_mono.yaml --output_dir=./results/euroc_vislam_mono --results_dir=./results/euroc_vislam_mono --platform laptop --odometry_error_per_dataset --plot_trajectories --rmse_table --rmse_boxplot --mul_trials=10 -``` -otherwise, run +Similar to the case of single trajectory evaluation, run ``` -python2 scripts/analyze_trajectories.py \ +python3 scripts/analyze_trajectories.py \ euroc_vislam_mono.yaml --output_dir=./results/euroc_vislam_mono --results_dir=./results/euroc_vislam_mono --platform laptop --odometry_error_per_dataset --plot_trajectories --rmse_table --rmse_boxplot --mul_trials=10 ``` diff --git a/package.xml b/package.xml deleted file mode 100644 index 0b28b700f..000000000 --- a/package.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - rpg_trajectory_evaluation - 0.0.0 - rpg_trajectory_evaluation - - Zichao Zhang - Zichao Zhang - Jonathan Huber - Jeffrey Delmerico - Christian Forster - MIT - - catkin - catkin_simple - - - diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100755 index 000000000..e69de29bb diff --git a/scripts/add_path.py b/scripts/add_path.py old mode 100644 new mode 100755 index f7fe5f7ea..94fecdce6 --- a/scripts/add_path.py +++ b/scripts/add_path.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import sys diff --git a/scripts/analyze_trajectories.py b/scripts/analyze_trajectories.py index 17906be49..861fd5888 100755 --- a/scripts/analyze_trajectories.py +++ b/scripts/analyze_trajectories.py @@ -1,23 +1,22 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 -import os import argparse -from ruamel.yaml import YAML -import shutil import json +import os +import shutil from datetime import datetime -import numpy as np -import matplotlib.pyplot as plt -from matplotlib import rc -from colorama import init, Fore - import add_path -from trajectory import Trajectory +import matplotlib.pyplot as plt +import numpy as np import plot_utils as pu import results_writer as res_writer from analyze_trajectory_single import analyze_multiple_trials -from fn_constants import kNsToEstFnMapping, kNsToMatchFnMapping, kFnExt +from colorama import Fore, init +from fn_constants import kFnExt, kNsToEstFnMapping, kNsToMatchFnMapping +from matplotlib import rc +from ruamel.yaml import YAML +from trajectory import Trajectory init(autoreset=True) @@ -26,11 +25,7 @@ FORMAT = '.pdf' -def spec(N): - t = np.linspace(-510, 510, N) - return np.round(np.clip(np.stack([-t, 510-np.abs(t), t], axis=1), 0, 255)).astype("float32")/255 - -PALLETE = spec(20) +PALLETE = ['b', 'g', 'r', 'c', 'k', 'y', 'm'] def collect_odometry_error_per_dataset(dataset_multierror_list, @@ -545,36 +540,38 @@ def parse_config_file(config_fn, sort_names): print("#####################################") print(Fore.RED+">>> Processing absolute trajectory errors...") if args.rmse_table: - rmse_table = {} - rmse_table['values'] = [] - for config_mt_error in config_multierror_list: - cur_trans_rmse = [] - for mt_error_d in config_mt_error: - print("> Processing {0}".format(mt_error_d.uid)) - if args.rmse_median_only or n_trials == 1: - cur_trans_rmse.append( - "{:3.3f}".format( - mt_error_d.abs_errors['rmse_trans_stats']['median'])) - else: - cur_trans_rmse.append( - "{:3.3f}, {:3.3f} ({:3.3f} - {:3.3f})".format( - mt_error_d.abs_errors['rmse_trans_stats']['mean'], - mt_error_d.abs_errors['rmse_trans_stats']['median'], - mt_error_d.abs_errors['rmse_trans_stats']['min'], - mt_error_d.abs_errors['rmse_trans_stats']['max'])) - rmse_table['values'].append(cur_trans_rmse) - rmse_table['rows'] = algorithms - rmse_table['cols'] = datasets - print('\n--- Generating RMSE tables... ---') - res_writer.write_tex_table( - rmse_table['values'], rmse_table['rows'], rmse_table['cols'], - os.path.join(output_dir, args.platform + '_translation_rmse_' + - eval_uid+'.txt')) + for absMetricKey in ['rmse_trans', 'rmse_rot', 'rmse_scale']: + rmse_table = {} + rmse_table['values'] = [] + for config_mt_error in config_multierror_list: + cur_rmse = [] + for mt_error_d in config_mt_error: + print("> Processing {0}".format(mt_error_d.uid)) + if args.rmse_median_only or n_trials == 1: + cur_rmse.append( + "{:3.3f}".format( + mt_error_d.abs_errors[absMetricKey + '_stats']['median'])) + else: + cur_rmse.append( + "{:3.3f}, {:3.3f} ({:3.3f} - {:3.3f})".format( + mt_error_d.abs_errors[absMetricKey + '_stats']['mean'], + mt_error_d.abs_errors[absMetricKey + '_stats']['median'], + mt_error_d.abs_errors[absMetricKey + '_stats']['min'], + mt_error_d.abs_errors[absMetricKey + '_stats']['max'])) + rmse_table['values'].append(cur_rmse) + rmse_table['rows'] = algorithms + rmse_table['cols'] = datasets + print('\n--- Generating RMSE tables... ---') + res_writer.write_tex_table( + rmse_table['values'], rmse_table['rows'], rmse_table['cols'], + os.path.join(output_dir, args.platform + '_' + absMetricKey + '_' + + eval_uid+'.txt')) if args.rmse_boxplot and n_trials > 1: rmse_plot_alg = [v for v in algorithms] algorithm_rmse = collect_rmse_per_dataset(config_multierror_list, rmse_plot_alg) + res_writer.save_algorithm_rmse(algorithm_rmse, datasets, algorithms, n_trials, output_dir) print("--- Generate boxplot for RMSE ---") plot_rmse_per_dataset(algorithm_rmse, datasets, algorithms, output_dir, plot_settings) diff --git a/scripts/analyze_trajectory_single.py b/scripts/analyze_trajectory_single.py index d7788afb8..38fb08960 100755 --- a/scripts/analyze_trajectory_single.py +++ b/scripts/analyze_trajectory_single.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import argparse diff --git a/scripts/dataset_tools/asl_groundtruth_to_pose.py b/scripts/dataset_tools/asl_groundtruth_to_pose.py index 3daa8d543..014774490 100755 --- a/scripts/dataset_tools/asl_groundtruth_to_pose.py +++ b/scripts/dataset_tools/asl_groundtruth_to_pose.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import argparse diff --git a/scripts/dataset_tools/bag_to_pose.py b/scripts/dataset_tools/bag_to_pose.py index 67390cec2..786f0ede0 100755 --- a/scripts/dataset_tools/bag_to_pose.py +++ b/scripts/dataset_tools/bag_to_pose.py @@ -1,54 +1,62 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 -import os import argparse - -import rosbag - - -def extract(bagfile, pose_topic, msg_type, out_filename): - n = 0 - f = open(out_filename, 'w') - f.write('# timestamp tx ty tz qx qy qz qw\n') - with rosbag.Bag(bagfile, 'r') as bag: - for (topic, msg, ts) in bag.read_messages(topics=str(pose_topic)): - if msg_type == "PoseWithCovarianceStamped": - f.write('%.12f %.12f %.12f %.12f %.12f %.12f %.12f %.12f\n' % - (msg.header.stamp.to_sec(), - msg.pose.pose.position.x, msg.pose.pose.position.y, - msg.pose.pose.position.z, - msg.pose.pose.orientation.x, - msg.pose.pose.orientation.y, - msg.pose.pose.orientation.z, - msg.pose.pose.orientation.w)) - elif msg_type == "PoseStamped": - f.write('%.12f %.12f %.12f %.12f %.12f %.12f %.12f %.12f\n' % - (msg.header.stamp.to_sec(), - msg.pose.position.x, msg.pose.position.y, - msg.pose.position.z, - msg.pose.orientation.x, msg.pose.orientation.y, - msg.pose.orientation.z, msg.pose.orientation.w)) - else: - assert False, "Unknown message type" - n += 1 - print('wrote ' + str(n) + ' imu messages to the file: ' + out_filename) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=''' - Extracts IMU messages from bagfile. - ''') - parser.add_argument('bag', help='Bagfile') - parser.add_argument('topic', help='Topic') - parser.add_argument('--msg_type', default='PoseStamped', - help='message type') - parser.add_argument('--output', default='stamped_poses.txt', - help='output filename') +import os +from pathlib import Path + +from rosbags.highlevel import AnyReader + + +def extract(bag_path, pose_topic, msg_type_str, out_filename): + with AnyReader([Path(bag_path)]) as reader: + connections = [c for c in reader.connections if c.topic == pose_topic] + if not connections: + raise RuntimeError(f"Topic {pose_topic} not found in bag.") + + with open(out_filename, 'w') as f: + f.write('# timestamp tx ty tz qx qy qz qw\n') + n = 0 + + for conn, timestamp, rawdata in reader.messages(connections=connections): + msg = reader.deserialize(rawdata, conn.msgtype) + stamp = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9 + + if msg_type_str == "PoseWithCovarianceStamped": + p = msg.pose.pose.position + o = msg.pose.pose.orientation + elif msg_type_str == "PoseStamped": + p = msg.pose.position + o = msg.pose.orientation + elif msg_type_str == "TransformStamped": + p = msg.transform.translation + o = msg.transform.rotation + elif msg_type_str == "Odometry": + p = msg.pose.pose.position + o = msg.pose.pose.orientation + else: + raise RuntimeError("Unexpected message type at runtime") + + f.write(f'{stamp:.12f} {p.x:.12f} {p.y:.12f} {p.z:.12f} ' + f'{o.x:.12f} {o.y:.12f} {o.z:.12f} {o.w:.12f}\n') + n += 1 + + print(f"Wrote {n} messages to {out_filename}") + + +def main(): + parser = argparse.ArgumentParser(description='Extract pose messages from a bag file (ROS 2 format, no ROS deps).') + parser.add_argument('bag', help='Path to ROS 2 bag folder (.db3 or .mcap)') + parser.add_argument('topic', help='Pose topic to extract') + parser.add_argument('--msg_type', default='PoseStamped', help='Message type') + parser.add_argument('--output', default='stamped_poses.txt', help='Output filename') args = parser.parse_args() out_dir = os.path.dirname(os.path.abspath(args.bag)) out_fn = os.path.join(out_dir, args.output) - print('Extract pose from bag '+args.bag+' in topic ' + args.topic) - print('Saving to file '+out_fn) + print(f"Extracting from {args.bag}, topic: {args.topic}, type: {args.msg_type}") extract(args.bag, args.topic, args.msg_type, out_fn) + + +if __name__ == '__main__': + main() diff --git a/scripts/dataset_tools/stamp_state_est.py b/scripts/dataset_tools/stamp_state_est.py index 00b202aca..69701267b 100755 --- a/scripts/dataset_tools/stamp_state_est.py +++ b/scripts/dataset_tools/stamp_state_est.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import argparse diff --git a/scripts/dataset_tools/stamp_state_est_using_matches.py b/scripts/dataset_tools/stamp_state_est_using_matches.py index 4bf4a7e8f..334c3b3c2 100755 --- a/scripts/dataset_tools/stamp_state_est_using_matches.py +++ b/scripts/dataset_tools/stamp_state_est_using_matches.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import argparse diff --git a/scripts/dataset_tools/strip_gt_id.py b/scripts/dataset_tools/strip_gt_id.py index e1681b634..100a8e878 100755 --- a/scripts/dataset_tools/strip_gt_id.py +++ b/scripts/dataset_tools/strip_gt_id.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import argparse diff --git a/scripts/dataset_tools/transform_trajectory.py b/scripts/dataset_tools/transform_trajectory.py index 98325c921..bf4691da4 100755 --- a/scripts/dataset_tools/transform_trajectory.py +++ b/scripts/dataset_tools/transform_trajectory.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Wed Apr 18 11:16:38 2018 diff --git a/scripts/fn_constants.py b/scripts/fn_constants.py index 9428b9aab..2ae40db9b 100755 --- a/scripts/fn_constants.py +++ b/scripts/fn_constants.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 kNsToEstFnMapping = {'traj_est': 'stamped_traj_estimate', 'pose_graph': 'stamped_pose_graph_estimate', diff --git a/scripts/overall_odometry_errors.py b/scripts/overall_odometry_errors.py old mode 100644 new mode 100755 index 96ca5d2ef..fdbeab1bc --- a/scripts/overall_odometry_errors.py +++ b/scripts/overall_odometry_errors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import numpy as np diff --git a/setup.py b/setup.py index e854cfc49..1f95e7eef 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,33 @@ -#!/usr/bin/env python +from setuptools import find_packages, setup -from distutils.core import setup -from catkin_pkg.python_setup import generate_distutils_setup - -d = generate_distutils_setup( - packages=['rpg_trajectory_evaluation'], +setup( + name='rpg_trajectory_evaluation', + version='0.1.0', + packages=find_packages(where='src'), package_dir={'': 'src'}, - install_requires=['rospkg'] - ) - -setup(**d) + scripts=[ + 'scripts/add_path.py', + 'scripts/analyze_trajectories.py', + 'scripts/analyze_trajectory_single.py', + 'scripts/change_eval_cfg_recursive.py', + 'scripts/fn_constants.py', + 'scripts/overall_odometry_errors.py', + 'scripts/recursive_clean_results_dir.py', + 'scripts/dataset_tools/asl_groundtruth_to_pose.py', + 'scripts/dataset_tools/bag_to_pose.py', + 'scripts/dataset_tools/stamp_state_est.py', + 'scripts/dataset_tools/stamp_state_est_using_matches.py', + 'scripts/dataset_tools/strip_gt_id.py', + 'scripts/dataset_tools/transform_trajectory.py', + ], + install_requires=[ + 'matplotlib', + 'numpy', + 'scipy', + 'PyYAML', + 'rosbags' + ], + python_requires='>=3.6', + include_package_data=True, + zip_safe=False, +) diff --git a/src/rpg_trajectory_evaluation/align_trajectory.py b/src/rpg_trajectory_evaluation/align_trajectory.py index 91c73a971..035bc890b 100644 --- a/src/rpg_trajectory_evaluation/align_trajectory.py +++ b/src/rpg_trajectory_evaluation/align_trajectory.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- import numpy as np diff --git a/src/rpg_trajectory_evaluation/align_utils.py b/src/rpg_trajectory_evaluation/align_utils.py index f15e29ea9..6e3a8ea0c 100644 --- a/src/rpg_trajectory_evaluation/align_utils.py +++ b/src/rpg_trajectory_evaluation/align_utils.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- import numpy as np diff --git a/src/rpg_trajectory_evaluation/associate_timestamps.py b/src/rpg_trajectory_evaluation/associate_timestamps.py index 522e5cb6e..635fab49b 100644 --- a/src/rpg_trajectory_evaluation/associate_timestamps.py +++ b/src/rpg_trajectory_evaluation/associate_timestamps.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # adapted from # https://svncvpr.in.tum.de/cvpr-ros-pkg/trunk/rgbd_benchmark/rgbd_benchmark_tools/src/rgbd_benchmark_tools/associate.py diff --git a/src/rpg_trajectory_evaluation/compute_trajectory_errors.py b/src/rpg_trajectory_evaluation/compute_trajectory_errors.py index 6e29736ed..8f74d545d 100644 --- a/src/rpg_trajectory_evaluation/compute_trajectory_errors.py +++ b/src/rpg_trajectory_evaluation/compute_trajectory_errors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import numpy as np diff --git a/src/rpg_trajectory_evaluation/metrics.py b/src/rpg_trajectory_evaluation/metrics.py index 436fdb472..521854b45 100644 --- a/src/rpg_trajectory_evaluation/metrics.py +++ b/src/rpg_trajectory_evaluation/metrics.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 kRelMetrics = ['rel_trans', 'rel_trans_perc', 'rel_yaw', 'rel_rot', 'rel_gravity', 'rel_rot_deg_per_m'] diff --git a/src/rpg_trajectory_evaluation/multiple_traj_errors.py b/src/rpg_trajectory_evaluation/multiple_traj_errors.py index b8846f0dd..99eae8495 100644 --- a/src/rpg_trajectory_evaluation/multiple_traj_errors.py +++ b/src/rpg_trajectory_evaluation/multiple_traj_errors.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import numpy as np import os diff --git a/src/rpg_trajectory_evaluation/plot_utils.py b/src/rpg_trajectory_evaluation/plot_utils.py index 146dda4af..3ef8235f3 100644 --- a/src/rpg_trajectory_evaluation/plot_utils.py +++ b/src/rpg_trajectory_evaluation/plot_utils.py @@ -1,15 +1,18 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ @author: Christian Forster """ import os -import yaml -import numpy as np -import matplotlib.pyplot as plt + import matplotlib as mpl -from mpl_toolkits.mplot3d import Axes3D +import matplotlib.lines as mlines +import matplotlib.pyplot as plt +import numpy as np +import yaml from matplotlib import rc +from mpl_toolkits.mplot3d import Axes3D + rc('font', **{'family': 'serif', 'serif': ['Cardo']}) rc('text', usetex=True) @@ -42,8 +45,8 @@ def boxplot_compare(ax, xlabels, # print("Positions: {0}".format(positions)) bp = ax.boxplot(d, 0, '', positions=positions, widths=widths) color_box(bp, data_colors[idx]) - tmp, = plt.plot([1, 1], c=data_colors[idx], alpha=0) - leg_handles.append(tmp) + leg_line = mlines.Line2D([], [], color=data_colors[idx]) + leg_handles.append(leg_line) leg_labels.append(data_labels[idx]) idx += 1 diff --git a/src/rpg_trajectory_evaluation/results_writer.py b/src/rpg_trajectory_evaluation/results_writer.py index a8b842e74..79fd34695 100644 --- a/src/rpg_trajectory_evaluation/results_writer.py +++ b/src/rpg_trajectory_evaluation/results_writer.py @@ -1,7 +1,8 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os -import yaml + import numpy as np +import yaml def compute_statistics(data_vec): @@ -73,3 +74,31 @@ def write_tex_table(list_values, rows, cols, outfn): for col_idx in range(len(row_values) - 1): f.write(row_values[col_idx] + ' & ') f.write(' ' + row_values[-1]+' \n') + +def save_algorithm_rmse(algorithm_rmse, dataset_names, algorithm_names, n_trials, + output_dir): + n_data = len(dataset_names) + for v in algorithm_names: + assert len(algorithm_rmse['trans_err'][v]) == n_data + assert len(algorithm_rmse['rot_err'][v]) == n_data + + output_names = ["ate_rmse_trans.txt", "ate_rmse_rot.txt"] + keys = ["trans_err", "rot_err"] + for index, outputname in enumerate(output_names): + output_file = os.path.join(output_dir, outputname) + with open(output_file, 'w') as stream: + stream.write("#Rows correspond to algorithms") + for v in algorithm_names: + stream.write(',{}'.format(v)) + stream.write(', on {} datasets x {} trials. Missing RMSE is filled with inf.\n'.format( + n_data, n_trials)) + for v in algorithm_names: + sessiontrials = algorithm_rmse[keys[index]][v] + for session in sessiontrials: + pad = n_trials - len(session) + for j in range(pad): + stream.write(' inf') + for trial in session: + stream.write(' {}'.format(trial)) + stream.write('\n') + stream.write('\n') \ No newline at end of file diff --git a/src/rpg_trajectory_evaluation/trajectory.py b/src/rpg_trajectory_evaluation/trajectory.py index a9d6a7fc5..7ae2d7526 100644 --- a/src/rpg_trajectory_evaluation/trajectory.py +++ b/src/rpg_trajectory_evaluation/trajectory.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import yaml diff --git a/src/rpg_trajectory_evaluation/trajectory_loading.py b/src/rpg_trajectory_evaluation/trajectory_loading.py index 31bf96e6a..719f8afff 100644 --- a/src/rpg_trajectory_evaluation/trajectory_loading.py +++ b/src/rpg_trajectory_evaluation/trajectory_loading.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 import os import numpy as np diff --git a/src/rpg_trajectory_evaluation/trajectory_utils.py b/src/rpg_trajectory_evaluation/trajectory_utils.py index 965a0bc62..49864b707 100644 --- a/src/rpg_trajectory_evaluation/trajectory_utils.py +++ b/src/rpg_trajectory_evaluation/trajectory_utils.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 """ @author: Christian Forster """