├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── definitions.py
├── examples
├── nclt_all_kml.png
├── nclt_all_png.png
├── nclt_tf_tree.png
└── rviz_example.gif
├── launch
├── convert.launch
├── download.launch
└── visualize.launch
├── nclt2ros
├── __init__.py
├── converter
│ ├── __init__.py
│ ├── base_convert.py
│ └── to_rosbag.py
├── downloader
│ ├── __init__.py
│ └── download.py
├── extractor
│ ├── __init__.py
│ ├── base_raw_data.py
│ ├── extract.py
│ ├── read_ground_truth.py
│ ├── read_ground_truth_covariance.py
│ └── read_sensor_data.py
├── transformer
│ ├── __init__.py
│ ├── coordinate_frame.py
│ ├── hokuyo_data.py
│ ├── image_data.py
│ ├── sensor_data.py
│ ├── velodyne_data.py
│ └── velodyne_sync_data.py
└── visualizer
│ ├── __init__.py
│ ├── all.py
│ ├── gps.py
│ ├── gps_rtk.py
│ ├── gt.py
│ ├── plotter.py
│ ├── visualize.py
│ └── wheel_odom.py
├── package.xml
├── requirements.txt
├── rviz
└── config.rviz
├── scripts
├── nclt2downloader
├── nclt2rosbag
└── nclt2visualizer
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | #project
107 | .idea/
108 | raw_data/
109 | plots/
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(nclt2ros)
3 |
4 |
5 | find_package(catkin REQUIRED COMPONENTS roscpp rospy)
6 |
7 | catkin_python_setup()
8 |
9 | catkin_package()
10 |
11 | install(PROGRAMS scripts/nclt2downloader scripts/nclt2rosbag scripts/nclt2visualizer
12 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
13 |
14 | install(DIRECTORY launch
15 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
16 |
17 | # install rviz directory
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Bierschneider Christian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # NCLT2ROS
3 |
4 | The [nclt2ros](https://github.com/bierschi/nclt2rosbag) package provides ROS nodes for
5 |
6 | - [downloading](https://github.com/bierschi/nclt2rosbag#downloadlaunch)
7 | - [extracting](https://github.com/bierschi/nclt2rosbag#downloadlaunch)
8 | - [visualizing](https://github.com/bierschi/nclt2rosbag#visualizelaunch)
9 | - [converting](https://github.com/bierschi/nclt2rosbag#convertlaunch)
10 |
11 | the data from [The University of Michigan North Campus Long-Term Vision and LIDAR Dataset.](http://robots.engin.umich.edu/nclt/)
12 |
13 |
14 | 
15 |
16 |
17 | #### Table of contents:
18 |
19 | - [Launch files](https://github.com/bierschi/nclt2ros#launch-files)
20 | - [Usage](https://github.com/bierschi/nclt2ros#usage)
21 | - [Examples](https://github.com/bierschi/nclt2ros#examples)
22 | - [Transformation tree](https://github.com/bierschi/nclt2ros#transformation-tree)
23 |
24 |
25 |
26 |
27 | ## Launch files
28 |
29 | ##### download.launch
30 | launch file for downloading the raw data
31 |
32 | ```xml
33 |
145 | mkdir -p ~/catkin_ws/src
146 | cd ~/catkin_ws/
147 | catkin_make
148 | source devel/setup.bash
149 |
150 |
151 | download and build this repository
152 |
153 | cd src
154 | git clone https://github.com/bierschi/nclt2ros.git
155 | cd ~/catkin_ws/
156 | catkin_make
157 |
158 |
159 | source the catkin workspace to access the package nclt2ros
160 |
161 | source devel/setup.bash
162 |
163 |
164 | execute specific roslaunch file
165 |
166 | roslaunch nclt2ros download.launch
167 |
168 |
169 |
170 |
171 | ## Examples
172 |
173 | #### data from date 2013-01-10
174 | visualizing as a png file
175 |
176 | utime_wheel: 302 | speed = (vl + vr) / 2.0 303 | 304 | odom.twist.twist.linear.x = speed 305 | 306 | self.last_twist = odom.twist.twist.linear.x 307 | self.i_wheel += 1 308 | 309 | elif self.init_twist: 310 | odom.twist.twist.linear.x = self.last_twist 311 | 312 | else: 313 | odom.twist.twist.linear.x = 0.0 314 | 315 | self.init_twist = True 316 | self.last_twist = odom.twist.twist.linear.x 317 | 318 | if utime > utime_kvh: 319 | odom.twist.twist.angular.z = kvh_heading 320 | self.last_twist_heading = odom.twist.twist.angular.z 321 | self.i_kvh += 1 322 | elif self.init_twist_heading: 323 | odom.twist.twist.angular.z = self.last_twist_heading 324 | 325 | else: 326 | odom.twist.twist.angular.z = 0.0 327 | self.init_twist_heading = True 328 | self.last_twist_heading = odom.twist.twist.angular.z 329 | 330 | odom.twist.twist.linear.y = 0.0 331 | odom.twist.twist.linear.z = 0.0 332 | odom.twist.twist.angular.x = 0.0 333 | odom.twist.twist.angular.y = 0.0 334 | 335 | # just an example 336 | odom.twist.covariance[0] = 0.001 337 | odom.twist.covariance[7] = 0.001 338 | odom.twist.covariance[14] = 99999.9 339 | odom.twist.covariance[21] = 99999.9 340 | odom.twist.covariance[28] = 99999.9 341 | odom.twist.covariance[35] = 0.001 342 | 343 | # broadcast odom base_link transform 344 | geo_msg = geometry_msgs.msg.TransformStamped() 345 | geo_msg.header.stamp = timestamp 346 | geo_msg.header.frame_id = wheel_odom_link 347 | geo_msg.child_frame_id = base_link 348 | geo_msg.transform.translation.x = x 349 | geo_msg.transform.translation.y = -y 350 | geo_msg.transform.translation.z = -z 351 | geo_msg.transform.rotation.x = quaternion[0] 352 | geo_msg.transform.rotation.y = quaternion[1] 353 | geo_msg.transform.rotation.z = quaternion[2] 354 | geo_msg.transform.rotation.w = quaternion[3] 355 | 356 | tf_msg = tf2_msgs.msg.TFMessage([geo_msg]) 357 | 358 | # create world odom static transformer 359 | odom_static_transform_stamped = geometry_msgs.msg.TransformStamped() 360 | odom_static_transform_stamped.header.stamp = timestamp 361 | odom_static_transform_stamped.header.frame_id = "world" 362 | odom_static_transform_stamped.child_frame_id = wheel_odom_link 363 | 364 | odom_static_transform_stamped.transform.translation.x = 0 365 | odom_static_transform_stamped.transform.translation.y = 0 366 | odom_static_transform_stamped.transform.translation.z = 0 367 | 368 | quat = tf.transformations.quaternion_from_euler(0, 0, 1.57) 369 | odom_static_transform_stamped.transform.rotation.x = quat[0] 370 | odom_static_transform_stamped.transform.rotation.y = quat[1] 371 | odom_static_transform_stamped.transform.rotation.z = quat[2] 372 | odom_static_transform_stamped.transform.rotation.w = quat[3] 373 | 374 | # publish static transform 375 | tf_static_msg = tf2_msgs.msg.TFMessage([odom_static_transform_stamped]) 376 | 377 | return odom, timestamp, tf_msg, tf_static_msg 378 | 379 | def ms25_to_imu(self, imu_list, i): 380 | """converts ms25 data to ROS imu messages 381 | 382 | :param imu_list: list containing the imu data from sensor ms25 383 | :param i: list row counter 384 | 385 | :return: fill bag with imu, mag, timestamp, tf_static_msg 386 | """ 387 | 388 | # load data from list 389 | utime = imu_list[i, 0] 390 | mag_xs = imu_list[i, 1] 391 | mag_ys = imu_list[i, 2] 392 | mag_zs = imu_list[i, 3] 393 | accel_xs = imu_list[i, 4] 394 | accel_ys = imu_list[i, 5] 395 | accel_zs = imu_list[i, 6] 396 | rot_rs = imu_list[i, 7] 397 | rot_ps = imu_list[i, 8] 398 | rot_hs = imu_list[i, 9] 399 | 400 | # create ros timestamp 401 | timestamp = rospy.Time.from_sec(utime / 1e6) 402 | 403 | # get imu and base link 404 | imu_link = self.imu_frame 405 | base_link = self.body_frame 406 | 407 | # create ros imu message 408 | imu = Imu() 409 | imu.header.stamp = timestamp 410 | imu.header.frame_id = imu_link 411 | 412 | # swap x,y due to the enu frame, negate z 413 | quaternion = tf.transformations.quaternion_from_euler( 414 | rot_ps, rot_rs, -rot_hs 415 | ) 416 | 417 | # alternative 418 | #quaternion = tf.transformations.quaternion_from_euler( 419 | # rot_rs, -rot_ps, -rot_hs 420 | #) 421 | 422 | imu.orientation.x = quaternion[0] 423 | imu.orientation.y = quaternion[1] 424 | imu.orientation.z = quaternion[2] 425 | imu.orientation.w = quaternion[3] 426 | 427 | # swap x,y due to the enu frame, negate z 428 | imu.angular_velocity.x = rot_ps # rot_rs 429 | imu.angular_velocity.y = rot_rs # -rot_ps 430 | imu.angular_velocity.z = -rot_hs 431 | imu.angular_velocity_covariance = IMU_VEL_COVAR 432 | 433 | # swap x,y due to the enu frame, negate z 434 | imu.linear_acceleration.x = accel_ys # accel_xs 435 | imu.linear_acceleration.y = accel_xs # -accel_ys 436 | imu.linear_acceleration.z = -accel_zs 437 | imu.linear_acceleration_covariance = IMU_ACCEL_COVAR 438 | 439 | # enu frame 440 | mag = MagneticField() 441 | mag.header.stamp = timestamp 442 | mag.magnetic_field.x = mag_ys 443 | mag.magnetic_field.y = mag_xs 444 | mag.magnetic_field.z = -mag_zs 445 | 446 | # create base_link imu static transformer 447 | imu_static_transform_stamped = geometry_msgs.msg.TransformStamped() 448 | imu_static_transform_stamped.header.stamp = timestamp 449 | imu_static_transform_stamped.header.frame_id = base_link 450 | imu_static_transform_stamped.child_frame_id = imu_link 451 | 452 | imu_static_transform_stamped.transform.translation.x = -0.11 453 | imu_static_transform_stamped.transform.translation.y = 0.18 454 | imu_static_transform_stamped.transform.translation.z = 0.71 455 | 456 | quat = tf.transformations.quaternion_from_euler(0, 0, 0) 457 | imu_static_transform_stamped.transform.rotation.x = quat[0] 458 | imu_static_transform_stamped.transform.rotation.y = quat[1] 459 | imu_static_transform_stamped.transform.rotation.z = quat[2] 460 | imu_static_transform_stamped.transform.rotation.w = quat[3] 461 | 462 | # publish static transform 463 | tf_static_msg = tf2_msgs.msg.TFMessage([imu_static_transform_stamped]) 464 | 465 | return imu, mag, timestamp, tf_static_msg 466 | 467 | def gt_to_odometry(self, gt_list, gt_cov_list, i): 468 | """converts ground_truth odometry to ROS odometry messages 469 | 470 | :param gt_list: list containing the ground truth data 471 | :param i: row counter 472 | 473 | :return: fill bag with gt, timestamp, tf_static_msg 474 | """ 475 | 476 | # load data from list 477 | utime = gt_list[i, 0] 478 | x = gt_list[i, 1] 479 | y = gt_list[i, 2] 480 | z = gt_list[i, 3] 481 | roll_rad = gt_list[i, 4] 482 | pitch_rad = gt_list[i, 5] 483 | yaw_rad = gt_list[i, 6] 484 | 485 | # get upper diagonal of the covariance matrix 486 | xx = gt_cov_list[self.i_gt, 1]; xy = gt_cov_list[self.i_gt, 2]; xz = gt_cov_list[self.i_gt, 3]; xr = gt_cov_list[self.i_gt, 4] 487 | xp = gt_cov_list[self.i_gt, 5]; xh = gt_cov_list[self.i_gt, 6]; yy = gt_cov_list[self.i_gt, 7]; yz = gt_cov_list[self.i_gt, 8] 488 | yr = gt_cov_list[self.i_gt, 9]; yp = gt_cov_list[self.i_gt, 10]; yh = gt_cov_list[self.i_gt, 11]; zz = gt_cov_list[self.i_gt, 12] 489 | zr = gt_cov_list[self.i_gt, 13]; zp = gt_cov_list[self.i_gt, 14]; zh = gt_cov_list[self.i_gt, 15]; rr = gt_cov_list[self.i_gt, 16] 490 | rp = gt_cov_list[self.i_gt, 17]; rh = gt_cov_list[self.i_gt, 18]; pp = gt_cov_list[self.i_gt, 19]; ph = gt_cov_list[self.i_gt, 20] 491 | hh = gt_cov_list[self.i_gt, 21] 492 | 493 | # load utime from gt_cov list 494 | utime_gt_cov = gt_cov_list[self.i_gt, 0] 495 | 496 | # create ros timestamp 497 | timestamp = rospy.Time.from_sec(utime / 1e6) 498 | 499 | # get ground truth link 500 | gt_link = str(self.ground_truth_frame) 501 | 502 | # create odometry message for ground truth 503 | gt = Odometry() 504 | gt.header.stamp = timestamp 505 | gt.header.frame_id = gt_link 506 | # odom.child_frame_id = 'base_link' 507 | 508 | # change due to the ENU frame 509 | gt.pose.pose.position.x = y - 107.724666286 # x # TODO not hardcoding 510 | gt.pose.pose.position.y = x - 75.829339527800 # y 511 | gt.pose.pose.position.z = -z + 3.272894625813646 # z 512 | 513 | # create quaternion from euler angles 514 | quaternion = tf.transformations.quaternion_from_euler(roll_rad, pitch_rad, yaw_rad) 515 | gt.pose.pose.orientation.x = quaternion[0] 516 | gt.pose.pose.orientation.y = quaternion[1] 517 | gt.pose.pose.orientation.z = quaternion[2] 518 | gt.pose.pose.orientation.w = quaternion[3] 519 | 520 | # fill covariance matrices 521 | if utime > utime_gt_cov: 522 | gt.pose.covariance[0] = xx; gt.pose.covariance[1] = xy; gt.pose.covariance[2] = xz; gt.pose.covariance[3] = xr 523 | gt.pose.covariance[4] = xp; gt.pose.covariance[5] = xh 524 | 525 | gt.pose.covariance[7] = yy; gt.pose.covariance[8] = yz; gt.pose.covariance[9] = yr; gt.pose.covariance[10] = yp 526 | gt.pose.covariance[11] = yh 527 | 528 | gt.pose.covariance[14] = zz; gt.pose.covariance[15] = zr; gt.pose.covariance[16] = zp; gt.pose.covariance[17] = zh 529 | 530 | gt.pose.covariance[21] = rr; gt.pose.covariance[22] = rp; gt.pose.covariance[23] = rh 531 | 532 | gt.pose.covariance[28] = pp; gt.pose.covariance[29] = ph 533 | 534 | gt.pose.covariance[35] = hh 535 | 536 | self.last_gt_cov = gt.pose.covariance 537 | self.i_gt += 1 538 | 539 | elif self.init_gt_cov: 540 | gt.pose.covariance = self.last_gt_cov 541 | 542 | else: 543 | gt.pose.covariance = POSE_COVAR 544 | 545 | self.init_gt_cov = True 546 | self.last_gt_cov = gt.pose.covariance 547 | 548 | # TODO fill TWIST 549 | gt.twist.twist.linear.x = 0.0 550 | gt.twist.twist.linear.y = 0.0 551 | gt.twist.twist.linear.z = 0.0 552 | gt.twist.twist.angular.x = 0.0 553 | gt.twist.twist.angular.y = 0.0 554 | gt.twist.twist.angular.z = 0.0 555 | 556 | # create world ground truth static transformer 557 | gt_static_transform_stamped = geometry_msgs.msg.TransformStamped() 558 | gt_static_transform_stamped.header.stamp = timestamp 559 | gt_static_transform_stamped.header.frame_id = "world" 560 | gt_static_transform_stamped.child_frame_id = gt_link 561 | 562 | gt_static_transform_stamped.transform.translation.x = 0 563 | gt_static_transform_stamped.transform.translation.y = 0 564 | gt_static_transform_stamped.transform.translation.z = 0 565 | 566 | quat = tf.transformations.quaternion_from_euler(0, 0, 0) 567 | gt_static_transform_stamped.transform.rotation.x = quat[0] 568 | gt_static_transform_stamped.transform.rotation.y = quat[1] 569 | gt_static_transform_stamped.transform.rotation.z = quat[2] 570 | gt_static_transform_stamped.transform.rotation.w = quat[3] 571 | 572 | # publish static transform 573 | tf_static_msg = tf2_msgs.msg.TFMessage([gt_static_transform_stamped]) 574 | 575 | return gt, timestamp, tf_static_msg 576 | -------------------------------------------------------------------------------- /nclt2ros/transformer/velodyne_data.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import numpy as np 3 | import rospy 4 | 5 | from sensor_msgs.msg import PointCloud2, PointField 6 | from nclt2ros.extractor.base_raw_data import BaseRawData 7 | from nclt2ros.converter.base_convert import BaseConvert 8 | 9 | 10 | class VelodyneData(BaseRawData, BaseConvert): 11 | """Class to convert the velodyne binary file to ROS PointCloud2 messages 12 | 13 | USAGE: 14 | VelodyneData('2013-01-10') 15 | 16 | """ 17 | def __init__(self, date): 18 | 19 | # init base class 20 | BaseRawData.__init__(self, date=date) 21 | BaseConvert.__init__(self, date=date) 22 | 23 | # load velodyne_binary file 24 | if self.velodyne_data_flag: 25 | self.f_bin_velodyne = open(self.velodyne_data_dir + '/%s/velodyne_hits.bin' % self.date, 'r') 26 | else: 27 | raise ValueError('velodyne_data directory not exists') 28 | 29 | def verify_magic(self, s): 30 | """verifies the binary data 31 | 32 | :param s: 33 | :return: True, if data is correct 34 | False 35 | """ 36 | 37 | magic = 44444 38 | m = struct.unpack('= 3) and (m[0] == magic) and (m[1] == magic) and (m[2] == magic) and (m[3] == magic) 41 | 42 | def convert_velodyne(self, x_s, y_s, z_s): 43 | """converts the velodyne binary data to corrected values, check out the paper http://robots.engin.umich.edu/nclt/nclt.pdf 44 | 45 | :param x_s: x value from binary file 46 | :param y_s: y value from binary file 47 | :param z_s: z value from binary file 48 | 49 | :return: converted x, y, z values 50 | """ 51 | scaling = 0.005 52 | offset = -100.0 53 | 54 | x = x_s * scaling + offset 55 | y = y_s * scaling + offset 56 | z = z_s * scaling + offset 57 | 58 | return x, y, z 59 | 60 | def read_next_velodyne_packet(self): 61 | """reads the velodyne binary file 62 | 63 | :return: utime: 64 | data: 65 | num_hits: 66 | """ 67 | try: 68 | 69 | magic = self.f_bin_velodyne.read(8) 70 | if magic == '': # EOF reached 71 | return -1, None 72 | 73 | if not self.verify_magic(magic): 74 | print "Could not verify magic" 75 | return -1, None 76 | 77 | num_hits = struct.unpack(' 0) 117 | 118 | NUM_FIELDS = 5 119 | assert(np.mod(num_values, NUM_FIELDS) == 0) 120 | 121 | num_points = num_values / NUM_FIELDS 122 | 123 | assert(len(points.shape) == 1) 124 | pc2_msg.height = 1 125 | 126 | FLOAT_SIZE_BYTES = 4 127 | pc2_msg.width = num_values * FLOAT_SIZE_BYTES 128 | 129 | pc2_msg.fields = [ 130 | PointField('x', 0, PointField.FLOAT32, 1), 131 | PointField('y', 4, PointField.FLOAT32, 1), 132 | PointField('z', 8, PointField.FLOAT32, 1), 133 | PointField('i', 12, PointField.FLOAT32, 1), 134 | PointField('l', 16, PointField.FLOAT32, 1) 135 | ] 136 | 137 | pc2_msg.is_bigendian = False 138 | pc2_msg.point_step = NUM_FIELDS * FLOAT_SIZE_BYTES 139 | 140 | pc2_msg.row_step = pc2_msg.point_step * num_points 141 | pc2_msg.is_dense = False 142 | 143 | pc2_msg.width = num_points 144 | pc2_msg.data = np.asarray(points, np.float32).tostring() 145 | 146 | return timestamp, pc2_msg -------------------------------------------------------------------------------- /nclt2ros/transformer/velodyne_sync_data.py: -------------------------------------------------------------------------------- 1 | import rospy 2 | import struct 3 | import os 4 | import numpy as np 5 | import geometry_msgs.msg 6 | import tf2_msgs.msg 7 | import tf.transformations 8 | 9 | from sensor_msgs.msg import PointCloud2, PointField 10 | from nclt2ros.extractor.base_raw_data import BaseRawData 11 | from nclt2ros.converter.base_convert import BaseConvert 12 | 13 | 14 | class VelodyneSyncData(BaseRawData, BaseConvert): 15 | """Class to transform the velodyne_sync binary files to ROS PointCloud2 messages 16 | 17 | USAGE: 18 | VelodyneSyncData('2013-01-10') 19 | 20 | """ 21 | def __init__(self, date): 22 | 23 | # init base classes 24 | BaseRawData.__init__(self, date=date) 25 | BaseConvert.__init__(self, date=date) 26 | 27 | def get_velodyne_sync_timestamps_and_files(self): 28 | """returns the timestamps and binary files in a sorted manner 29 | 30 | :return: timestamps_microsec: List, containing the sorted timestamps 31 | bin_files: List, containing the sorted binary files 32 | """ 33 | if self.velodyne_data_flag: 34 | files = os.listdir(self.velodyne_sync_data_dir) 35 | else: 36 | raise ValueError('velodyne_data not exists') 37 | 38 | timestamps_microsec = sorted([long(os.path.splitext(f)[0]) for f in files if f.endswith('.bin')]) 39 | bin_files = sorted([f for f in files if f.endswith('.bin')]) 40 | 41 | return timestamps_microsec, bin_files 42 | 43 | def convert_velodyne(self, x_s, y_s, z_s): 44 | """converts the velodyne binary data to corrected values, check out the paper http://robots.engin.umich.edu/nclt/nclt.pdf 45 | 46 | :param x_s: x value from binary file 47 | :param y_s: y value from binary file 48 | :param z_s: z value from binary file 49 | 50 | :return: converted x, y, z values 51 | """ 52 | 53 | scaling = 0.005 54 | offset = -100.0 55 | 56 | x = x_s * scaling + offset 57 | y = y_s * scaling + offset 58 | z = z_s * scaling + offset 59 | 60 | return x, y, z 61 | 62 | def read_next_velodyne_sync_packet(self, file): 63 | """reads one velodyne sync binary file, equals one revolution of the velodyne sensor 64 | 65 | :param file: one binary file from the velodyne_sync directory 66 | 67 | :return: hits: list, containing the x, y, z, intensity, laser_id of the pointcloud 68 | """ 69 | 70 | try: 71 | os.chdir(self.velodyne_sync_data_dir) 72 | f_bin = open(file, "r") 73 | 74 | hits = [] 75 | while True: 76 | 77 | x_str = f_bin.read(2) 78 | if x_str == '': # eof 79 | break 80 | 81 | x = struct.unpack(' 0) 124 | 125 | NUM_FIELDS = 5 126 | assert(np.mod(num_values, NUM_FIELDS) == 0) 127 | 128 | num_points = num_values / NUM_FIELDS 129 | 130 | assert(len(points.shape) == 1) 131 | pc2_msg.height = 1 132 | 133 | FLOAT_SIZE_BYTES = 4 134 | pc2_msg.width = num_values * FLOAT_SIZE_BYTES 135 | 136 | pc2_msg.fields = [ 137 | PointField('x', 0, PointField.FLOAT32, 1), 138 | PointField('y', 4, PointField.FLOAT32, 1), 139 | PointField('z', 8, PointField.FLOAT32, 1), 140 | PointField('i', 12, PointField.FLOAT32, 1), 141 | PointField('l', 16, PointField.FLOAT32, 1) 142 | ] 143 | 144 | pc2_msg.is_bigendian = False 145 | pc2_msg.point_step = NUM_FIELDS * FLOAT_SIZE_BYTES 146 | 147 | pc2_msg.row_step = pc2_msg.point_step * num_points 148 | pc2_msg.is_dense = False 149 | 150 | pc2_msg.width = num_points 151 | pc2_msg.data = np.asarray(points, np.float32).tostring() 152 | 153 | # create base_link velodyne_link static transformer 154 | vel_static_transform_stamped = geometry_msgs.msg.TransformStamped() 155 | vel_static_transform_stamped.header.stamp = timestamp 156 | vel_static_transform_stamped.header.frame_id = base_link 157 | vel_static_transform_stamped.child_frame_id = velodyne_link 158 | 159 | vel_static_transform_stamped.transform.translation.x = 0.002 160 | vel_static_transform_stamped.transform.translation.y = 0.004 161 | vel_static_transform_stamped.transform.translation.z = 0.957 162 | 163 | quat = tf.transformations.quaternion_from_euler(0, 0, 0) 164 | vel_static_transform_stamped.transform.rotation.x = quat[0] 165 | vel_static_transform_stamped.transform.rotation.y = quat[1] 166 | vel_static_transform_stamped.transform.rotation.z = quat[2] 167 | vel_static_transform_stamped.transform.rotation.w = quat[3] 168 | 169 | # publish static transform 170 | tf_static_msg = tf2_msgs.msg.TFMessage([vel_static_transform_stamped]) 171 | 172 | return timestamp, pc2_msg, tf_static_msg 173 | -------------------------------------------------------------------------------- /nclt2ros/visualizer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bierschi/nclt2ros/77b30ca6750d4b0cd82ccb6660f2fd0946581091/nclt2ros/visualizer/__init__.py -------------------------------------------------------------------------------- /nclt2ros/visualizer/all.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from nclt2ros.visualizer.plotter import Plotter 3 | from nclt2ros.visualizer.gt import GroundTruth 4 | from nclt2ros.visualizer.gps_rtk import GPS_RTK 5 | from nclt2ros.visualizer.gps import GPS 6 | from nclt2ros.visualizer.wheel_odom import WheelOdom 7 | 8 | 9 | class AllSensors(Plotter, GroundTruth, GPS_RTK, GPS, WheelOdom): 10 | """Class to visualize all sensor data in one plot 11 | 12 | USAGE: 13 | AllSensors('2013-01-10', plt_show=True) 14 | 15 | """ 16 | def __init__(self, date, plt_show=True): 17 | 18 | self.date = date 19 | 20 | # init base classes 21 | Plotter.__init__(self, date=self.date) 22 | GroundTruth.__init__(self, date=self.date) 23 | GPS_RTK.__init__(self, date=self.date) 24 | GPS.__init__(self, date=self.date) 25 | WheelOdom.__init__(self, date=self.date) 26 | 27 | self.plt_show = plt_show 28 | 29 | def plot(self): 30 | """visualize all data in one plot 31 | """ 32 | 33 | gt_x, gt_y = self.get_gt_data() 34 | gps_rtk_x, gps_rtk_y = self.get_gps_rtk_data() 35 | gps_x, gps_y = self.get_gps_data() 36 | wheel_odom_x, wheel_odom_y = self.get_wheel_odom_data() 37 | print(self.plt_show) 38 | gt = plt.plot(gt_y, gt_x, color="lime", label='ground truth') # swap x,y coordinate axes! 39 | gps_rtk = plt.plot(gps_rtk_y, gps_rtk_x, 'r-', label='gps rtk') # swap x,y coordinate axes! 40 | gps = plt.plot(gps_y, gps_x, 'y-', label='gps') # swap x,y coordinate axes! 41 | wheel_odom = plt.plot(wheel_odom_y, wheel_odom_x, 'm-', label='wheel odom') # swap x,y coordinate axes! 42 | 43 | plt.title('All Sensors') 44 | plt.xlabel('x in meter') 45 | plt.ylabel('y in meter') 46 | plt.legend(loc='upper left') 47 | 48 | plt.grid() 49 | plt.savefig(self.visualization_png_all_dir + 'raw_data_all.png') 50 | 51 | if self.plt_show: 52 | plt.show() 53 | 54 | def get_png_all_dir(self): 55 | """get the png all sensors directory 56 | 57 | :return: path to png all sensors directory 58 | """ 59 | return self.visualization_png_all_dir 60 | 61 | 62 | if __name__ == '__main__': 63 | all =AllSensors('2012-01-15', plt_show=False) 64 | all.plot() 65 | -------------------------------------------------------------------------------- /nclt2ros/visualizer/gps.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | from nclt2ros.visualizer.plotter import Plotter 6 | from nclt2ros.transformer.coordinate_frame import CoordinateFrame 7 | 8 | 9 | class GPS(Plotter): 10 | """Class to visualize the GPS data as a kml and png file 11 | 12 | USAGE: 13 | GPS(date='2013-01-10', output_file='gps', plt_show=True) 14 | 15 | """ 16 | def __init__(self, date, output_file='gps', plt_show=True): 17 | 18 | if isinstance(output_file, str): 19 | self.output_file = output_file 20 | else: 21 | raise TypeError("'output_file' must be type of string") 22 | 23 | self.date = date 24 | self.plt_show = plt_show 25 | 26 | # init base class 27 | Plotter.__init__(self, date=self.date) 28 | 29 | # transformer coordinate frame into 'odom' or 'gt' 30 | if self.date == '2013-01-10': 31 | self.gps_converter = CoordinateFrame(origin='odom') 32 | else: 33 | self.gps_converter = CoordinateFrame(origin='gt') 34 | 35 | # load data 36 | self.gps = self.reader.read_gps_csv(all_in_one=True) 37 | 38 | def save_kml_line(self): 39 | """visualize the gps data as a kml file 40 | """ 41 | 42 | lat = self.gps[:, 3] 43 | lng = self.gps[:, 4] 44 | gps_list = list() 45 | 46 | for (i, j) in zip(lat, lng): 47 | 48 | if not math.isnan(i) and not math.isnan(j): 49 | tup = (np.rad2deg(j), np.rad2deg(i)) # swap and convert lat long to deg 50 | gps_list.append(tup) 51 | 52 | ls = self.kml.newlinestring(name="gps", coords=gps_list, description="latitude and longitude from gps") 53 | ls.style.linestyle.width = 1 54 | ls.style.linestyle.color = self.yellow 55 | 56 | self.kml.save(self.visualization_kml_dir + self.output_file + '.kml') 57 | 58 | def get_gps_data(self): 59 | """get gps data for visualization 60 | 61 | :return: list for x coordinates, list for y coordinates 62 | """ 63 | lat = self.gps[:, 3] 64 | lng = self.gps[:, 4] 65 | x_list = list() 66 | y_list = list() 67 | 68 | for (i, j) in zip(lat, lng): 69 | 70 | if not math.isnan(i) and not math.isnan(j): 71 | x = self.gps_converter.get_x(lat=np.rad2deg(i)) 72 | y = self.gps_converter.get_y(lon=np.rad2deg(j)) 73 | x_list.append(x) 74 | y_list.append(y) 75 | 76 | return x_list, y_list 77 | 78 | def save_gps_png(self): 79 | """visualize the gps data as a kml file 80 | """ 81 | 82 | x_list, y_list = self.get_gps_data() 83 | 84 | plt.plot(y_list, x_list, 'y-', label='gps') 85 | plt.title('GPS') 86 | plt.xlabel('x in meter') 87 | plt.ylabel('y in meter') 88 | plt.legend(loc='upper left') 89 | 90 | plt.grid() 91 | plt.savefig(self.visualization_png_gps_dir + 'gps.png') 92 | if self.plt_show: 93 | plt.show() 94 | 95 | def get_png_gps_dir(self): 96 | """get the png gps directory 97 | 98 | :return: path to png gps directory 99 | """ 100 | return self.visualization_png_gps_dir 101 | 102 | 103 | if __name__ == '__main__': 104 | gps = GPS(date='2012-01-08') 105 | gps.save_kml_line() 106 | 107 | -------------------------------------------------------------------------------- /nclt2ros/visualizer/gps_rtk.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | from nclt2ros.visualizer.plotter import Plotter 6 | from nclt2ros.transformer.coordinate_frame import CoordinateFrame 7 | 8 | 9 | class GPS_RTK(Plotter): 10 | """Class to visualize the GPS RTK data as a kml and png file 11 | 12 | USAGE: 13 | GPS_RTK(date='2013-01-10', output_file='gps_rtk', plt_show=True) 14 | 15 | """ 16 | def __init__(self, date, output_file='gps_rtk', plt_show=True): 17 | 18 | if isinstance(output_file, str): 19 | self.output_file = output_file 20 | else: 21 | raise TypeError("'output_file' must be type of string") 22 | 23 | self.date = date 24 | self.plt_show = plt_show 25 | 26 | # init base class 27 | Plotter.__init__(self, date=self.date) 28 | 29 | # transform coordinate frame into 'odom' or 'gt' 30 | if self.date == '2013-01-10': 31 | self.gps_rtk_converter = CoordinateFrame(origin='odom') 32 | else: 33 | self.gps_rtk_converter = CoordinateFrame(origin='gt') 34 | 35 | # load data 36 | self.gps_rtk = self.reader.read_gps_rtk_csv(all_in_one=True) 37 | 38 | def save_kml_line(self): 39 | """visualize the gps rtk data as a kml file 40 | """ 41 | 42 | lat = self.gps_rtk[:, 3] 43 | lng = self.gps_rtk[:, 4] 44 | gps_rtk_list = list() 45 | 46 | for (i_lat, j_lng) in zip(lat, lng): 47 | 48 | if not math.isnan(i_lat) and not math.isnan(j_lng): 49 | tup = (np.rad2deg(j_lng), np.rad2deg(i_lat)) # swap and convert lat long to deg 50 | gps_rtk_list.append(tup) 51 | 52 | ls = self.kml.newlinestring(name="gps rtk", coords=gps_rtk_list, description="latitude and longitude from gps rtk") 53 | ls.style.linestyle.width = 1 54 | ls.style.linestyle.color = self.red 55 | 56 | self.kml.save(self.visualization_kml_dir + self.output_file + '.kml') 57 | 58 | def get_gps_rtk_data(self): 59 | """get gps rtk data for visualization 60 | 61 | :return: list for x coordinates, list for y coordinates 62 | """ 63 | lat = self.gps_rtk[:, 3] 64 | lng = self.gps_rtk[:, 4] 65 | x_list = list() 66 | y_list = list() 67 | 68 | for (i_lat, j_lng) in zip(lat, lng): 69 | 70 | if not math.isnan(i_lat) and not math.isnan(j_lng): 71 | x = self.gps_rtk_converter.get_x(lat=np.rad2deg(i_lat)) 72 | y = self.gps_rtk_converter.get_y(lon=np.rad2deg(j_lng)) 73 | x_list.append(x) 74 | y_list.append(y) 75 | 76 | return x_list, y_list 77 | 78 | def save_gps_rtk_png(self): 79 | """visualize the gps rtk data as a png file 80 | 81 | """ 82 | 83 | x_list, y_list = self.get_gps_rtk_data() 84 | 85 | plt.plot(y_list, x_list, 'r-', label='gps rtk') 86 | plt.title('GPS RTK') 87 | plt.xlabel('x in meter') 88 | plt.ylabel('y in meter') 89 | plt.legend(loc='upper left') 90 | 91 | plt.grid() 92 | plt.savefig(self.visualization_png_gps_rtk_dir + 'gps_rtk.png') 93 | 94 | if self.plt_show: 95 | plt.show() 96 | 97 | def get_png_gps_rtk_dir(self): 98 | """get the png gps rtk directory 99 | 100 | :return: path to png gps rtk directory 101 | """ 102 | return self.visualization_png_gps_rtk_dir 103 | 104 | 105 | if __name__ == '__main__': 106 | gps = GPS_RTK(date='2012-01-08') 107 | gps.save_gps_rtk_png() -------------------------------------------------------------------------------- /nclt2ros/visualizer/gt.py: -------------------------------------------------------------------------------- 1 | import math 2 | import matplotlib.pyplot as plt 3 | 4 | from nclt2ros.visualizer.plotter import Plotter 5 | from nclt2ros.transformer.coordinate_frame import CoordinateFrame 6 | 7 | 8 | class GroundTruth(Plotter): 9 | """Class to visualize the ground truth data as a kml and png file 10 | 11 | USAGE: 12 | GroundTruth(date='2013-01-10', output_file='ground_truth', plt_show=True) 13 | 14 | """ 15 | def __init__(self, date, output_file='ground_truth', plt_show=True): 16 | 17 | if isinstance(output_file, str): 18 | self.output_file = output_file 19 | else: 20 | raise TypeError("'output_file' must be type of string") 21 | 22 | self.date = date 23 | self.plt_show = plt_show 24 | 25 | # init base class 26 | Plotter.__init__(self, date=self.date) 27 | 28 | # transformer coordinate frame into 'gt' 29 | self.gt_converter = CoordinateFrame(origin='gt') 30 | 31 | # load gt data 32 | self.gt = self.reader_gt.read_gt_csv(all_in_one=True) 33 | 34 | def save_kml_line(self): 35 | """visualize the ground truth as a kml file 36 | """ 37 | 38 | gt_x = self.gt[:, 1] 39 | gt_y = self.gt[:, 2] 40 | gt_list = list() 41 | 42 | if len(gt_x) > 600000: 43 | divider = 35 44 | elif len(gt_x) > 200000: 45 | divider = 15 46 | else: 47 | divider = 5 48 | 49 | for row, (x_i, y_j) in enumerate(zip(gt_x, gt_y)): 50 | 51 | if not math.isnan(x_i) and not math.isnan(y_j): 52 | lat = self.gt_converter.get_lat(x=x_i) 53 | lng = self.gt_converter.get_lon(y=y_j) 54 | 55 | tup = (lng, lat) 56 | 57 | # minimize the elements in the kml output file 58 | if (row % divider) == 0: 59 | gt_list.append(tup) 60 | 61 | # create line string 62 | ls = self.kml.newlinestring(name="ground truth", coords=gt_list, description="latitude and longitude from ground truth",) 63 | ls.style.linestyle.width = 1 64 | ls.style.linestyle.color = self.green 65 | 66 | # save kml file in visualization directory 67 | self.kml.save(self.visualization_kml_dir + self.output_file + '.kml') 68 | 69 | def get_gt_data(self): 70 | """get ground truth data for visualization 71 | 72 | :return: list for x coordinates, list for y coordinates 73 | """ 74 | gt_x = self.gt[:, 1] 75 | gt_y = self.gt[:, 2] 76 | first_x_coord = gt_x[0] 77 | first_y_coord = gt_y[0] 78 | x_new = list() 79 | y_new = list() 80 | 81 | for row, (x_i, y_j) in enumerate(zip(gt_x, gt_y)): 82 | 83 | if not math.isnan(x_i) and not math.isnan(y_j): 84 | # eliminate offset in this dataset 85 | if self.date == '2013-01-10': 86 | x_i = x_i - first_x_coord 87 | y_j = y_j - first_y_coord 88 | y_new.append(y_j) 89 | x_new.append(x_i) 90 | else: 91 | y_new.append(y_j) 92 | x_new.append(x_i) 93 | 94 | return x_new, y_new 95 | 96 | def save_gt_png(self): 97 | """visualize the ground truth as a png file 98 | 99 | :param offset: Boolean, True if eliminate the offset between odom and ground truth coordinate frame 100 | """ 101 | 102 | x_new, y_new = self.get_gt_data() 103 | 104 | plt.plot(y_new, x_new, color="lime", label='ground truth') 105 | 106 | plt.xlabel('x in meter') 107 | plt.ylabel('y in meter') 108 | plt.legend(loc='upper left') 109 | 110 | plt.grid() 111 | 112 | plt.title('Ground Truth') 113 | plt.savefig(self.visualization_png_gt_dir + self.output_file + '.png') 114 | 115 | if self.plt_show: 116 | plt.show() 117 | 118 | def save_roll_png(self): 119 | """visualize the roll angle as a png file 120 | """ 121 | 122 | utimes = self.gt[:, 0] 123 | roll_rad = self.gt[:, 4] 124 | plt.plot(utimes, roll_rad, color="blue", label='roll angle') 125 | 126 | plt.xlabel('time in sec') 127 | plt.ylabel('roll in rad') 128 | plt.legend(loc='upper left') 129 | plt.grid() 130 | 131 | plt.title('Roll angle from Ground Truth') 132 | plt.savefig(self.visualization_png_gt_dir + self.output_file + '_roll.png') 133 | plt.show() 134 | 135 | def save_pitch_png(self): 136 | """visualize the pitch angle as a png file 137 | """ 138 | utimes = self.gt[:, 0] 139 | pitch_rad = self.gt[:, 5] 140 | 141 | plt.plot(utimes, pitch_rad, color="blue", label='pitch angle') 142 | 143 | plt.xlabel('time in sec') 144 | plt.ylabel('pitch in rad') 145 | plt.legend(loc='upper left') 146 | plt.grid() 147 | 148 | plt.title('Pitch angle from Ground Truth') 149 | plt.savefig(self.visualization_png_gt_dir + self.output_file + '_pitch.png') 150 | plt.show() 151 | 152 | def save_yaw_png(self): 153 | """visualize the yaw angle as a png file 154 | """ 155 | utimes = self.gt[:, 0] 156 | yaw_rad = self.gt[:, 6] 157 | 158 | plt.plot(utimes, yaw_rad, color="blue", label='yaw angle') 159 | 160 | plt.xlabel('time in sec') 161 | plt.ylabel('yaw in rad') 162 | plt.legend(loc='upper left') 163 | plt.grid() 164 | 165 | plt.title('Yaw angle from Ground Truth') 166 | plt.savefig(self.visualization_png_gt_dir + self.output_file + '_yaw.png') 167 | plt.show() 168 | 169 | def get_png_gt_dir(self): 170 | """get the png ground truth directory 171 | 172 | :return: path to png ground truth directory 173 | """ 174 | return self.visualization_png_gt_dir 175 | 176 | 177 | if __name__ == '__main__': 178 | gt = GroundTruth(date='2012-01-15') 179 | gt.save_gt_png() 180 | -------------------------------------------------------------------------------- /nclt2ros/visualizer/plotter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import simplekml 3 | import rospy 4 | from definitions import ROOT_DIR 5 | from nclt2ros.extractor.read_sensor_data import ReadSensorData 6 | from nclt2ros.extractor.read_ground_truth import ReadGroundTruth 7 | 8 | 9 | class Plotter: 10 | """Base class for visualizing 11 | 12 | USAGE: 13 | Plotter('2013-01-10') 14 | 15 | """ 16 | def __init__(self, date): 17 | 18 | if isinstance(date, str): 19 | self.date = date 20 | self.reader = ReadSensorData(date=date) 21 | self.reader_gt = ReadGroundTruth(date=date) 22 | else: 23 | raise TypeError('"date" must be of type string') 24 | 25 | PLOT_PATH_DFLT = ROOT_DIR + '/nclt2ros/plots/' 26 | PLOT_PATH_DFLT = '/home/christian/nclt2ros/plots/' # TODO remove, only for debugging 27 | self.plot_path = rospy.get_param('~plot_path', PLOT_PATH_DFLT) 28 | 29 | if self.plot_path.endswith('/'): 30 | self.plot_dir = self.plot_path 31 | else: 32 | self.plot_dir = self.plot_path + '/' 33 | 34 | # define directories 35 | self.visualization_kml_dir = self.plot_dir + '%s/kml/' % self.date 36 | self.visualization_png_gt_dir = self.plot_dir + '%s/png/gt/' % self.date 37 | self.visualization_png_gps_rtk_dir = self.plot_dir + '%s/png/gps_rtk/' % self.date 38 | self.visualization_png_gps_dir = self.plot_dir + '%s/png/gps/' % self.date 39 | self.visualization_png_wheel_odom_dir = self.plot_dir + '%s/png/wheel_odom/' % self.date 40 | self.visualization_png_all_dir = self.plot_dir + '%s/png/all/' % self.date 41 | 42 | # check directories 43 | if not os.path.exists(self.visualization_kml_dir): 44 | os.makedirs(self.visualization_kml_dir) 45 | 46 | if not os.path.exists(self.visualization_png_gt_dir): 47 | os.makedirs(self.visualization_png_gt_dir) 48 | 49 | if not os.path.exists(self.visualization_png_gps_rtk_dir): 50 | os.makedirs(self.visualization_png_gps_rtk_dir) 51 | 52 | if not os.path.exists(self.visualization_png_gps_dir): 53 | os.makedirs(self.visualization_png_gps_dir) 54 | 55 | if not os.path.exists(self.visualization_png_wheel_odom_dir): 56 | os.makedirs(self.visualization_png_wheel_odom_dir) 57 | 58 | if not os.path.exists(self.visualization_png_all_dir): 59 | os.makedirs(self.visualization_png_all_dir) 60 | 61 | # kml settings 62 | self.kml = simplekml.Kml() 63 | self.green = simplekml.Color.lime 64 | self.red = simplekml.Color.red 65 | self.yellow = simplekml.Color.yellow 66 | self.magenta = simplekml.Color.magenta 67 | 68 | def get_kml_dir(self): 69 | """get the kml directory 70 | 71 | :return: path to kml directory 72 | """ 73 | return self.visualization_kml_dir -------------------------------------------------------------------------------- /nclt2ros/visualizer/visualize.py: -------------------------------------------------------------------------------- 1 | import rospy 2 | from nclt2ros.visualizer.gt import GroundTruth 3 | from nclt2ros.visualizer.gps import GPS 4 | from nclt2ros.visualizer.gps_rtk import GPS_RTK 5 | from nclt2ros.visualizer.wheel_odom import WheelOdom 6 | from nclt2ros.visualizer.all import AllSensors 7 | 8 | 9 | class Visualize: 10 | """Class to visualize the raw data depending on the parameter specified in the launch file 11 | 12 | USAGE: 13 | Visualize(date='2013-01-10') 14 | 15 | """ 16 | def __init__(self, date, show_plot, **kwargs): 17 | 18 | self.date = date 19 | 20 | if isinstance(show_plot, bool): 21 | self.show_plot = show_plot 22 | else: 23 | raise TypeError('show plot must be type of boolean') 24 | 25 | self.v_gt_kml = None 26 | self.v_gt_png = None 27 | self.v_gps_kml = None 28 | self.v_gps_png = None 29 | self.v_gps_rtk_kml = None 30 | self.v_gps_rtk_png = None 31 | self.v_odom_kml = None 32 | self.v_odom_png = None 33 | self.v_all = None 34 | 35 | for (key, value) in kwargs.iteritems(): 36 | if hasattr(self, key): 37 | setattr(self, key, value) 38 | 39 | # TODO expand with more commands 40 | 41 | if self.v_gt_kml: 42 | rospy.loginfo("visualize ground truth kml from date %s" % self.date) 43 | self.gt = GroundTruth(date=self.date, plt_show=show_plot) 44 | self.gt.save_kml_line() 45 | kml_dir = self.gt.get_kml_dir() 46 | rospy.loginfo("successfully created ground truth kml file in %s" % kml_dir) 47 | 48 | if self.v_gt_png: 49 | rospy.loginfo("visualize ground truth png from date %s" % self.date) 50 | self.gt = GroundTruth(date=self.date, plt_show=show_plot) 51 | self.gt.save_gt_png() 52 | png_gt_dir = self.gt.get_png_gt_dir() 53 | rospy.loginfo("successfully created ground truth png file in %s" % png_gt_dir) 54 | 55 | if self.v_gps_kml: 56 | rospy.loginfo("visualize gps kml from date %s" % self.date) 57 | self.gps = GPS(date=self.date, plt_show=show_plot) 58 | self.gps.save_kml_line() 59 | kml_dir = self.gps.get_kml_dir() 60 | rospy.loginfo("successfully created gps kml file in %s" % kml_dir) 61 | 62 | if self.v_gps_png: 63 | rospy.loginfo("visualize gps png from %s" % self.date) 64 | self.gps = GPS(date=self.date, plt_show=show_plot) 65 | self.gps.save_gps_png() 66 | png_gps_dir = self.gps.get_png_gps_dir() 67 | rospy.loginfo("successfully created gps png file in %s" % png_gps_dir) 68 | 69 | if self.v_gps_rtk_kml: 70 | rospy.loginfo("visualize gps_rtk kml from date %s" % self.date) 71 | self.gps_rtk = GPS_RTK(date=self.date, plt_show=show_plot) 72 | self.gps_rtk.save_kml_line() 73 | kml_dir = self.gps_rtk.get_kml_dir() 74 | rospy.loginfo("successfully created gps_rtk kml file in %s" % kml_dir) 75 | 76 | if self.v_gps_rtk_png: 77 | rospy.loginfo("visualize gps_rtk png from date %s" % self.date) 78 | self.gps_rtk = GPS_RTK(date=self.date, plt_show=show_plot) 79 | self.gps_rtk.save_gps_rtk_png() 80 | png_gps_rtk_dir = self.gps_rtk.get_png_gps_rtk_dir() 81 | rospy.loginfo("successfully created gps_rtk png file in %s" % png_gps_rtk_dir) 82 | 83 | if self.v_odom_kml: 84 | rospy.loginfo("visualize wheel odometry kml from date %s" % self.date) 85 | self.wheel_odom = WheelOdom(date=self.date, plt_show=show_plot) 86 | self.wheel_odom.save_kml_line() 87 | kml_dir = self.wheel_odom.get_kml_dir() 88 | rospy.loginfo("successfully created wheel odometry kml file in %s" % kml_dir) 89 | 90 | if self.v_odom_png: 91 | rospy.loginfo("visualize wheel odometry png from date %s" % self.date) 92 | self.wheel_odom = WheelOdom(date=self.date, plt_show=show_plot) 93 | self.wheel_odom.save_wheel_odom_png() 94 | png_odom_dir = self.wheel_odom.get_png_odom_dir() 95 | rospy.loginfo("successfully created wheel odometry png file in %s/" % png_odom_dir) 96 | 97 | if self.v_all: 98 | rospy.loginfo("visualizes all data from date %s" % self.date) 99 | self.all = AllSensors(date=self.date, plt_show=show_plot) 100 | self.all.plot() 101 | png_all_dir = self.all.get_png_all_dir() 102 | rospy.loginfo("successfully created raw_data_all png file in %s" % png_all_dir) 103 | 104 | else: 105 | rospy.loginfo("no matching parameters were found") 106 | 107 | -------------------------------------------------------------------------------- /nclt2ros/visualizer/wheel_odom.py: -------------------------------------------------------------------------------- 1 | import math 2 | import matplotlib.pyplot as plt 3 | from nclt2ros.visualizer.plotter import Plotter 4 | from nclt2ros.transformer.coordinate_frame import CoordinateFrame 5 | 6 | 7 | class WheelOdom(Plotter): 8 | """Class to visualize the wheel odometry data as a kml and png file 9 | 10 | USAGE: 11 | WheelOdom(date='2013-01-10', output_file='wheel_odom', plt_show=True) 12 | 13 | """ 14 | def __init__(self, date, output_file='wheel_odom', plt_show=True): 15 | 16 | if isinstance(output_file, str): 17 | self.output_file = output_file 18 | else: 19 | raise TypeError("'output_file' must be type of string") 20 | 21 | self.date = date 22 | self.plt_show = plt_show 23 | 24 | # init base class 25 | Plotter.__init__(self, date=self.date) 26 | 27 | # transformer coordinate frame into 'odom' or 'gt' 28 | if self.date == '2013-01-10' or self.date == '2012-01-15': 29 | self.odom_converter = CoordinateFrame(origin='odom') 30 | else: 31 | self.odom_converter = CoordinateFrame(origin='gt') 32 | 33 | # load data 34 | self.wheel_odom = self.reader.read_odometry_mu_100hz_csv(all_in_one=True) 35 | 36 | def save_kml_line(self): 37 | """visualize wheel odometry data as a kml file 38 | """ 39 | 40 | wheel_odom_x = self.wheel_odom[:, 1] 41 | wheel_odom_y = self.wheel_odom[:, 2] 42 | odom_list = list() 43 | 44 | if len(wheel_odom_x) > 200000: 45 | divider = 15 46 | else: 47 | divider = 5 48 | 49 | for row, (x_i, y_j) in enumerate(zip(wheel_odom_x, wheel_odom_y)): 50 | 51 | if not math.isnan(x_i) and not math.isnan(y_j): 52 | lat = self.odom_converter.get_lat(x=x_i) 53 | lng = self.odom_converter.get_lon(y=y_j) 54 | tup = (lng, lat) 55 | 56 | # minimize the elements in the kml output file 57 | if (row % divider) == 0: 58 | odom_list.append(tup) 59 | 60 | ls = self.kml.newlinestring(name="wheel odometry", coords=odom_list, description="latitute and longitude from wheel odometry") 61 | ls.style.linestyle.width = 1 62 | ls.style.linestyle.color = self.magenta 63 | 64 | self.kml.save(self.visualization_kml_dir + self.output_file + '.kml') 65 | 66 | def get_wheel_odom_data(self): 67 | """get wheel odometry data for visualization 68 | 69 | :return: list for x coordinates, list for y coordinates 70 | """ 71 | wheel_odom_x = self.wheel_odom[:, 1] 72 | wheel_odom_y = self.wheel_odom[:, 2] 73 | 74 | return wheel_odom_x, wheel_odom_y 75 | 76 | def save_wheel_odom_png(self): 77 | """visualize wheel odometry data as a png file 78 | """ 79 | wheel_odom_x, wheel_odom_y = self.get_wheel_odom_data() 80 | 81 | plt.plot(wheel_odom_y, wheel_odom_x, 'm-', label="wheel odom") 82 | plt.title('Wheel Odometry') 83 | plt.xlabel('x in meter') 84 | plt.ylabel('y in meter') 85 | plt.legend(loc='upper left') 86 | 87 | plt.grid() 88 | plt.savefig(self.visualization_png_wheel_odom_dir + 'wheel_odom.png') 89 | 90 | if self.plt_show: 91 | plt.show() 92 | 93 | def get_png_odom_dir(self): 94 | """get the png odom directory 95 | 96 | :return: path to png odom directory 97 | """ 98 | return self.visualization_png_wheel_odom_dir 99 | 100 | 101 | if __name__ == '__main__': 102 | odom = WheelOdom(date='2012-01-15') 103 | odom.save_wheel_odom_png() -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 35 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | simplekml 2 | numpy 3 | matplotlib 4 | -------------------------------------------------------------------------------- /rviz/config.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz/Displays 3 | Help Height: 116 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Global Options1 8 | - /Status1 9 | - /Odometry2/Shape1 10 | - /Image1 11 | Splitter Ratio: 0.5 12 | Tree Height: 632 13 | - Class: rviz/Selection 14 | Name: Selection 15 | - Class: rviz/Tool Properties 16 | Expanded: 17 | - /2D Pose Estimate1 18 | - /2D Nav Goal1 19 | - /Publish Point1 20 | Name: Tool Properties 21 | Splitter Ratio: 0.588679016 22 | - Class: rviz/Views 23 | Expanded: 24 | - /Current View1 25 | Name: Views 26 | Splitter Ratio: 0.5 27 | - Class: rviz/Time 28 | Experimental: false 29 | Name: Time 30 | SyncMode: 0 31 | SyncSource: PointCloud2 32 | Visualization Manager: 33 | Class: "" 34 | Displays: 35 | - Alpha: 0.5 36 | Cell Size: 1 37 | Class: rviz/Grid 38 | Color: 160; 160; 164 39 | Enabled: true 40 | Line Style: 41 | Line Width: 0.0299999993 42 | Value: Lines 43 | Name: Grid 44 | Normal Cell Count: 0 45 | Offset: 46 | X: 0 47 | Y: 0 48 | Z: 0 49 | Plane: XY 50 | Plane Cell Count: 10 51 | Reference Frame:nclt2ros 4 |1.0.0 5 |nclt2ros package provides ROS nodes for downloading, extracting, converting and visualizing the NCLT Dataset 6 | 7 |christian bierschneider 8 |christian bierschneider 9 | 10 |MIT 11 | 12 | 13 |catkin 14 | 15 |roscpp 16 |rospy 17 |roslaunch 18 | 19 |roscpp 20 |rospy 21 |roslaunch 22 | 23 |roscpp 24 |rospy 25 |python-numpy 26 |python-matplotlib 27 |std_msgs 28 |sensor_msgs 29 |nav_msgs 30 |geometry_msgs 31 |tf 32 | 33 |simplekml 34 |52 | Value: true 53 | - Class: rviz/TF 54 | Enabled: true 55 | Frame Timeout: 15 56 | Frames: 57 | All Enabled: true 58 | base_link: 59 | Value: true 60 | camera: 61 | Value: true 62 | gps_link: 63 | Value: true 64 | gps_rtk_link: 65 | Value: true 66 | ground_truth_link: 67 | Value: true 68 | imu_link: 69 | Value: true 70 | laser_urg: 71 | Value: true 72 | laser_utm: 73 | Value: true 74 | odom: 75 | Value: true 76 | velodyne: 77 | Value: true 78 | world: 79 | Value: true 80 | Marker Scale: 1 81 | Name: TF 82 | Show Arrows: true 83 | Show Axes: true 84 | Show Names: true 85 | Tree: 86 | world: 87 | ground_truth_link: 88 | {} 89 | odom: 90 | base_link: 91 | camera: 92 | {} 93 | gps_link: 94 | {} 95 | gps_rtk_link: 96 | {} 97 | imu_link: 98 | {} 99 | laser_urg: 100 | {} 101 | laser_utm: 102 | {} 103 | velodyne: 104 | {} 105 | Update Interval: 0 106 | Value: true 107 | - Alpha: 1 108 | Autocompute Intensity Bounds: true 109 | Autocompute Value Bounds: 110 | Max Value: 17.5682011 111 | Min Value: -2.36935019 112 | Value: true 113 | Axis: Z 114 | Channel Name: intensity 115 | Class: rviz/PointCloud2 116 | Color: 255; 255; 255 117 | Color Transformer: AxisColor 118 | Decay Time: 0 119 | Enabled: true 120 | Invert Rainbow: false 121 | Max Color: 255; 255; 255 122 | Max Intensity: 4096 123 | Min Color: 0; 0; 0 124 | Min Intensity: 0 125 | Name: PointCloud2 126 | Position Transformer: XYZ 127 | Queue Size: 10 128 | Selectable: true 129 | Size (Pixels): 3 130 | Size (m): 0.0199999996 131 | Style: Flat Squares 132 | Topic: /velodyne_points 133 | Unreliable: false 134 | Use Fixed Frame: true 135 | Use rainbow: true 136 | Value: true 137 | - Alpha: 1 138 | Autocompute Intensity Bounds: true 139 | Autocompute Value Bounds: 140 | Max Value: 10 141 | Min Value: -10 142 | Value: true 143 | Axis: Z 144 | Channel Name: intensity 145 | Class: rviz/LaserScan 146 | Color: 255; 255; 255 147 | Color Transformer: Intensity 148 | Decay Time: 0 149 | Enabled: true 150 | Invert Rainbow: false 151 | Max Color: 255; 255; 255 152 | Max Intensity: 4096 153 | Min Color: 0; 0; 0 154 | Min Intensity: 0 155 | Name: LaserScan 156 | Position Transformer: XYZ 157 | Queue Size: 10 158 | Selectable: true 159 | Size (Pixels): 3 160 | Size (m): 0.0199999996 161 | Style: Flat Squares 162 | Topic: /hokuyo_30m 163 | Unreliable: false 164 | Use Fixed Frame: true 165 | Use rainbow: true 166 | Value: true 167 | - Alpha: 1 168 | Autocompute Intensity Bounds: true 169 | Autocompute Value Bounds: 170 | Max Value: 0.493303388 171 | Min Value: 0.304097891 172 | Value: true 173 | Axis: Z 174 | Channel Name: intensity 175 | Class: rviz/LaserScan 176 | Color: 255; 255; 255 177 | Color Transformer: AxisColor 178 | Decay Time: 0 179 | Enabled: true 180 | Invert Rainbow: false 181 | Max Color: 255; 255; 255 182 | Max Intensity: 4096 183 | Min Color: 0; 0; 0 184 | Min Intensity: 0 185 | Name: LaserScan 186 | Position Transformer: XYZ 187 | Queue Size: 10 188 | Selectable: true 189 | Size (Pixels): 3 190 | Size (m): 0.0199999996 191 | Style: Flat Squares 192 | Topic: /hokuyo_4m 193 | Unreliable: false 194 | Use Fixed Frame: true 195 | Use rainbow: true 196 | Value: true 197 | - Angle Tolerance: 0.100000001 198 | Class: rviz/Odometry 199 | Covariance: 200 | Orientation: 201 | Alpha: 0.5 202 | Color: 255; 255; 127 203 | Color Style: Unique 204 | Frame: Local 205 | Offset: 1 206 | Scale: 1 207 | Value: true 208 | Position: 209 | Alpha: 0.300000012 210 | Color: 204; 51; 204 211 | Scale: 1 212 | Value: true 213 | Value: false 214 | Enabled: true 215 | Keep: 0 216 | Name: Odometry 217 | Position Tolerance: 0.100000001 218 | Shape: 219 | Alpha: 1 220 | Axes Length: 1 221 | Axes Radius: 0.100000001 222 | Color: 255; 25; 0 223 | Head Length: 0.300000012 224 | Head Radius: 0.100000001 225 | Shaft Length: 1 226 | Shaft Radius: 0.0500000007 227 | Value: Arrow 228 | Topic: /odom 229 | Unreliable: false 230 | Value: true 231 | - Angle Tolerance: 0.100000001 232 | Class: rviz/Odometry 233 | Covariance: 234 | Orientation: 235 | Alpha: 0.5 236 | Color: 255; 255; 127 237 | Color Style: Unique 238 | Frame: Local 239 | Offset: 1 240 | Scale: 1 241 | Value: true 242 | Position: 243 | Alpha: 0.300000012 244 | Color: 204; 51; 204 245 | Scale: 1 246 | Value: true 247 | Value: false 248 | Enabled: true 249 | Keep: 0 250 | Name: Odometry 251 | Position Tolerance: 0.100000001 252 | Shape: 253 | Alpha: 1 254 | Axes Length: 1 255 | Axes Radius: 0.100000001 256 | Color: 0; 255; 0 257 | Head Length: 0.300000012 258 | Head Radius: 0.100000001 259 | Shaft Length: 1 260 | Shaft Radius: 0.0500000007 261 | Value: Arrow 262 | Topic: /ground_truth 263 | Unreliable: false 264 | Value: true 265 | - Class: rviz/Image 266 | Enabled: true 267 | Image Topic: /images/raw5 268 | Max Value: 1 269 | Median window: 5 270 | Min Value: 0 271 | Name: Image 272 | Normalize Range: true 273 | Queue Size: 2 274 | Transport Hint: raw 275 | Unreliable: false 276 | Value: true 277 | Enabled: true 278 | Global Options: 279 | Background Color: 48; 48; 48 280 | Default Light: true 281 | Fixed Frame: odom 282 | Frame Rate: 30 283 | Name: root 284 | Tools: 285 | - Class: rviz/Interact 286 | Hide Inactive Objects: true 287 | - Class: rviz/MoveCamera 288 | - Class: rviz/Select 289 | - Class: rviz/FocusCamera 290 | - Class: rviz/Measure 291 | - Class: rviz/SetInitialPose 292 | Topic: /initialpose 293 | - Class: rviz/SetGoal 294 | Topic: /move_base_simple/goal 295 | - Class: rviz/PublishPoint 296 | Single click: true 297 | Topic: /clicked_point 298 | Value: true 299 | Views: 300 | Current: 301 | Class: rviz/Orbit 302 | Distance: 41.4196281 303 | Enable Stereo Rendering: 304 | Stereo Eye Separation: 0.0599999987 305 | Stereo Focal Distance: 1 306 | Swap Stereo Eyes: false 307 | Value: false 308 | Focal Point: 309 | X: 0 310 | Y: 0 311 | Z: 0 312 | Focal Shape Fixed Size: false 313 | Focal Shape Size: 0.0500000007 314 | Invert Z Axis: false 315 | Name: Current View 316 | Near Clip Distance: 0.00999999978 317 | Pitch: 0.690397322 318 | Target Frame: 319 | Value: Orbit (rviz) 320 | Yaw: 3.44359231 321 | Saved: ~ 322 | Window Geometry: 323 | Camera: 324 | collapsed: false 325 | Displays: 326 | collapsed: false 327 | Height: 951 328 | Hide Left Dock: false 329 | Hide Right Dock: false 330 | Image: 331 | collapsed: false 332 | QMainWindow State: 000000ff00000000fd00000004000000000000016a0000032dfc0200000009fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006100fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afc000000280000032d000000d700fffffffa000000040100000005fb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb0000000c00430061006d0065007200610000000000ffffffff0000006700000067fb0000000c00430061006d0065007200610000000000ffffffff0000006700000067fb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb000000100044006900730070006c00610079007301000000000000016a0000016a00fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006503000001c3000001e40000024f00000186000000010000010f0000032dfc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007301000000280000032d000000ad00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073f0000003efc0100000002fb0000000800540069006d006501000000000000073f0000030000fffffffb0000000800540069006d00650100000000000004500000000000000000000004ba0000032d00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 333 | Selection: 334 | collapsed: false 335 | Time: 336 | collapsed: false 337 | Tool Properties: 338 | collapsed: false 339 | Views: 340 | collapsed: false 341 | Width: 1855 342 | X: 65 343 | Y: 24 344 | -------------------------------------------------------------------------------- /scripts/nclt2downloader: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | from nclt2ros.downloader.download import Download 5 | from nclt2ros.extractor.extract import Extract 6 | from definitions import ROOT_DIR 7 | 8 | 9 | def main(): 10 | 11 | rospy.init_node('nclt2downloader') 12 | rospy.loginfo("running node 'nclt2downloader'") 13 | 14 | date = rospy.get_param('~date', '2013-01-10') 15 | 16 | RAW_DATA_PATH_DFLT = ROOT_DIR + '/nclt2ros/raw_data/' 17 | raw_data_path = rospy.get_param('~raw_data_path', RAW_DATA_PATH_DFLT) 18 | 19 | lb3 = rospy.get_param('~lb3', False) 20 | sen = rospy.get_param('~sen', False) 21 | vel = rospy.get_param('~vel', False) 22 | hokuyo = rospy.get_param('~hokuyo', False) 23 | gt = rospy.get_param('~gt', False) 24 | gt_cov = rospy.get_param('~gt_cov', False) 25 | 26 | loader = Download(date=date, raw_data_path=raw_data_path, lb3=lb3, sen=sen, vel=vel, hokuyo=hokuyo, gt=gt, gt_cov=gt_cov) 27 | 28 | extractor = Extract(date=date, lb3=lb3, sen=sen, vel=vel, hokuyo=hokuyo) 29 | 30 | rospy.loginfo("terminating node 'nclt2downloader'") 31 | 32 | 33 | if __name__ == '__main__': 34 | try: 35 | main() 36 | except rospy.ROSInterruptException: 37 | pass 38 | -------------------------------------------------------------------------------- /scripts/nclt2rosbag: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | from nclt2ros.converter.to_rosbag import ToRosbag 5 | 6 | 7 | def main(): 8 | 9 | rospy.init_node('nclt2ros') 10 | 11 | date = rospy.get_param('~date', '2013-01-10') 12 | 13 | bag_name = rospy.get_param('~bag_name', 'nclt') 14 | cam_folder = rospy.get_param('~cam_folder', 5) 15 | lb3 = rospy.get_param('~lb3', False) 16 | sen = rospy.get_param('~sen', False) 17 | hokuyo = rospy.get_param('~hokuyo', False) 18 | vel = rospy.get_param('~vel', False) 19 | gt = rospy.get_param('~gt', False) 20 | 21 | converter = ToRosbag(date=date, bag_name=bag_name, cam_folder=cam_folder, gt=gt, sen=sen, hokuyo=hokuyo, vel=vel, lb3=lb3) 22 | converter.process() 23 | 24 | 25 | if __name__ == '__main__': 26 | try: 27 | main() 28 | except rospy.ROSInterruptException: 29 | pass 30 | -------------------------------------------------------------------------------- /scripts/nclt2visualizer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | from nclt2ros.visualizer.visualize import Visualize 5 | 6 | 7 | def main(): 8 | 9 | rospy.init_node('nclt2visualizer') 10 | rospy.loginfo("running node 'nclt2visualizer'") 11 | 12 | date = rospy.get_param('~date', '2013-01-10') 13 | show_plot = rospy.get_param('~show_plot', True) 14 | 15 | v_gt_kml = rospy.get_param('~gt_kml', False) 16 | v_gt_png = rospy.get_param('~gt_png', False) 17 | v_gps_kml = rospy.get_param('~gps_kml', False) 18 | v_gps_png = rospy.get_param('~gps_png', False) 19 | v_gps_rtk_kml = rospy.get_param('~gps_rtk_kml', False) 20 | v_gps_rtk_png = rospy.get_param('~gps_rtk_png', False) 21 | v_odom_kml = rospy.get_param('~odom_kml', False) 22 | v_odom_png = rospy.get_param('~odom_png', False) 23 | v_all = rospy.get_param('~all', False) 24 | 25 | visualizer = Visualize(date=date, show_plot=show_plot, 26 | v_gt_kml=v_gt_kml, v_gt_png=v_gt_png, v_gps_kml=v_gps_kml, v_gps_png=v_gps_png, 27 | v_gps_rtk_kml=v_gps_rtk_kml, v_gps_rtk_png=v_gps_rtk_png, v_odom_kml=v_odom_kml, 28 | v_odom_png=v_odom_png, v_all=v_all) 29 | 30 | rospy.loginfo("terminating node 'nclt2visualizer'") 31 | 32 | 33 | if __name__ == '__main__': 34 | try: 35 | main() 36 | except rospy.ROSInterruptException: 37 | pass -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from catkin_pkg.python_setup import generate_distutils_setup 3 | 4 | setup(**generate_distutils_setup( 5 | packages=['nclt2ros'], 6 | package_dir={'': ''} 7 | )) 8 | --------------------------------------------------------------------------------