Skip to content

Commit 7e21e3d

Browse files
committed
tuning
1 parent 196e4ab commit 7e21e3d

8 files changed

Lines changed: 103 additions & 27 deletions

File tree

elevation_mapping_cupy/config/setups/g1/g1_parameters.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
resolution: 0.04 # resolution in m.
33
map_length: 8 # map's size in m.
44
sensor_noise_factor: 0.05 # point's noise is sensor_noise_factor*z^2 (z is distance from sensor).
5-
mahalanobis_thresh: 2.0 # points outside this distance is outlier.
5+
mahalanobis_thresh: 1.5 # points outside this distance is outlier.
66
outlier_variance: 0.01 # if point is outlier, add this value to the cell.
77
drift_compensation_variance_inler: 0.05 # cells under this value is used for drift compensation.
88
max_drift: 0.1 # drift compensation happens only the drift is smaller than this value (for safety)
@@ -12,7 +12,7 @@ max_variance: 100.0 # maximum variance for each cell
1212
initial_variance: 1000.0 # initial variance for each cell.
1313
traversability_inlier: 0.9 # cells with higher traversability are used for drift compensation.
1414
dilation_size: 3 # dilation filter size before traversability filter.
15-
wall_num_thresh: 8 # if there are more points than this value, only higher points than the current height are used to make the wall more sharp.
15+
wall_num_thresh: 20 # if there are more points than this value, only higher points than the current height are used to make the wall more sharp.
1616
min_height_drift_cnt: 100 # drift compensation only happens if the valid cells are more than this number.
1717
position_noise_thresh: 0.01 # if the position change is bigger than this value, the drift compensation happens.
1818
orientation_noise_thresh: 0.01 # if the orientation change is bigger than this value, the drift compensation happens.
@@ -23,15 +23,15 @@ max_height_range: -0.1 # points higher than this value
2323
ramped_height_range_a: 0.3 # if z > max(d - ramped_height_range_b, 0) * ramped_height_range_a + ramped_height_range_c, reject.
2424
ramped_height_range_b: 1.0 # if z > max(d - ramped_height_range_b, 0) * ramped_height_range_a + ramped_height_range_c, reject.
2525
ramped_height_range_c: 0.2 # if z > max(d - ramped_height_range_b, 0) * ramped_height_range_a + ramped_height_range_c, reject.
26-
update_variance_fps: 5.0 # fps for updating variance.
27-
update_pose_fps: 10.0 # fps for updating pose and shift the center of map.
26+
update_variance_fps: 10.0 # fps for updating variance.
27+
update_pose_fps: 20.0 # fps for updating pose and shift the center of map.
2828
time_interval: 0.1 # Time layer is updated with this interval.
29-
map_acquire_fps: 5.0 # Raw map is fetched from GPU memory in this fps.
29+
map_acquire_fps: 10.0 # Raw map is fetched from GPU memory in this fps.
3030
publish_statistics_fps: 1.0 # Publish statistics topic in this fps.
3131

3232
max_ray_length: 10.0 # maximum length for ray tracing.
33-
cleanup_step: 0.001 # subtitute this value from validity layer at visibiltiy cleanup.
34-
cleanup_cos_thresh: 0.8 # subtitute this value from validity layer at visibiltiy cleanup.
33+
cleanup_step: 0.005 # subtitute this value from validity layer at visibiltiy cleanup.
34+
cleanup_cos_thresh: 0.7 # subtitute this value from validity layer at visibiltiy cleanup.
3535

3636
safe_thresh: 0.7 # if traversability is smaller, it is counted as unsafe cell.
3737
safe_min_thresh: 0.4 # polygon is unsafe if there exists lower traversability than this.
@@ -47,7 +47,7 @@ corrected_map_frame: 'odom_corrected'
4747
#### Feature toggles ########
4848
enable_edge_sharpen: true
4949
enable_visibility_cleanup: true
50-
enable_drift_compensation: true
50+
enable_drift_compensation: false
5151
enable_overlap_clearance: true
5252
enable_pointcloud_publishing: false
5353
enable_drift_corrected_TF_publishing: false

