跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonWeChatAI算法

智能车竞赛惯导与视觉避障实战经验分享

智能车竞赛中网络延迟与视觉定位是常见痛点。本文分享了基于路由器的网络优化方案,上位机可视化辅助驾驶技巧,以及利用深度相机提升扫码效率的方法。针对终点返回难题,提出了基于地图固定元素校准和 YOLO 识别校正的两种惯导融合思路,并给出了逆透视变换的具体数学推导。此外,还包含 STM32 舵机对称性调整、串口频率优化及数据集自动化处理脚本,旨在为参赛团队提供从底层硬件到上层算法的完整实战参考。

whyljy2903发布于 2026/3/23更新于 2026/6/2420 浏览
智能车竞赛惯导与视觉避障实战经验分享

前言

在全国大学生智能车竞赛(智慧医疗机器人创意赛)中,我们团队取得了不错的成绩。作为技术负责人,我想分享一下备赛过程中关于惯导、视觉处理及系统优化的实际思路。比赛竞争激烈,细节决定成败,以下经验希望能帮助到后续参赛的同学。

网络稳定性优化

网络延迟是比赛中常见的痛点,直接影响云端 API 调用和实时控制。第一年参赛时我们也遇到过严重延迟问题,第二年重点解决了这一环节。

硬件选择上,我们使用了华为 BE7 Pro 路由器。在校赛阶段发现其他队伍连接困难,而我们的设备运行稳定。在正式赛场上,我们将信道固定在 165,这个频段通常干扰较少。调试期间若遇到中继模式导致的延迟波动,建议直接切换至本地部署方案,避免依赖不稳定的云端服务。

连接方式上,上位机务必使用有线网络连接路由器,不要依赖板载无线网卡。这能显著降低丢包率和抖动。

上位机辅助可视化

为了帮助机师快速判断障碍物位置,我们在上位机端开发了一个基于 Tkinter 的辅助界面。它接收 YOLO 的检测数据,将障碍物和终点信息绘制在画布上,相当于游戏里的小地图。

核心逻辑是订阅 ROS 话题,解析目标坐标后映射到屏幕像素。例如,对于障碍物,我们计算其在图像中的矩形区域并绘制红线;对于终点,则标记置信度。

import tkinter as tk
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy

class LLM2Origincar:
    def __init__(self, host, port):
        self.ros = None
        self.host = host
        self.port = port
        self.roadblock_list = []
        self.end_list = []
        self.init_ros()
        self.init_topic()
        self.init_thread()
        self.keep()

    def init_topic(self):
        # 订阅感知目标话题
        self.yolo_sub = Topic(self.ros, '/hobot_dnn_detection', 'ai_msgs/msg/PerceptionTargets', latch=True)
        self.yolo_sub.subscribe(self.yolo_sub_callback)

    def yolo_sub_callback(self, msg):
        self.roadblock_list.clear()
        self.end_list.clear()
        for target in msg['targets']:
            if target['type'] == 'roadblock':
                rect = target['rois'][0]['rect']
                self.roadblock_list.append({
                    'x': rect['x_offset'],
                    'w': rect['width'],
                    'b': rect['y_offset'] + rect['height'],
                })
            elif target['type'] == 'end':
                rect = target['rois'][0]['rect']
                self.end_list.append({
                    'x': rect['x_offset'],
                    'y': rect['y_offset'],
                    'w': rect['width'],
                    'b': rect['y_offset'] + rect['height'],
                    'c': target['rois'][0]['confidence'],
                })

    def keep(self):
        try:
            while True:
                canvas.delete("all")
                # 绘制车道线参考
                canvas.create_line(141, 0, 141, 680, fill="red", width=1)
                canvas.create_line(689, 0, 689, 680, fill="red", width=1)
                
                if self.roadblock_list:
                    for obst in self.roadblock_list:
                        b = int(obst['b'] * 1.42) # 高度缩放系数
                        x_start = int(obst['x'] * 1.41)
                        x_end = int((obst['x'] + obst['w']) * 1.41)
                        canvas.create_line(x_start, b, x_end, b, fill="red", width=2)
                
                if self.end_list:
                    for end in self.end_list:
                        x1 = int(end['x'] * 1.41)
                        y1 = int(end['y'] * 1.41)
                        x2 = int((end['x'] + end['w']) * 1.41)
                        y2 = int(end['b'] * 1.42)
                        canvas.create_line(x1, y2, x2, y2, fill="blue", width=1)
                        canvas.create_text(int((x1+x2)/2), max(0, y1-20), text=f"conf:{end['c']:.2f}", fill='cyan')
        except Exception as e:
            print(f"Visualization error: {e}")

此外,我们还添加了键盘快捷键来控制任务状态切换,比如连接信号、激活遥操作、退出遥操作等,方便现场调试。

半场扫码优化

