Skip to content

Commit c3daa89

Browse files
authored
Merge branch 'feature/4_project_semantic_image' into documentation
2 parents 0061aad + 0da0350 commit c3daa89

File tree

7 files changed

+276
-38
lines changed

7 files changed

+276
-38
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
*.pyc
22
*.bkp
33
*.orig
4-
.idea
4+
.idea*
55
.pytest_cache
66
.run/
77
docs/build
88
_build
9-
9+
.idea*
10+
.vscode*
11+
*.egg-info

elevation_mapping_cupy/config/parameters.yaml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ overlap_clear_range_xy: 4.0 # xy range [m] for clearing over
4141
overlap_clear_range_z: 2.0 # z range [m] for clearing overlapped area. cells outside this range will be cleared. (used for multi floor setting)
4242

4343
map_frame: 'odom' # The map frame where the odometry source uses.
44-
base_frame: 'base_footprint' # The robot's base frame. This frame will be a center of the map.
45-
corrected_map_frame: 'corrected_odom'
44+
base_frame: 'base' # The robot's base frame. This frame will be a center of the map.
45+
corrected_map_frame: 'odom_corrected'
4646

4747
#### Feature toggles ########
4848
enable_edge_sharpen: true
@@ -76,14 +76,14 @@ publishers:
7676
layers: ['elevation', 'traversability', 'variance']
7777
basic_layers: ['elevation', 'traversability']
7878
fps: 5.0
79-
elevation_map_recordable:
80-
layers: ['elevation', 'traversability']
81-
basic_layers: ['elevation', 'traversability']
82-
fps: 2.0
83-
elevation_map_filter:
84-
layers: ['min_filter', 'smooth', 'inpaint', 'upper_bound','rgb']
85-
basic_layers: ['min_filter']
86-
fps: 3.0
79+
# elevation_map_recordable:
80+
# layers: ['elevation', 'traversability']
81+
# basic_layers: ['elevation', 'traversability']
82+
# fps: 2.0
83+
# elevation_map_filter:
84+
# layers: ['min_filter', 'smooth', 'inpaint', 'upper_bound','rgb']
85+
# basic_layers: ['min_filter']
86+
# fps: 3.0
8787

8888
#### Initializer ########
8989
initialize_method: 'linear' # Choose one from 'nearest', 'linear', 'cubic'

elevation_mapping_cupy/config/sensor_parameter.yaml

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
subscribers:
2-
# sensor_name: #if you have multiple sensors assign different names
3-
# channels: ['features','person'] # names of the channels, for sem seg it should be the same name as the
4-
# pretrained network uses
5-
# fusion: ['average','class_average'] # the fusion algorithm type used.
6-
# Pick between: ['color', 'average','class_average']
7-
# topic_name: '/elevation_mapping/pointcloud_semantic' # of the published pointcloud subscribed by the el_map
2+
sensor_name:
3+
channels: ['feat_0','feat_1']
4+
fusion: ['average','average']
5+
topic_name: '/elevation_mapping/pointcloud_semantic'
6+
87
front_cam:
98
channels: ['rgb','feat_0','feat_1','grass','tree','fence','person']
109
fusion: ['color','average','average','class_average','class_average','class_average','class_average']
@@ -30,7 +29,33 @@ subscribers:
3029
confidence_topic: "/camera/depth/image_raw"
3130
confidence_threshold: 10
3231
feature_extractor: False
32+
33+
front_bpearl:
34+
channels: []
35+
fusion: ['average']
36+
topic_name: /robot_self_filter/bpearl_front/point_cloud
37+
38+
rear_bpearl:
39+
channels: []
40+
fusion: ['average']
41+
topic_name: /robot_self_filter/bpearl_rear/point_cloud
3342

34-
35-
36-
43+
front_depth:
44+
channels: []
45+
fusion: ['average']
46+
topic_name: /depth_camera_front/point_cloud_self_filtered
47+
48+
rear_depth:
49+
channels: []
50+
fusion: ['average']
51+
topic_name: /depth_camera_rear/point_cloud_self_filtered
52+
53+
left_depth:
54+
channels: []
55+
fusion: ['average']
56+
topic_name: /depth_camera_left/point_cloud_self_filtered
57+
58+
right_depth:
59+
channels: []
60+
fusion: ['average']
61+
topic_name: /depth_camera_right/point_cloud_self_filtered
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .parameter import Parameter
2+
from .elevation_mapping import ElevationMap

