@@ -29,9 +29,9 @@ void PathAlignCritic::initialize()
2929 getParam (threshold_to_consider_, " threshold_to_consider" , 0 .5f );
3030 getParam (use_path_orientations_, " use_path_orientations" , false );
3131 getParam (use_geometric_alignment_, " use_geometric_alignment" , false );
32- getParam (search_window_, " search_window" , 2.0 );
32+ getParam (search_window_, " search_window" , 0.5 );
3333 getParam (score_arc_length_, " score_arc_length" , true );
34- getParam (lookahead_distance_, " lookahead_distance" , 5 .0f );
34+ getParam (lookahead_distance_, " lookahead_distance" , 1 .0f );
3535
3636 if (!score_arc_length_ && !use_geometric_alignment_) {
3737 RCLCPP_WARN (logger_,
@@ -110,63 +110,65 @@ void PathAlignCritic::scoreArcLength(CriticData & data, std::vector<bool> & path
110110 unsigned int path_pt = 0u ;
111111 float traj_integrated_distance = 0 .0f ;
112112
113- const Eigen::Index traj_length = data.trajectories .x .cols ();
114- const int effective_stride = std::max (1 , std::min (trajectory_point_step_,
115- static_cast <int >(traj_length)));
116- const int num_sample_points = (traj_length + effective_stride - 1 ) / effective_stride;
113+ int strided_traj_rows = data.trajectories .x .rows ();
114+ int strided_traj_cols = floor ((data.trajectories .x .cols () - 1 ) / trajectory_point_step_) + 1 ;
115+ int outer_stride = strided_traj_rows * trajectory_point_step_;
116+ // Get strided trajectory information
117+ const auto T_x = Eigen::Map<const Eigen::ArrayXXf, 0 ,
118+ Eigen::Stride<-1 , -1 >>(
119+ data.trajectories .x .data (),
120+ strided_traj_rows, strided_traj_cols, Eigen::Stride<-1 , -1 >(outer_stride, 1 ));
121+ const auto T_y = Eigen::Map<const Eigen::ArrayXXf, 0 ,
122+ Eigen::Stride<-1 , -1 >>(
123+ data.trajectories .y .data (),
124+ strided_traj_rows, strided_traj_cols, Eigen::Stride<-1 , -1 >(outer_stride, 1 ));
125+ const auto T_yaw = Eigen::Map<const Eigen::ArrayXXf, 0 ,
126+ Eigen::Stride<-1 , -1 >>(
127+ data.trajectories .yaws .data (), strided_traj_rows, strided_traj_cols,
128+ Eigen::Stride<-1 , -1 >(outer_stride, 1 ));
129+ const auto traj_sampled_size = T_x.cols ();
117130
118- // Score each trajectory in the batch
119131 for (size_t t = 0 ; t < batch_size; ++t) {
120132 summed_path_dist = 0 .0f ;
121133 num_samples = 0u ;
122134 traj_integrated_distance = 0 .0f ;
123135 path_pt = 0u ;
124-
125- // Start from first trajectory point (close to current pose, one time step ahead)
126- float prev_x = data.trajectories .x (t, 0 );
127- float prev_y = data.trajectories .y (t, 0 );
128-
129- // Sample trajectory points at regular intervals
130- for (int sample_idx = 1 ; sample_idx < num_sample_points; ++sample_idx) {
131- const Eigen::Index traj_col = sample_idx * effective_stride;
132- if (traj_col >= traj_length) {break ;}
133-
134- const float curr_x = data.trajectories .x (t, traj_col);
135- const float curr_y = data.trajectories .y (t, traj_col);
136-
137- // Accumulate distance traveled along trajectory
138- dx = curr_x - prev_x;
139- dy = curr_y - prev_y;
136+ float Tx_m1 = T_x (t, 0 );
137+ float Ty_m1 = T_y (t, 0 );
138+ for (int p = 1 ; p < traj_sampled_size; p++) {
139+ const float Tx = T_x (t, p);
140+ const float Ty = T_y (t, p);
141+ dx = Tx - Tx_m1;
142+ dy = Ty - Ty_m1;
143+ Tx_m1 = Tx;
144+ Ty_m1 = Ty;
140145 traj_integrated_distance += sqrtf (dx * dx + dy * dy);
141-
142- // Find corresponding path point at same arc length distance
143146 path_pt = utils::findClosestPathPtBinary (
144147 cumulative_distances_.data (), path_segments_count,
145148 traj_integrated_distance);
146149
147- // Calculate distance from trajectory point to corresponding path point
150+ // The nearest path point to align to needs to be not in collision, else
151+ // let the obstacle critic take over in this region due to dynamic obstacles
148152 if (path_pts_valid[path_pt]) {
149153 const auto & pose = path[path_pt];
150- dx = pose.x - curr_x ;
151- dy = pose.y - curr_y ;
154+ dx = pose.x - Tx ;
155+ dy = pose.y - Ty ;
152156 num_samples++;
153157 if (use_path_orientations_) {
154- dyaw = angles::shortest_angular_distance (pose.theta , data. trajectories . yaws (t, traj_col ));
158+ dyaw = angles::shortest_angular_distance (pose.theta , T_yaw (t, p ));
155159 summed_path_dist += sqrtf (dx * dx + dy * dy + dyaw * dyaw);
156160 } else {
157161 summed_path_dist += sqrtf (dx * dx + dy * dy);
158162 }
159163 }
160-
161- prev_x = curr_x;
162- prev_y = curr_y;
163164 }
164-
165- // Average distance across all sampled points
166- cost (t) = (num_samples > 0u ) ? summed_path_dist / static_cast <float >(num_samples) : 0 .0f ;
165+ if (num_samples > 0u ) {
166+ cost (t) = summed_path_dist / static_cast <float >(num_samples);
167+ } else {
168+ cost (t) = 0 .0f ;
169+ }
167170 }
168171
169- // Apply weight and power, then add to total costs
170172 if (power_ > 1u ) {
171173 data.costs += (cost * weight_).pow (power_);
172174 } else {
@@ -200,19 +202,22 @@ void PathAlignCritic::scoreGeometric(CriticData & data, std::vector<bool> & path
200202 const float robot_y = data.state .pose .pose .position .y ;
201203 const size_t path_segments_count = *data.furthest_reached_path_point ;
202204 const Eigen::Index num_segments = static_cast <Eigen::Index>(path_size_cache_) - 1 ;
203-
205+
204206 Eigen::Index current_closest_seg = 0 ;
205207 float min_dist_sq = std::numeric_limits<float >::max ();
206-
208+
207209 // Find initial hint by locating robot position on path
208- for (Eigen::Index i = 0 ; i < num_segments && i < static_cast <Eigen::Index>(path_segments_count); ++i) {
210+ for (Eigen::Index i = 0 ;
211+ i < num_segments && i < static_cast <Eigen::Index>(path_segments_count);
212+ ++i)
213+ {
209214 const float dist_sq = distSqToSegment (robot_x, robot_y, i);
210215 if (dist_sq < min_dist_sq) {
211216 min_dist_sq = dist_sq;
212217 current_closest_seg = i;
213218 }
214219 }
215-
220+
216221 closest_indices_.setConstant (current_closest_seg);
217222
218223 const int effective_stride = std::max (1 , std::min (trajectory_point_step_,
@@ -221,13 +226,13 @@ void PathAlignCritic::scoreGeometric(CriticData & data, std::vector<bool> & path
221226 // Score each trajectory in the batch
222227 for (Eigen::Index traj_idx = 0 ; traj_idx < batch_size; ++traj_idx) {
223228 float accumulated_distance = 0 .0f ;
224-
229+
225230 // Start from first trajectory point
226231 float prev_x = traj_x (traj_idx, 0 );
227232 float prev_y = traj_y (traj_idx, 0 );
228-
233+
229234 int sample_idx = 0 ;
230-
235+
231236 // Sample trajectory points at regular intervals
232237 while (true ) {
233238 const Eigen::Index traj_col = sample_idx * effective_stride;
@@ -241,7 +246,7 @@ void PathAlignCritic::scoreGeometric(CriticData & data, std::vector<bool> & path
241246 const float dx = px - prev_x;
242247 const float dy = py - prev_y;
243248 accumulated_distance += std::sqrt (dx * dx + dy * dy);
244-
249+
245250 // Stop scoring beyond lookahead distance
246251 if (accumulated_distance > lookahead_distance_) {break ;}
247252 }
@@ -257,10 +262,8 @@ void PathAlignCritic::scoreGeometric(CriticData & data, std::vector<bool> & path
257262 cost_array (traj_idx) += min_dist;
258263 valid_sample_count (traj_idx)++;
259264 }
260-
261265 // Update hint for next sample
262266 closest_indices_ (traj_idx) = closest_seg;
263-
264267 prev_x = px;
265268 prev_y = py;
266269 sample_idx++;
@@ -295,7 +298,7 @@ void PathAlignCritic::updatePathCache(const models::Path & path, size_t path_seg
295298 const size_t path_points_count = path_segments_count + 1 ;
296299
297300 // Only update cache if path changed
298- // TODO: Optimize path change detection. Current element-wise comparison is expensive.
301+ // TODO(@silanus23) : Optimize path change detection. Current element-wise comparison is expensive.
299302 // Possible approaches:
300303 // 1. Add hash field to models::Path (cleanest, needs controller-level change)
301304 // 2. Compute local hash here (self-contained, small overhead)
@@ -331,6 +334,7 @@ void PathAlignCritic::updatePathCache(const models::Path & path, size_t path_seg
331334 segment_dx_.resize (num_segments);
332335 segment_dy_.resize (num_segments);
333336 segment_len_sq_.resize (num_segments);
337+ segment_inv_len_sq_.resize (num_segments);
334338 }
335339
336340 // Precompute segment geometry for efficient distance calculations
@@ -341,45 +345,13 @@ void PathAlignCritic::updatePathCache(const models::Path & path, size_t path_seg
341345 segment_len_sq_ (i) = segment_dx_ (i) * segment_dx_ (i) + segment_dy_ (i) * segment_dy_ (i);
342346 segment_lengths_ (i) = std::sqrt (segment_len_sq_ (i));
343347 cumulative_distances_ (i + 1 ) = cumulative_distances_ (i) + segment_lengths_ (i);
348+ segment_inv_len_sq_ (i) = (segment_len_sq_ (i) > 1e-6f ) ? (1 .0f / segment_len_sq_ (i)) : 0 .0f ;
344349 }
345350}
346- // TODO: Even I have tried other ways and spend effort maybe there can still be
347- // room for improvement here
348- float PathAlignCritic::distSqToSegment (float px, float py, Eigen::Index seg_idx)
349- {
350- const float x1 = path_x_cache_ (seg_idx);
351- const float y1 = path_y_cache_ (seg_idx);
352- const float x2 = path_x_cache_ (seg_idx + 1 );
353- const float y2 = path_y_cache_ (seg_idx + 1 );
354-
355- const float dx = x2 - x1;
356- const float dy = y2 - y1;
357- const float len_sq = dx * dx + dy * dy;
358-
359- if (len_sq < 1e-6f ) {
360- // Degenerate segment, return distance to point
361- const float dpx = px - x1;
362- const float dpy = py - y1;
363- return dpx * dpx + dpy * dpy;
364- }
365-
366- // Project point onto line, clamped to segment
367- const float t = std::max (0 .0f , std::min (1 .0f , ((px - x1) * dx + (py - y1) * dy) / len_sq));
368-
369- const float closest_x = x1 + t * dx;
370- const float closest_y = y1 + t * dy;
371-
372- const float dist_x = px - closest_x;
373- const float dist_y = py - closest_y;
374-
375- return dist_x * dist_x + dist_y * dist_y;
376- }
377-
378351
352+ // TODO(@silanus23): Not sure if this should be in util too
379353float PathAlignCritic::computeMinDistanceToPath (float px, float py, Eigen::Index & closest_seg_idx)
380354{
381- static constexpr float MIN_SEGMENT_LENGTH_SQ = 1e-6f ;
382-
383355 const Eigen::Index num_segments = static_cast <Eigen::Index>(path_size_cache_) - 1 ;
384356 if (num_segments < 1 ) {return std::numeric_limits<float >::max ();}
385357
@@ -401,10 +373,10 @@ float PathAlignCritic::computeMinDistanceToPath(float px, float py, Eigen::Index
401373 float min_dist_sq = std::numeric_limits<float >::max ();
402374 Eigen::Index best_seg = closest_seg_idx;
403375
404- // Search only within window for efficiency (O(window_size) instead of O(path_length))
376+ // Search only within window for efficiency
405377 for (Eigen::Index seg_idx = start_idx; seg_idx < num_segments; ++seg_idx) {
406378 if (cumulative_distances_ (seg_idx) > end_distance) {break ;}
407- if (segment_len_sq_ (seg_idx) < MIN_SEGMENT_LENGTH_SQ ) {continue ;} // Skip degenerate segments
379+ if (segment_len_sq_ (seg_idx) < 1e- 6f ) {continue ;} // Skip degenerate segments
408380
409381 const float dist_sq = distSqToSegment (px, py, seg_idx);
410382
0 commit comments