跳到主要内容2025 年第 27 届中国机器人及人工智能大赛自主巡航实战经验分享 | 极客日志C++AI算法
2025 年第 27 届中国机器人及人工智能大赛自主巡航实战经验分享
分享了第 27 届中国机器人及人工智能大赛自主巡航项目的实战经验。文章指出系统鲁棒性和稳定性优于算法先进性,详细介绍了 ROS 多节点架构设计、激光雷达数据预处理、视觉自适应增强、Cartographer SLAM 配置、多模型融合识别方案以及 TEB 导航参数优化。同时总结了机械结构、光照变化和定位丢失等常见问题的解决方案,强调基础分获取、多传感器融合及故障容错机制的重要性。
战神70 浏览
作为连续两届参加中国机器人及人工智能大赛并拿下国一的"老兵",我想跟大家分享一些在自主巡航项目中的实战经验。这个项目看起来简单,但真正做起来才发现里面有太多坑需要踩,希望我的一些经验能让你少走弯路。
一、项目实战理解
刚开始接触这个项目时,我和团队都以为主要难点在于算法的精巧设计。结果第一年比赛只拿了个国二,回来复盘才发现,比赛成败的关键不在于算法多高级,而在于系统的鲁棒性和稳定性。
场地中那些任务信息图像看似简单,但在不同光照、不同角度下识别难度差异很大。记得去年决赛时,有支 985 高校的队伍用了很牛的深度学习算法,结果在现场因为光照问题,识别率直接掉到 40% 以下,连基本的任务点都没完成。
核心任务拆解:
- 语音识别与播报(10 分)
- 三次任务点识别与到达(60 分)
- 终点到达(10 分)
- 技术文档(10 分)
首先要确保 60 分的基础分稳稳拿到,才有机会冲击更高分数。
二、软件架构实战经验
ROS 框架设计
第一年我们用了单体架构,所有功能都堆在一个节点里,结果调试和找 bug 特别痛苦。第二年重构为多节点设计:

这种模块化设计好处太多了:
- 团队可以并行开发
- 单元测试变得简单
- 找 bug 和调试效率提高 10 倍不止
实战代码技巧
1. 激光数据预处理
比赛中经常会遇到激光数据异常的情况,这段代码帮我们解决了很多问题:
sensor_msgs::LaserScan filterScan(const sensor_msgs::LaserScan& scan) {
sensor_msgs::LaserScan filtered = scan;
for (size_t i = 0; i < scan.ranges.size(); i++) {
if (scan.ranges[i] < scan.range_min || scan.ranges[i] > scan.range_max || !std::isfinite(scan.ranges[i])) {
filtered.ranges[i] = scan.range_max;
}
}
for (size_t i = 1; i < filtered.ranges.size() - 1; i++) {
std::vector<float> window = { filtered.ranges[i], filtered.ranges[i], filtered.ranges[i] };
std::(window.(), window.());
filtered.ranges[i] = window[];
}
( i = ; i < filtered.ranges.(); i++) {
diff = std::(filtered.ranges[i] - filtered.ranges[i]);
(diff > && filtered.ranges[i] > ) {
filtered.ranges[i] = filtered.ranges[i];
}
}
filtered;
}
-1
+1
sort
begin
end
1
for
size_t
2
size
float
abs
-1
if
0.5
5.0
-1
return
2. 视觉自适应增强
不同场地光照差异很大,这段代码可以自动调整图像处理参数
def adaptive_image_processing(image):
"""根据图像特性自动调整处理参数"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
mean_brightness = np.mean(gray)
std_brightness = np.std(gray)
if mean_brightness < 80:
alpha = 1.5
beta = 25
elif mean_brightness > 180:
alpha = 0.7
beta = -20
else:
alpha = 1.0
beta = 0
adjusted = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
if std_brightness > 60:
lab = cv2.cvtColor(adjusted, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl = clahe.apply(l)
merged = cv2.merge((cl, a, b))
return cv2.cvtColor(merged, cv2.COLOR_LAB2BGR)
else:
return adjusted
这个函数在我们决赛中立了大功,解决了场馆顶部射灯照射导致的图像过曝问题。
三、核心算法实战选择
SLAM 定位算法
实战经验: 正式比赛我们用 Cartographer,虽然占用资源多,但闭环检测能力强,精度高。关键参数调优是:
# cartographer.lua 中的关键参数
TRAJECTORY_BUILDER_2D.submaps.num_range_data = 80 # 减小以降低延迟
TRAJECTORY_BUILDER_2D.min_range = 0.15 # 设置为激光雷达最小值
TRAJECTORY_BUILDER_2D.max_range = 12.0 # 设置为激光雷达最大值
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 5.0 # 处理遮挡
POSE_GRAPH.optimization_problem.huber_scale = 1e2 # 提高优化稳定性
POSE_GRAPH.constraint_builder.min_score = 0.55 # 提高约束阈值
视觉识别方案
不要迷信单一的深度学习模型,我们的最佳方案是多模型融合:
def ensemble_prediction(image):
"""多模型融合预测"""
yolo_results = yolo_model.predict(image)
resnet_results = resnet_model.predict(image)
all_boxes = []
for det in yolo_results:
x1, y1, x2, y2 = det['bbox']
confidence = det['confidence'] * 0.6
label = det['class']
all_boxes.append([x1, y1, x2, y2, confidence, label])
for det in resnet_results:
x1, y1, x2, y2 = det['bbox']
confidence = det['confidence'] * 0.4
label = det['class']
all_boxes.append([x1, y1, x2, y2, confidence, label])
final_boxes = non_maximum_suppression(all_boxes, 0.5)
final_results = []
for box_group in final_boxes:
avg_box = calculate_average_box(box_group)
labels = [b[5] for b in box_group]
label = most_common(labels)
confidence = sum([b[4] for b in box_group])
final_results.append({
'bbox': avg_box,
'class': label,
'confidence': confidence
})
return final_results
这种多模型融合方法在不稳定光照条件下识别率要高 15% 左右,虽然计算开销大了点,但值得。
四、导航平滑性优化
速度平滑滤波器
导航卡顿的核心问题是速度命令变化过于剧烈。我们实现了一种基于双指数平滑的速度命令过滤器:
class VelocityFilter {
public:
VelocityFilter(double alpha = 0.3, double beta = 0.1)
: alpha_(alpha), beta_(beta), s_x_(0), s_y_(0), s_theta_(0),
b_x_(0), b_y_(0), b_theta_(0), initialized_(false) {}
geometry_msgs::Twist filter(const geometry_msgs::Twist& raw_cmd) {
if (!initialized_) {
s_x_ = raw_cmd.linear.x;
s_y_ = raw_cmd.linear.y;
s_theta_ = raw_cmd.angular.z;
b_x_ = 0; b_y_ = 0; b_theta_ = 0;
initialized_ = true;
return raw_cmd;
}
double s_x_prev = s_x_; double s_y_prev = s_y_; double s_theta_prev = s_theta_;
s_x_ = alpha_ * raw_cmd.linear.x + (1 - alpha_) * (s_x_prev + b_x_);
s_y_ = alpha_ * raw_cmd.linear.y + (1 - alpha_) * (s_y_prev + b_y_);
s_theta_ = alpha_ * raw_cmd.angular.z + (1 - alpha_) * (s_theta_prev + b_theta_);
b_x_ = beta_ * (s_x_ - s_x_prev) + (1 - beta_) * b_x_;
b_y_ = beta_ * (s_y_ - s_y_prev) + (1 - beta_) * b_y_;
b_theta_ = beta_ * (s_theta_ - s_theta_prev) + (1 - beta_) * b_theta_;
geometry_msgs::Twist filtered_cmd;
filtered_cmd.linear.x = s_x_ + b_x_;
filtered_cmd.linear.y = s_y_ + b_y_;
filtered_cmd.angular.z = s_theta_ + b_theta_;
return filtered_cmd;
}
private:
double alpha_;
double beta_;
double s_x_, s_y_, s_theta_;
double b_x_, b_y_, b_theta_;
bool initialized_;
};
TEB 参数优化
通过对比测试,我们总结出一套最优的 TEB 局部规划器参数:
TebLocalPlannerROS:
max_vel_x: 0.35
max_vel_x_backwards: 0.1
max_vel_y: 0.35
max_vel_theta: 0.8
acc_lim_x: 0.2
acc_lim_y: 0.2
acc_lim_theta: 0.3
min_turning_radius: 0.0
teb_autosize: True
dt_ref: 0.4
dt_hysteresis: 0.1
global_plan_overwrite_orientation: True
max_global_plan_lookahead_dist: 2.0
feasibility_check_no_poses: 5
no_inner_iterations: 5
no_outer_iterations: 4
penalty_epsilon: 0.05
weight_max_vel_x: 1.0
weight_max_vel_y: 1.0
weight_max_vel_theta: 1.0
weight_acc_lim_x: 2.0
weight_acc_lim_y: 2.0
weight_acc_lim_theta: 2.0
weight_kinematics_nh: 1000.0
weight_kinematics_forward_drive: 100.0
weight_optimaltime: 1.0
weight_obstacle: 50.0
weight_inflation: 0.1
weight_adapt_factor: 2.0
enable_homotopy_class_planning: False
simple_exploration: False
在比赛间隙调整这些参数时,我习惯按照这个顺序调整:先调速度限制,再调加速度限制,然后是权重参数。最关键的三个参数是 weight_kinematics_forward_drive、weight_obstacle 和 acc_lim_theta。
五、实战踩坑与应对
1. 机械结构问题
第一年比赛,我们的摄像头固定方式太简单,用的 3D 打印支架。结果比赛中底盘急停导致摄像头大幅晃动,视觉识别全乱了。
解决方案: 第二年改用铝合金框架 + 减震垫,效果好很多。记住一点:不要低估机械振动对传感器的影响。
2. 光照变化
比赛场地光照往往是最大变数。记得去年西安的比赛,场馆靠窗一侧阳光直射,另一侧则很暗。
- 视觉算法做好自适应处理
- 提前 20 分钟进场调试
- 准备多套参数配置,快速切换
3. 定位丢失
导航过程中定位丢失是常见问题。一个队友不小心走进场地或者场地有高反光材料,都可能导致定位失败。
def emergency_relocation():
"""定位丢失应急处理"""
stop_navigation()
cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=1)
twist = Twist()
twist.angular.z = 0.5
start_time = rospy.Time.now()
rate = rospy.Rate(10)
while (rospy.Time.now() - start_time).to_sec() < 5.0:
cmd_vel.publish(twist)
rate.sleep()
twist.angular.z = 0.0
cmd_vel.publish(twist)
success = feature_based_relocalization()
if success:
rospy.loginfo("重定位成功,继续导航")
resume_navigation()
else:
rospy.logwarn("重定位失败,切换到紧急模式")
switch_to_emergency_mode()
六、总结与建议
如果让我给参加自主巡航项目的同学们一些建议,那就是:
- 基础分最重要:确保导航稳定可靠,拿到 60 分基础分,再追求更高分数
- 系统鲁棒性大于算法先进性:算法再先进,如果不够稳定,在比赛中也会翻车
- 充分测试各种极端情况:刻意制造干扰和异常,测试系统恢复能力
- 做好知识积累和经验传承:记录所有问题和解决方案,避免后来者重复踩坑
- 多传感器融合是解决单一传感器不稳定的最佳方案
- 参数自适应比固定参数在实际比赛中可靠得多
- 故障容错机制是区分一般队伍和优秀队伍的关键
最后,希望大家都能享受这个比赛带来的技术挑战!我当年就是在比赛中学到了很多东西,今年我依旧会来参加今年的比赛。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online