elevation_mapping_cupy/script/elevation_mapping_cupy/elevation_mapping.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@
99
import threading
1010
import subprocess
1111

12-
from .traversability_filter import get_filter_chainer, get_filter_torch
13-
from .parameter import Parameter
14-
from .custom_kernels import add_points_kernel, add_color_kernel, color_average_kernel
15-
from .custom_kernels import sum_kernel
16-
from .custom_kernels import error_counting_kernel
17-
from .custom_kernels import average_map_kernel
18-
from .custom_kernels import dilation_filter_kernel
19-
from .custom_kernels import normal_filter_kernel
20-
from .custom_kernels import polygon_mask_kernel
21-
from .map_initializer import MapInitializer
22-
from .plugins.plugin_manager import PluginManager
23-
from .semantic_map import SemanticMap
24-
25-
from .traversability_polygon import (
12+
from elevation_mapping_cupy.traversability_filter import get_filter_chainer, get_filter_torch
13+
from elevation_mapping_cupy.parameter import Parameter
14+
from elevation_mapping_cupy.custom_kernels import add_points_kernel, add_color_kernel, color_average_kernel
15+
from elevation_mapping_cupy.custom_kernels import sum_kernel
16+
from elevation_mapping_cupy.custom_kernels import error_counting_kernel
17+
from elevation_mapping_cupy.custom_kernels import average_map_kernel
18+
from elevation_mapping_cupy.custom_kernels import dilation_filter_kernel
19+
from elevation_mapping_cupy.custom_kernels import normal_filter_kernel
20+
from elevation_mapping_cupy.custom_kernels import polygon_mask_kernel
21+
from elevation_mapping_cupy.map_initializer import MapInitializer
22+
from elevation_mapping_cupy.plugins.plugin_manager import PluginManager
23+
from elevation_mapping_cupy.semantic_map import SemanticMap
24+
from elevation_mapping_cupy.traversability_polygon import (
2625
get_masked_traversability,
2726
is_traversable,
2827
calculate_area,
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
from elevation_mapping_cupy import ElevationMap
2+
from elevation_mapping_cupy import Parameter
3+
4+
# General
5+
import os
6+
import numpy as np
7+
8+
# ROS
9+
import rospy
10+
import ros_numpy
11+
from tf.transformations import quaternion_matrix
12+
import tf2_ros
13+
import rospkg
14+
15+
from sensor_msgs.msg import PointCloud2
16+
from grid_map_msgs.msg import GridMap
17+
from std_msgs.msg import Float32MultiArray
18+
from std_msgs.msg import MultiArrayLayout as MAL
19+
from std_msgs.msg import MultiArrayDimension as MAD
20+
21+
class ElevationMapWrapper():
22+
def __init__(self):
23+
rospack = rospkg.RosPack()
24+
self.root = rospack.get_path("elevation_mapping_cupy")
25+
weight_file = os.path.join(self.root, "config/weights.dat")
26+
plugin_config_file = os.path.join(self.root, "config/plugin_config.yaml")
27+
self.param = Parameter( use_chainer=False, weight_file=weight_file, plugin_config_file=plugin_config_file )
28+
self.node_name = "elevation_mapping"
29+
# ROS
30+
self.initalize_ros()
31+
self.register_subscribers()
32+
self.initalize_elevation_mapping()
33+
self.register_publishers()
34+
self.register_timers()
35+
36+
def initalize_elevation_mapping(self):
37+
self.param.update()
38+
self._pointcloud_process_counter = 0
39+
self._map = ElevationMap(self.param)
40+
self._map_data = np.zeros((self._map.cell_n - 2, self._map.cell_n - 2), dtype=np.float32)
41+
self._map_q = None
42+
self._map_t = None
43+
44+
def initalize_ros(self):
45+
rospy.init_node(self.node_name, anonymous=False)
46+
self._tf_buffer = tf2_ros.Buffer()
47+
self._listener = tf2_ros.TransformListener(self._tf_buffer)
48+
self.get_ros_params()
49+
50+
def register_subscribers(self):
51+
pointcloud_subs = {}
52+
additional = []
53+
fusion = []
54+
for key, config in self.subscribers.items():
55+
pointcloud_subs[key] = rospy.Subscriber(config["topic_name"], PointCloud2, self.pointcloud_callback, (config["channels"], config["fusion"]))
56+
for chan,fus in zip(config["channels"],config["fusion"]):
57+
if chan not in additional:
58+
additional.append(chan)
59+
fusion.append(fus)
60+
self.param.additional_layers = additional
61+
self.param.fusion_algorithms = fusion
62+
self.dtype = [
63+
("x", np.float32),
64+
("y", np.float32),
65+
("z", np.float32),
66+
]
67+
for chan in config["channels"]:
68+
self.dtype.append((chan, np.float32))
69+
70+
def register_publishers(self):
71+
# TODO publishers
72+
self._publishers = {}
73+
self._publishers_timers = []
74+
for k, v in self.publishers.items():
75+
print(f"Register Publisher: {k}")
76+
self._publishers[k] = rospy.Publisher(f"/{self.node_name}/{k}", GridMap, queue_size=10)
77+
self._publishers_timers.append( rospy.Timer(rospy.Duration(1/v["fps"]), lambda x: self.publish_map(x, k)))
78+
79+
def publish_map(self, t, k):
80+
print(k)
81+
if self._map_q is None:
82+
return
83+
84+
gm = GridMap()
85+
gm.info.header.frame_id = self.map_frame
86+
gm.info.header.stamp = rospy.Time.now()
87+
gm.info.header.seq = 0
88+
gm.info.resolution = self._map.resolution
89+
gm.info.length_x = self._map.map_length
90+
gm.info.length_y = self._map.map_length
91+
gm.info.pose.position.x = self._map_t.x
92+
gm.info.pose.position.y = self._map_t.y
93+
gm.info.pose.position.z = self._map_t.z
94+
gm.info.pose.orientation.w = 1 # self._map_q.w
95+
gm.info.pose.orientation.x = 0 # self._map_q.x
96+
gm.info.pose.orientation.y = 0 # self._map_q.y
97+
gm.info.pose.orientation.z = 0 # self._map_q.z
98+
gm.layers = self.publishers[k]["layers"]
99+
gm.basic_layers = self.publishers[k]["basic_layers"]
100+
101+
# arr.layout.dim = [int(gm.info.length_x/gm.info.resolution), int(gm.info.length_y/gm.info.resolution)]
102+
103+
for i, layer in enumerate(gm.layers):
104+
self._map.get_map_with_name_ref(layer, self._map_data)
105+
# self._map_data = np.nan_to_num(self._map_data, False, 0)
106+
data = self._map_data.copy()
107+
arr = Float32MultiArray()
108+
arr.layout = MAL()
109+
N = self._map_data.shape[0]
110+
arr.layout.dim.append( MAD(label="column_index",size=N,stride=int(N*N)))
111+
arr.layout.dim.append( MAD(label="row_index",size=N,stride=N))
112+
arr.data = tuple(np.ascontiguousarray(data).reshape(-1))
113+
gm.data.append( arr )
114+
115+
self._publishers[k].publish( gm )
116+
117+
def register_timers(self):
118+
self.timer_variance = rospy.Timer(rospy.Duration(1/self.update_variance_fps), self.update_variance)
119+
self.timer_pose = rospy.Timer(rospy.Duration(1/self.update_pose_fps), self.update_pose)
120+
self.timer_time = rospy.Timer(rospy.Duration(self.time_interval), self.update_time)
121+
122+
def pointcloud_callback(self, msg, config):
123+
# convert pcd into numpy array
124+
channels = ['x','y','z']+config[0]
125+
fusion = config[1]
126+
points = ros_numpy.numpify(msg)
127+
pts = np.empty((points.shape[0],0))
128+
for ch in channels:
129+
pts = np.append(pts,np.expand_dims(points[ch],1),axis=1)
130+
131+
# get pose of pointcloud
132+
t = rospy.Time(secs=msg.header.stamp.secs, nsecs=msg.header.stamp.nsecs)
133+
try:
134+
transform = self._tf_buffer.lookup_transform(self.map_frame, msg.header.frame_id, t, rospy.Duration(1.0))
135+
except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException) as e:
136+
print(e)
137+
return
138+
139+
t = transform.transform.translation
140+
t = np.array( [t.x, t.y, t.z] )
141+
q = transform.transform.rotation
142+
R = quaternion_matrix([q.w, q.x, q.y, q.z])[:3,:3]
143+
144+
# process pointcloud
145+
self._map.input(pts, channels, R, t, 0, 0)
146+
self._pointcloud_process_counter += 1
147+
print(self._pointcloud_process_counter)
148+
149+
def update_pose(self, t):
150+
# get pose of base
151+
t = rospy.Time.now()
152+
try:
153+
transform = self._tf_buffer.lookup_transform(self.map_frame, self.base_frame, t, rospy.Duration(1.0))
154+
except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException) as e:
155+
print(e)
156+
return
157+
t = transform.transform.translation
158+
trans = np.array( [t.x, t.y, t.z] )
159+
q = transform.transform.rotation
160+
rot = quaternion_matrix([q.w, q.x, q.y, q.z])[:3,:3]
161+
self._map.move_to(trans, rot)
162+
163+
self._map_t = t
164+
self._map_q = q
165+
166+
def update_variance(self, t):
167+
self._map.update_variance()
168+
169+
def update_time(self, t):
170+
self._map.update_time()
171+
172+
def get_ros_params(self):
173+
# TODO fix this here when later launching with launch-file
174+
# This is currently {p} elevation_mapping")
175+
para = os.path.join(self.root, "config/parameters.yaml")
176+
sens = os.path.join(self.root, "config/sensor_parameter.yaml")
177+
os.system(f"rosparam delete /{self.node_name}")
178+
os.system(f"rosparam load {para} elevation_mapping")
179+
os.system(f"rosparam load {sens} elevation_mapping")
180+
self.subscribers = rospy.get_param("~subscribers")
181+
self.publishers = rospy.get_param("~publishers")
182+
self.initialize_frame_id = rospy.get_param("~initialize_frame_id", "base")
183+
self.initialize_tf_offset = rospy.get_param("~initialize_tf_offset", 0.0)
184+
self.pose_topic = rospy.get_param("~pose_topic", "pose")
185+
self.map_frame = rospy.get_param("~map_frame", "map")
186+
self.base_frame = rospy.get_param("~base_frame", "base")
187+
self.corrected_map_frame = rospy.get_param("~corrected_map_frame", "corrected_map")
188+
self.initialize_method = rospy.get_param("~initialize_method", "cubic")
189+
self.position_lowpass_alpha = rospy.get_param("~position_lowpass_alpha", 0.2)
190+
self.orientation_lowpass_alpha = rospy.get_param("~orientation_lowpass_alpha", 0.2)
191+
self.recordable_fps = rospy.get_param("~recordable_fps", 3.0)
192+
self.update_variance_fps = rospy.get_param("~update_variance_fps", 1.0)
193+
self.time_interval = rospy.get_param("~time_interval", 0.1)
194+
self.update_pose_fps = rospy.get_param("~update_pose_fps", 10.0)
195+
self.initialize_tf_grid_size = rospy.get_param("~initialize_tf_grid_size", 0.5)
196+
self.map_acquire_fps = rospy.get_param("~map_acquire_fps", 5.0)
197+
self.publish_statistics_fps = rospy.get_param("~publish_statistics_fps", 1.0)
198+
self.enable_pointcloud_publishing = rospy.get_param("~enable_pointcloud_publishing", False)
199+
self.enable_normal_arrow_publishing = rospy.get_param("~enable_normal_arrow_publishing", False)
200+
self.enable_drift_corrected_TF_publishing = rospy.get_param("~enable_drift_corrected_TF_publishing", False)
201+
self.use_initializer_at_start = rospy.get_param("~use_initializer_at_start", False)
202+
203+
if __name__ == '__main__':
204+
emw = ElevationMapWrapper()
205+
206+
while not rospy.is_shutdown():
207+
try:
208+
rospy.spin()
209+
except rospy.ROSInterruptException:
210+
print("Error")

elevation_mapping_cupy/script/elevation_mapping_cupy/parameter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import pickle
77
import numpy as np
88
from simple_parsing.helpers import Serializable
9-
9+
from dataclasses import field
1010

1111
@dataclass
1212
class Parameter(Serializable):
1313
resolution: float = 0.04
14-
additional_layers: list = ['feat_0'].copy
15-
fusion_algorithms: list = ['average'].copy
14+
additional_layers: list = field(default_factory=lambda: ["feat_0"])
15+
fusion_algorithms: list = field(default_factory=lambda: ["average"])
1616
cell_n: int = None
1717
data_type: str = np.float32
1818

0 commit comments

Comments
 (0)