小橙小车配备的深度相机在扫码清晰度上远优于 USB 相机。深度相机不仅能提供深度信息,其成像质量也更适合 OCR 识别。

扫码节点不应一直运行,这会消耗大量 CPU。我们设置了触发条件:仅在任务一且小车全局坐标 X 超过 2 米时开启扫码。同时,我们对图像进行了裁剪,去除了底部无效区域,减小了检测模型的输入尺寸,提升了推理速度。

import rclpy
from rclpy.node import Node
import cv2
import numpy as np
from sensor_msgs.msg import Image
from std_msgs.msg import String, Int32
from nav_msgs.msg import Odometry
from origincar_msg.msg import Sign
from cv_bridge import CvBridge

TASK1 = 1
TASK2_WAITFOR_CMD = 2
TASK2 = 3
TASK3 = 4
TASK_STOP = 5

class QrCodeDetection(Node):
    def __init__(self):
        super().__init__('QRcodeSub')
        self.Sign4ReturnSub = self.create_subscription(Int32, 'sign4return', self.sign4return_callback, 10)
        self.ImageSub = self.create_subscription(Image, '/aurora/rgb/image_raw', self.image_callback, 10)
        self.OdomSub = self.create_subscription(Odometry, '/odom_combined', self.Odom_callback, 10)
        self.qrcode_publisher = self.create_publisher(String, "/qrcode_information", 10)
        self.info_result = String()
        self.sign_publisher = self.create_publisher(Sign, '/sign_switch', 10)
        self.sign_msg = Sign()
        
        # 加载微信二维码模型
        self.detector = cv2.wechat_qrcode_WeChatQRCode(
            "/userdata/WorkSpace/codes/src/qrcode/qrcode/model/detect.prototxt",
            "/userdata/WorkSpace/codes/src/qrcode/qrcode/model/detect.caffemodel",
            "/userdata/WorkSpace/codes/src/qrcode/qrcode/model/sr.prototxt",
            "/userdata/WorkSpace/codes/src/qrcode/qrcode/model/sr.caffemodel"
        )
        self.bridge = CvBridge()
        self.node_run = False
        self.task = TASK1

    def image_callback(self, msg):
        if self.node_run and (self.task == TASK1 or self.task == TASK2):
            # 裁剪图像,去除底部无效区域
            cv2_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='mono8')[155:, :]
            res = self.detector.detectAndDecode(cv2_image)[0]
            if res:
                self.node_run = False
                for r in res:
                    self.info_result.data = str(r)
                    self.qrcode_publisher.publish(self.info_result)
                    self.get_logger().info(f"Detected QR: {self.info_result.data}")
                    
                    if self.info_result.data == "AntiClockWise":
                        self.sign_msg.sign_data = 4
                    elif self.info_result.data == "ClockWise":
                        self.sign_msg.sign_data = 3
                    else:
                        try:
                            data = int(r)
                            self.sign_msg.sign_data = 3 if data % 2 else 4
                        except:
                            pass
                    self.sign_publisher.publish(self.sign_msg)
                    self.info_result.data = "None"
                    self.sign_msg.sign_data = 0

    def sign4return_callback(self, msg):
        if msg.data in [0, -1]:
            self.task = TASK1
            self.node_run = False
        elif msg.data == 5:
            self.task = TASK2
        elif msg.data == 6:
            self.task = TASK3

    def Odom_callback(self, msg):
        if self.task == TASK1 and msg.pose.pose.position.x > 2:
            self.node_run = True

准确返回 P 点策略

P 点定位是任务二的关键。我们总结了两种校准思路。

思路一:利用地图固定元素校准

此方法需要重置里程计。每次从通道出来后停在固定位置,以车头方向为 X+,左侧为 Y+,建立局部坐标系。通过逆透视变换(IPM)计算前方两条固定标线的相对位置,结合已知的终点坐标,反推当前车辆相对于终点的位姿。

具体步骤如下:

  1. 固定车辆位置,拍摄照片,记录终点坐标(如 1.9m, -1.5m)。
  2. 利用 IPM 计算该视角下两条标线的像素坐标 A, B。
  3. 在实际行驶中,再次获取标线坐标 A', B'。
  4. 求解旋转矩阵 R 和平移向量 t,从而计算出当前视角下的终点 P'。

数学推导基于二维平面变换: $$ \Delta A'B' = R \Delta AB $$ $$ R = \Delta A'B' \cdot \Delta AB^{-1} $$ $$ t = A' - RA $$ $$ P' = RP + t $$

Python 实现示例:

def end_point(x1, y1, x2, y2, x3, y3, x1_, y1_, x2_, y2_):
    delta_x = x1 - x2
    delta_y = y1 - y2
    delta_x_ = x1_ - x2_
    delta_y_ = y1_ - y2_
    den = delta_x ** 2 + delta_y ** 2
    
    a = (delta_x * delta_x_ + delta_y * delta_y_) / den
    b = (delta_x * delta_y_ - delta_y * delta_x_) / den
    
    tx = x1_ - a * x1 + b * y1
    ty = y1_ - b * x1 - a * y1
    
    x3_ = a * x3 - b * y3 + tx
    y3_ = b * x3 + a * y3 + ty
    return x3_, y3_

思路二:YOLO 识别校正

此方法无需停车重置里程计,适合高速场景。全程保持里程计运行,利用 YOLO 识别 P 点中心像素,结合单应性矩阵转换到全局坐标。

# 单应性矩阵 H
H = np.array([
    [-4.66389128e-04, -2.26288030e-04, -4.92300831e-02],
    [7.59821540e-04, 5.20569143e-05, -2.33074608e-01],
    [-6.59643252e-04, -7.15022786e-03, 1.00000000e+00],
])

def pixel2global(self, pixel_x, pixel_y):
    pixel = np.array([pixel_x, pixel_y, 1], dtype=np.float32)
    local = np.dot(H, pixel)
    local /= local[2]
    local[0] += 0.25
    
    car_cos = np.cos(self.current_pos[2])
    car_sin = np.sin(self.current_pos[2])
    global_x = self.current_pos[0] + car_cos * local[0] - car_sin * local[1]
    global_y = self.current_pos[1] + car_sin * local[0] + car_cos * local[1]
    return global_x, global_y

训练数据方面,我们采集了大量包含 P 点的图片,并进行增强。注意区分任务三的白色网格区域,避免误检。

STM32 底层调优

STM32 源码中,舵机转角的多项式计算是关键。我们发现左右转角的限制不对称,导致转向半径不一致。通过调整多项式的二次项系数,使左右最大转角对应的舵机量一致,改善了车辆的对称性。

此外,为了提高惯导频率,我们将串口波特率提升至 921600,发送频率设为 50Hz。同时关闭 CAN、蓝牙等未使用外设,仅保留串口 1 和 3。EKF 配置文件中,imu 的频率也需同步调整为 50Hz。

车型号部分,我们删除了电位器读取逻辑,固定为 Ackerman 底盘,简化了启动流程。

数据处理脚本

为了高效处理数据集,我们编写了自动化脚本,包括自动标注、无效数据清理、数据增强及分批次打包。

自动标注脚本

利用已有模型对图片进行预标注,人工仅需复核,大幅提升效率。

数据清洗与增强

脚本会自动检查图片与标签是否对应,删除空标签或无标签的图片。增强策略包含亮度、对比度调整和高斯模糊,但不包含旋转,以保持赛道特征一致性。

任务分包

支持将数据集按数量切分为多个压缩包,便于团队成员并行标注。

以上脚本均经过验证,可直接用于项目初始化阶段。希望这些经验能为你的备赛之路提供参考。

目录

  1. 前言
  2. 网络稳定性优化
  3. 上位机辅助可视化
  4. 半场扫码优化
  5. 准确返回 P 点策略
  6. 思路一:利用地图固定元素校准
  7. 思路二:YOLO 识别校正
  8. 单应性矩阵 H
  9. STM32 底层调优
  10. 数据处理脚本
  11. 自动标注脚本
  12. 数据清洗与增强
  13. 任务分包
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • uniapp 通用价格组件开发与 DCloud 插件市场发布指南
  • 大模型 Agent 设计模式全解析:原理与代码示例
  • AI 大模型推理优化技术详解
  • 二叉树深度优先搜索(DFS)算法详解与实战
  • RTX 4070 本地部署 Stable Diffusion 教程:环境搭建与 4K 人像生成
  • Java 主流 JDK 版本核心区别与流行原因分析
  • Qwen-Image-Edit-2511 与 Stable Diffusion 图像编辑能力对比
  • Linux 文件权限详解:类型、访问者及操作指令
  • WindowsCleaner v5.0:基于 Python Tkinter 的磁盘清理与系统优化工具
  • 基于 Docker 在 Linux 上搭建单机 PostgreSQL 数据库
  • 企业级工作流引擎低代码开发实战:RuoYi-Flowable-Plus 指南
  • SeaTunnel 2.3.11 + Web 1.0.3 Docker 部署及 Kafka 同步 Hive/ES 指南
  • Web2Executable 实用指南:将网页快速封装为桌面应用
  • Spring AI 基础入门:环境搭建与模型接入实战
  • 常见 Web 漏洞渗透及防护方法
  • MCP 协议详解:与 Function Call 的区别及使用思路
  • 基于 Playwright 封装反爬爬虫:隐藏自动化特征实战
  • MySQL 数据库核心操作:创建、修改、备份与连接排查
  • 网络安全入门核心知识体系与学习路径指南
  • 钉钉 Webhook 完全指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online