elevation_mapping_cupy/config/setups/g1/g1_sensor_parameter.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ subscribers:
3737
Livox:
3838
topic_name: /livox/lidar_filtered
3939
data_type: pointcloud
40-
D435:
41-
topic_name: /camera/depth_registered/points
42-
data_type: pointcloud
40+
# Frame whose origin is the true sensor optical center, used as the visibility-cleanup
41+
# ray source (and point-validity sensor origin). Required because the cloud is deskewed
42+
# into 'odom', so its header frame is no longer the sensor. If omitted, the cloud's
43+
# header frame is used (original behavior).
44+
ray_source_frame: livox_frame
45+
46+
# Odin:
47+
# topic_name: /odin1/cloud_slam
48+
# data_type: pointcloud
49+
# ray_source_frame: odin1_base_link

elevation_mapping_cupy/include/elevation_mapping_cupy/elevation_mapping_ros.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class ElevationMappingNode {
9191
void readParameters();
9292
void setupMapPublishers();
9393
void pointcloudCallback(const sensor_msgs::PointCloud2& cloud, const std::string& key);
94-
void inputPointCloud(const sensor_msgs::PointCloud2& cloud, const std::vector<std::string>& channels);
94+
void inputPointCloud(const sensor_msgs::PointCloud2& cloud, const std::vector<std::string>& channels, const std::string& key);
9595
void inputImage(const sensor_msgs::ImageConstPtr& image_msg, const sensor_msgs::CameraInfoConstPtr& camera_info_msg, const std::vector<std::string>& channels);
9696
void imageCallback(const sensor_msgs::ImageConstPtr& image_msg, const sensor_msgs::CameraInfoConstPtr& camera_info_msg, const std::string& key);
9797
void imageChannelCallback(const sensor_msgs::ImageConstPtr& image_msg, const sensor_msgs::CameraInfoConstPtr& camera_info_msg, const elevation_map_msgs::ChannelInfoConstPtr& channel_info_msg);
@@ -159,6 +159,10 @@ class ElevationMappingNode {
159159
std::set<double> map_fps_unique_;
160160
std::vector<ros::Timer> mapTimers_;
161161
std::map<std::string, std::vector<std::string>> channels_;
162+
// Optional per-subscriber frame whose origin is used as the visibility-cleanup ray source
163+
// (and point validity sensor origin), instead of the point cloud's header frame. Needed when
164+
// clouds are published in a non-sensor frame (e.g. deskewed into odom).
165+
std::map<std::string, std::string> raySourceFrames_;
162166

163167
std::vector<std::string> initialize_frame_id_;
164168
std::vector<double> initialize_tf_offset_;

elevation_mapping_cupy/include/elevation_mapping_cupy/elevation_mapping_wrapper.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ElevationMappingWrapper {
4747
void initialize(ros::NodeHandle& nh);
4848

4949
void input(const RowMatrixXd& points, const std::vector<std::string>& channels, const RowMatrixXd& R, const Eigen::VectorXd& t,
50-
const double positionNoise, const double orientationNoise);
50+
const Eigen::VectorXd& sensorOrigin, const double positionNoise, const double orientationNoise);
5151
void input_image(const std::vector<ColMatrixXf>& multichannel_image, const std::vector<std::string>& channels, const RowMatrixXd& R,
5252
const Eigen::VectorXd& t, const RowMatrixXd& cameraMatrix, const Eigen::VectorXd& D, const std::string distortion_model, int height, int width);
5353
void move_to(const Eigen::VectorXd& p, const RowMatrixXd& R);

elevation_mapping_cupy/script/elevation_mapping_cupy/elevation_mapping.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,16 +313,19 @@ def shift_translation_to_map_center(self, t):
313313
"""
314314
t -= self.center
315315

316-
def update_map_with_kernel(self, points_all, channels, R, t, position_noise, orientation_noise):
316+
def update_map_with_kernel(self, points_all, channels, R, t, position_noise, orientation_noise, sensor_t=None):
317317
"""Update map with new measurement.
318318
319319
Args:
320320
points_all (cupy._core.core.ndarray):
321321
channels (List[str]):
322322
R (cupy._core.core.ndarray):
323-
t (cupy._core.core.ndarray):
323+
t (cupy._core.core.ndarray): Translation of the point-cloud frame (used to place points).
324324
position_noise (float):
325325
orientation_noise (float):
326+
sensor_t (cupy._core.core.ndarray): Origin of the sensor (ray_source_frame) used for
327+
visibility-cleanup ray tracing and point validity checks. Defaults to ``t`` when None,
328+
which reproduces the original behavior (cloud header frame == sensor frame).
326329
"""
327330
self.new_map *= 0.0
328331
error = cp.array([0.0], dtype=cp.float32)
@@ -331,6 +334,13 @@ def update_map_with_kernel(self, points_all, channels, R, t, position_noise, ori
331334
# additional_fusion = self.get_fusion_of_pcl(channels)
332335
with self.map_lock:
333336
self.shift_translation_to_map_center(t)
337+
# Resolve the sensor origin used for ray tracing / validity. It must be expressed
338+
# in the same map-center-relative frame as t, so apply the same shift.
339+
if sensor_t is None:
340+
sensor_t = t
341+
else:
342+
sensor_t = cp.asarray(sensor_t, dtype=self.data_type).copy()
343+
self.shift_translation_to_map_center(sensor_t)
334344
self.error_counting_kernel(
335345
self.elevation_map,
336346
points,
@@ -360,6 +370,7 @@ def update_map_with_kernel(self, points_all, channels, R, t, position_noise, ori
360370
cp.array([0.0], dtype=self.data_type),
361371
R,
362372
t,
373+
sensor_t,
363374
self.normal_map,
364375
points,
365376
self.elevation_map,
@@ -439,16 +450,19 @@ def input_pointcloud(
439450
t: cp._core.core.ndarray,
440451
position_noise: float,
441452
orientation_noise: float,
453+
sensor_t: cp._core.core.ndarray = None,
442454
):
443455
"""Input the point cloud and fuse the new measurements to update the elevation map.
444456
445457
Args:
446458
raw_points (cupy._core.core.ndarray):
447459
channels (List[str]):
448460
R (cupy._core.core.ndarray):
449-
t (cupy._core.core.ndarray):
461+
t (cupy._core.core.ndarray): Translation of the point-cloud frame.
450462
position_noise (float):
451463
orientation_noise (float):
464+
sensor_t (cupy._core.core.ndarray): Origin of the sensor (ray_source_frame) used for
465+
visibility cleanup and validity checks. Defaults to ``t`` when None.
452466
453467
Returns:
454468
None:
@@ -463,6 +477,7 @@ def input_pointcloud(
463477
cp.asarray(t, dtype=self.data_type),
464478
position_noise,
465479
orientation_noise,
480+
None if sensor_t is None else cp.asarray(sensor_t, dtype=self.data_type),
466481
)
467482

468483
def input_image(

elevation_mapping_cupy/script/elevation_mapping_cupy/kernels/custom_kernels.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def add_points_kernel(
142142
enable_visibility_cleanup=True,
143143
):
144144
add_points_kernel = cp.ElementwiseKernel(
145-
in_params="raw U center_x, raw U center_y, raw U R, raw U t, raw U norm_map",
145+
in_params="raw U center_x, raw U center_y, raw U R, raw U t, raw U sensor_t, raw U norm_map",
146146
out_params="raw U p, raw U map, raw T newmap",
147147
preamble=map_utils(
148148
resolution,
@@ -165,7 +165,7 @@ def add_points_kernel(
165165
U z = transform_p(rx, ry, rz, R[6], R[7], R[8], t[2]);
166166
U v = z_noise(rz);
167167
int idx = get_idx(x, y, center_x[0], center_y[0]);
168-
if (is_valid(x, y, z, t[0], t[1], t[2])) {
168+
if (is_valid(x, y, z, sensor_t[0], sensor_t[1], sensor_t[2])) {
169169
if (is_inside(idx)) {
170170
U map_h = map[get_map_idx(idx, 0)];
171171
U map_v = map[get_map_idx(idx, 1)];
@@ -197,7 +197,11 @@ def add_points_kernel(
197197
}
198198
if (${enable_visibility_cleanup}) {
199199
float16 ray_x, ray_y, ray_z;
200-
float16 ray_length = ray_vector(t[0], t[1], t[2], x, y, z, ray_x, ray_y, ray_z);
200+
// Visibility cleanup traces rays from the sensor origin. Use sensor_t (the
201+
// configured ray_source_frame origin) instead of t (the point-cloud frame
202+
// origin), so deskewed clouds whose header frame is not the sensor still get
203+
// the true sensor optical center as the ray start. Defaults to t upstream.
204+
float16 ray_length = ray_vector(sensor_t[0], sensor_t[1], sensor_t[2], x, y, z, ray_x, ray_y, ray_z);
201205
ray_length = min(ray_length, (float16)${max_ray_length});
202206
int last_nidx = -1;
203207
for (float16 s=${ray_step}; s < ray_length; s+=${ray_step}) {
@@ -223,7 +227,7 @@ def add_points_kernel(
223227
224228
// If point is close or is farther away than ray length, skip.
225229
float16 d = (x - nx) * (x - nx) + (y - ny) * (y - ny) + (z - nz) * (z - nz);
226-
if (d < 0.1 || !is_valid(x, y, z, t[0], t[1], t[2])) {continue;}
230+
if (d < 0.1 || !is_valid(x, y, z, sensor_t[0], sensor_t[1], sensor_t[2])) {continue;}
227231
228232
// If invalid, do upper bound check, then skip
229233
if (nmap_valid < 0.5) {
@@ -258,7 +262,7 @@ def add_points_kernel(
258262
}
259263
}
260264
p[i * 3]= idx;
261-
p[i * 3 + 1] = is_valid(x, y, z, t[0], t[1], t[2]);
265+
p[i * 3 + 1] = is_valid(x, y, z, sensor_t[0], sensor_t[1], sensor_t[2]);
262266
p[i * 3 + 2] = is_inside(idx);
263267
"""
264268
).substitute(

elevation_mapping_cupy/src/elevation_mapping_ros.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ ElevationMappingNode::ElevationMappingNode(ros::NodeHandle& nh)
8484
channels_[key].push_back("x");
8585
channels_[key].push_back("y");
8686
channels_[key].push_back("z");
87+
// Optional override of the frame used as the visibility-cleanup ray source. If the cloud is
88+
// published in a non-sensor frame (e.g. deskewed into odom), set this to the true sensor frame.
89+
if (subscriber.second.hasMember("ray_source_frame")) {
90+
raySourceFrames_[key] = static_cast<std::string>(subscriber.second["ray_source_frame"]);
91+
ROS_INFO_STREAM("[" << key << "] ray_source_frame override: " << raySourceFrames_[key]);
92+
}
8793
boost::function<void(const sensor_msgs::PointCloud2&)> f = boost::bind(&ElevationMappingNode::pointcloudCallback, this, _1, key);
8894
ros::Subscriber sub = nh_.subscribe<sensor_msgs::PointCloud2>(pointcloud_topic, 1, f);
8995
pointcloudSubs_.push_back(sub);
@@ -310,14 +316,14 @@ void ElevationMappingNode::pointcloudCallback(const sensor_msgs::PointCloud2& cl
310316
auto& field = fields[it];
311317
channels.push_back(field.name);
312318
}
313-
inputPointCloud(cloud, channels);
319+
inputPointCloud(cloud, channels, key);
314320

315321
// This is used for publishing as statistics.
316322
pointCloudProcessCounter_++;
317323
}
318324

319325
void ElevationMappingNode::inputPointCloud(const sensor_msgs::PointCloud2& cloud,
320-
const std::vector<std::string>& channels) {
326+
const std::vector<std::string>& channels, const std::string& key) {
321327
auto start = ros::Time::now();
322328
auto* pcl_pc = new pcl::PCLPointCloud2;
323329
pcl::PCLPointCloud2ConstPtr cloudPtr(pcl_pc);
@@ -351,15 +357,53 @@ void ElevationMappingNode::inputPointCloud(const sensor_msgs::PointCloud2& cloud
351357
return;
352358
}
353359

360+
// Resolve the sensor origin used for visibility-cleanup ray tracing and point validity.
361+
// By default this is the origin of the cloud's header frame. If a ray_source_frame is
362+
// configured for this subscriber (e.g. because the cloud is deskewed into odom and its
363+
// header frame no longer coincides with the sensor), look up that frame's origin instead.
364+
Eigen::Vector3d rayOrigin = transformationSensorToMap.translation();
365+
std::string rayFrameUsed = sensorFrameId;
366+
auto rayFrameIt = raySourceFrames_.find(key);
367+
if (rayFrameIt != raySourceFrames_.end() && !rayFrameIt->second.empty()) {
368+
tf::StampedTransform raySourceTf;
369+
Eigen::Affine3d raySourceToMap;
370+
try {
371+
transformListener_.waitForTransform(mapFrameId_, rayFrameIt->second, timeStamp, ros::Duration(1.0));
372+
transformListener_.lookupTransform(mapFrameId_, rayFrameIt->second, timeStamp, raySourceTf);
373+
poseTFToEigen(raySourceTf, raySourceToMap);
374+
rayOrigin = raySourceToMap.translation();
375+
rayFrameUsed = rayFrameIt->second;
376+
} catch (tf::TransformException& ex) {
377+
// A configured ray_source_frame that cannot be resolved must NOT silently fall back to the
378+
// cloud header frame: that origin (e.g. odom) is exactly what causes the supporting surface
379+
// to be erased by visibility cleanup. Drop this cloud instead and raise a loud alert.
380+
ROS_ERROR_STREAM_THROTTLE(
381+
2.0, "\n"
382+
<< "========================================================================\n"
383+
<< " [ELEVATION MAPPING] ray_source_frame NOT AVAILABLE -- SKIPPING CLOUD\n"
384+
<< " subscriber key : " << key << "\n"
385+
<< " ray_source_frame : " << rayFrameIt->second << "\n"
386+
<< " map frame : " << mapFrameId_ << "\n"
387+
<< " TF error : " << ex.what() << "\n"
388+
<< " This point cloud is NOT used for mapping. Fix the TF tree or the\n"
389+
<< " ray_source_frame setting in the sensor parameter YAML.\n"
390+
<< "========================================================================");
391+
return;
392+
}
393+
}
394+
ROS_INFO_STREAM_THROTTLE(5.0, "[" << key << "] point frame: " << sensorFrameId << ", cleanup ray source frame: "
395+
<< rayFrameUsed << ", ray origin in " << mapFrameId_ << " = ["
396+
<< rayOrigin.x() << ", " << rayOrigin.y() << ", " << rayOrigin.z() << "]");
397+
354398
double positionError{0.0};
355399
double orientationError{0.0};
356400
{
357401
std::lock_guard<std::mutex> lock(errorMutex_);
358402
positionError = positionError_;
359403
orientationError = orientationError_;
360404
}
361-
map_.input(points, channels, transformationSensorToMap.rotation(), transformationSensorToMap.translation(), positionError,
362-
orientationError);
405+
map_.input(points, channels, transformationSensorToMap.rotation(), transformationSensorToMap.translation(), rayOrigin,
406+
positionError, orientationError);
363407

364408
if (enableDriftCorrectedTFPublishing_) {
365409
publishMapToOdom(map_.get_additive_mean_error());

elevation_mapping_cupy/src/elevation_mapping_wrapper.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,12 @@ void ElevationMappingWrapper::setParameters(ros::NodeHandle& nh) {
171171
}
172172

173173
void ElevationMappingWrapper::input(const RowMatrixXd& points, const std::vector<std::string>& channels, const RowMatrixXd& R,
174-
const Eigen::VectorXd& t, const double positionNoise, const double orientationNoise) {
174+
const Eigen::VectorXd& t, const Eigen::VectorXd& sensorOrigin, const double positionNoise,
175+
const double orientationNoise) {
175176
py::gil_scoped_acquire acquire;
176177
map_.attr("input_pointcloud")(Eigen::Ref<const RowMatrixXd>(points), channels, Eigen::Ref<const RowMatrixXd>(R),
177-
Eigen::Ref<const Eigen::VectorXd>(t), positionNoise, orientationNoise);
178+
Eigen::Ref<const Eigen::VectorXd>(t), positionNoise, orientationNoise,
179+
Eigen::Ref<const Eigen::VectorXd>(sensorOrigin));
178180
}
179181

180182
void ElevationMappingWrapper::input_image(const std::vector<ColMatrixXf>& multichannel_image, const std::vector<std::string>& channels, const RowMatrixXd& R,

0 commit comments

Comments
 (0)