多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

深度学习机器人人群导航:从感知-决策闭环到Jetson实时部署

深度学习机器人人群导航:从感知-决策闭环到Jetson实时部署 简介本资源是一份面向本科毕业设计与人工智能课程实践的深度学习机器人人群导航系统完整实现聚焦于复杂动态环境中智能体的安全自主导航问题适用于人工智能、机器人学方向的高年级本科生及入门级研究者。压缩包共146个文件含95个Python核心算法与仿真脚本涵盖CNN/LSTM模型构建、crowd_sim模拟器集成、SGDQN强化学习训练等、14张可视化结果PNG图如test_safe_5human.gif等避障效果动图、9份PDF技术文档含设计说明、实验报告与算法原理以及环境配置脚本sh/requirements.txt和ROS消息定义msg文件整体8.26MB结构清晰开箱即用。已有41人学习下载读者可直接复现端到端导航流程获取从数据预处理、模型训练、仿真测试到运动控制策略落地的全链路代码与配套说明尤其适合开展期末大作业、课程设计或小型科研验证。1. 为什么传统导航在人群里会“失明”深度学习不是加个模型就灵而是重写感知-决策闭环你有没有试过让机器人在食堂、地铁口或展会现场自主穿行ROS 的move_base一上人群就卡死局部规划器反复震荡全局路径被行人实时截断激光雷达点云里人腿被误判成静态障碍DWA 算法调参调到凌晨三点最后发现——它根本分不清“站着不动的柱子”和“随时会横移一步的大学生”。这不是算力不够是传统基于几何建模规则决策的导航范式在动态、非结构化、语义模糊的人群场景中从底层逻辑上就失效了。而“基于深度学习的机器人人群导航.zip”这个标题背后不是简单套个 ResNet 分类人多还是人少而是用端到端或分层深度模型把激光雷达RGB-DIMU 多源信号直接映射为安全导航动作如左偏0.3m/减速至0.2m/s/原地等待1.7s跳过人工定义“可通行区域”“社会力参数”这些玄学环节。它适合三类人正在做服务机器人落地的嵌入式工程师要跑在Jetson Orin上、高校做导航方向毕设的研二学生需复现调参、以及工业AGV厂商算法组里被客户投诉“进不了医院走廊”的技术负责人。核心价值不在“用了深度学习”而在用数据驱动替代先验假设让机器人第一次真正“看懂”人群的意图与节奏。2. 从原始数据到导航动作四步构建可训练的深度导航流水线人群导航不是图像分类输入是时序多模态传感器流输出是带物理约束的动作序列。直接套用CNN或Transformer会翻车。我一般会拆成四个强耦合但可独立调试的模块数据对齐 → 行人状态编码 → 社交上下文建模 → 动作解码。下面每步都给出最小可运行命令和关键参数说明所有代码均适配 PyTorch 1.13 和 ROS NoeticUbuntu 20.04。2.1 对齐激光雷达、RGB-D与IMU时间戳硬同步比插值更可靠人群场景下毫秒级时间偏移会导致激光点云与人体框错位进而让模型学到错误关联。很多开源方案用message_filters做软同步但在高动态场景丢包率超15%。我的做法是硬件级触发用 Arduino Nano 输出 100Hz 方波信号同时接入激光雷达的外部同步口如 RPLIDAR A3 的 EXT_SYNC和 Realsense D435 的 GPIO 触发引脚并在 ROS 中强制所有话题以该信号为时间基准发布。# 启动硬同步后的多传感器节点需提前烧录Arduino固件 roslaunch robot_nav sync_sensors.launch \ laser_topic:/scan_sync \ depth_topic:/camera/aligned_depth_to_color/image_raw_sync \ rgb_topic:/camera/color/image_raw_sync \ imu_topic:/imu/data_sync提示sync_sensors.launch中关键参数sync_tolerance:0.0055ms容差必须设为0.005而非默认0.1否则在人群快速移动时/scan_sync与/camera/color/image_raw_sync的帧对齐失败率从3%飙升至37%。实测用示波器抓取两路信号硬同步后抖动 0.8ms。2.2 将激光雷达点云转为“行人中心热图”比直接喂点云更鲁棒直接将原始点云如 1080×1 维向量输入网络模型极易过拟合到特定雷达型号的噪声模式。我们借鉴 Social-STGCNN 的思路将扫描范围划分为 32×32 的栅格对每个栅格计算是否有行人检测框中心落入来自 YOLOv5s DeepSORT 跟踪该栅格内点云距离均值反映障碍密度相邻栅格距离方差反映运动突变最终生成 3 通道热图行人置信度、距离均值、距离方差尺寸 32×32×3。转换脚本如下# generate_heatmap.py import numpy as np import cv2 from sensor_msgs.msg import LaserScan, Image from detection_msgs.msg import BoundingBoxes # 自定义行人检测消息 def scan_to_heatmap(scan_msg: LaserScan, bbox_list: BoundingBoxes) - np.ndarray: # 初始化3通道热图 heatmap np.zeros((32, 32, 3), dtypenp.float32) # 步骤1将激光角度映射到32x32栅格坐标极坐标转栅格 angles np.linspace(scan_msg.angle_min, scan_msg.angle_max, len(scan_msg.ranges)) ranges np.array(scan_msg.ranges) valid_mask (ranges scan_msg.range_min) (ranges scan_msg.range_max) # 极坐标转直角坐标以机器人中心为原点 x ranges[valid_mask] * np.cos(angles[valid_mask]) y ranges[valid_mask] * np.sin(angles[valid_mask]) # 映射到32x32栅格x∈[-5,5], y∈[-5,5] → 栅格索引[0,31] grid_x np.clip(((x 5) / 10 * 31).astype(int), 0, 31) grid_y np.clip(((y 5) / 10 * 31).astype(int), 0, 31) # 步骤2填充距离均值通道通道0 for gx, gy in zip(grid_x, grid_y): heatmap[gy, gx, 0] 1 # 计数 heatmap[gy, gx, 1] ranges[valid_mask][np.where((grid_xgx)(grid_ygy))[0]] # 累加距离 # 步骤3归一化距离均值通道1 count_map heatmap[:, :, 0] heatmap[:, :, 1] np.divide(heatmap[:, :, 1], count_map, outnp.zeros_like(heatmap[:, :, 1]), wherecount_map!0) # 步骤4填充行人置信度通道2——仅当检测框中心落入对应栅格 for box in bbox_list.bounding_boxes: if box.Class person: cx, cy (box.xmin box.xmax)/2, (box.ymin box.ymax)/2 # 将图像坐标(cx,cy)反推到机器人坐标系下的(x,y)再映射到栅格 # 此处省略相机标定矩阵R/T实际需用cv2.projectPoints gx_img, gy_img int(cx/640*32), int(cy/480*32) # 粗略映射正式部署需精确投影 if 0 gx_img 32 and 0 gy_img 32: heatmap[gy_img, gx_img, 2] max(heatmap[gy_img, gx_img, 2], box.probability) return heatmap # shape: (32, 32, 3)参数说明scan_msg.range_min0.15和range_max10.0必须严格匹配雷达实际量程x∈[-5,5], y∈[-5,5]是经验范围——覆盖机器人前方3米内主要冲突区超出部分栅格信息被裁剪避免稀疏点云干扰。实测若扩大到[-10,10]模型收敛速度下降40%因无效栅格引入噪声。2.3 用时空图卷积建模行人交互为什么GNN比LSTM更适合人群LSTM 处理行人轨迹时把每个人当作独立序列完全忽略“张三减速是因为李四突然左转”这类空间依赖。而图神经网络GNN天然适合建模这种关系。我们构建动态图节点跟踪ID边权重欧氏距离倒数×相对速度夹角余弦。关键创新是边权重不固定——每帧重新计算且加入“社会力衰减因子”距离0.8m时权重×1.52.5m时权重×0.2。# social_graph.py import torch import torch.nn as nn from torch_geometric.data import Data from torch_geometric.nn import GCNConv class SocialGNN(nn.Module): def __init__(self, input_dim4, hidden_dim64, output_dim2): super().__init__() self.gcn1 GCNConv(input_dim, hidden_dim) self.gcn2 GCNConv(hidden_dim, hidden_dim) self.fc nn.Linear(hidden_dim, output_dim) def forward(self, x, edge_index, edge_weight): # x: [N, 4] 每个节点特征[vx, vy, ax, ay]当前帧速度加速度 # edge_index: [2, E] 边连接索引 # edge_weight: [E] 动态计算的边权重 x torch.relu(self.gcn1(x, edge_index, edge_weight)) x torch.relu(self.gcn2(x, edge_index, edge_weight)) return self.fc(x) # 输出每个节点的导航修正量 [N, 2] def build_dynamic_graph(tracks: list) - Data: # tracks: [{id:0, x:1.2, y:0.5, vx:0.3, vy:0.1}, ...] N len(tracks) if N 0: return Data(xtorch.zeros(0,4), edge_indextorch.empty(2,0,dtypetorch.long), edge_weighttorch.empty(0)) # 构建节点特征矩阵 x x torch.tensor([[t[vx], t[vy], t[ax], t[ay]] for t in tracks], dtypetorch.float) # 构建边全连接图KNN会漏掉远距离但关键的交互如迎面走来的行人 edge_index torch.combinations(torch.arange(N), r2).t().contiguous() edge_index torch.cat([edge_index, edge_index.flip(0)], dim1) # 无向图转双向 # 计算边权重带社会力衰减 edge_weight [] for i, j in zip(edge_index[0], edge_index[1]): dx tracks[i][x] - tracks[j][x] dy tracks[i][y] - tracks[j][y] dist np.sqrt(dx**2 dy**2) # 相对速度夹角余弦cosθ (v_i·v_j)/(|v_i||v_j|) v_i np.array([tracks[i][vx], tracks[i][vy]]) v_j np.array([tracks[j][vx], tracks[j][vy]]) cos_theta np.dot(v_i, v_j) / (np.linalg.norm(v_i)*np.linalg.norm(v_j) 1e-6) weight 1.0 / (dist 1e-6) * cos_theta # 社会力衰减 if dist 0.8: weight * 1.5 elif dist 2.5: weight * 0.2 edge_weight.append(weight) edge_weight torch.tensor(edge_weight, dtypetorch.float) return Data(xx, edge_indexedge_index, edge_weightedge_weight)避坑点torch.combinations生成的边索引必须用.contiguous()否则在GCNConv中触发 CUDA illegal memory access。实测未加此操作时训练第3轮GPU显存报错率100%。另外cos_theta分母加1e-6防止除零但dist的1e-6不可省略——当两人几乎重合时如电梯门口dist≈0会导致权重爆炸模型梯度直接 NaN。3. 模型选型与轻量化为什么不用ViT而用MobileNetV3ST-GCN混合架构看到“深度学习”就上 ViT 或 Swin Transformer在 Jetson Orin 上ViT-Base 单帧推理耗时 210ms而人群导航要求控制周期 ≤ 100ms10Hz。我们必须在精度和实时性间做硬取舍。经过在 ETH Zurich Pedestrian Dataset 和我们的自采校园食堂数据集含 127 小时视频标注 89 万帧行人轨迹上的对比测试最终选定MobileNetV3-small视觉分支 ST-GCN时序分支 轻量级动作解码器的混合架构。这不是妥协而是工程最优解。3.1 视觉分支MobileNetV3 替代 ResNet参数量降为 1/7ResNet-18 在 224×224 输入下参数量 11.2M而 MobileNetV3-small 仅 1.5M且针对 ARM 架构做了深度可分离卷积优化。关键改动输入分辨率从 224×224 降至 128×128人群场景无需细粒度纹理最后一层全局平均池化前插入nn.AdaptiveAvgPool2d((4,4))强制空间维度一致避免不同距离行人导致特征图尺寸波动移除所有 BatchNorm 层改用 GroupNorm小批量训练时 BN 方差大导致导航抖动# vision_backbone.py from torchvision.models import mobilenet_v3_small class MobileNetV3Nav(nn.Module): def __init__(self, num_classes2): # 输出[v_linear, v_angular] super().__init__() self.backbone mobilenet_v3_small(pretrainedTrue) # 替换分类头为导航头 self.backbone.classifier nn.Sequential( nn.Dropout(p0.2, inplaceTrue), nn.Linear(self.backbone.classifier[3].in_features, 128), nn.Hardswish(), # MobileNetV3 原生激活函数 nn.GroupNorm(4, 128), # 替代 BN nn.Linear(128, num_classes) ) def forward(self, x): # x: [B, 3, 128, 128] 热图转RGB伪彩色图3通道 return self.backbone(x) # 使用 OpenCV 生成伪彩色热图替代matplotlib提速3倍 def heatmap_to_rgb(heatmap: np.ndarray) - np.ndarray: # heatmap: (32,32,3) → resize to (128,128) → apply colormap h_resized cv2.resize(heatmap, (128,128), interpolationcv2.INTER_NEAREST) # 归一化到 [0,255] h_norm cv2.normalize(h_resized, None, 0, 255, cv2.NORM_MINMAX) # 转为 uint8 并应用 COLORMAP_JET h_uint8 h_norm.astype(np.uint8) rgb_img cv2.applyColorMap(h_uint8[:,:,0], cv2.COLORMAP_JET) # 只用通道0行人置信度做主视觉 return rgb_img # shape: (128,128,3)参数说明interpolationcv2.INTER_NEAREST是关键——双线性插值会模糊热图边缘导致模型误判行人边界最近邻插值保留锐利过渡实测在密集人群场景下 mAP0.5 提升 5.2%。COLORMAP_JET比COLORMAP_VIRIDIS更有效因红色高置信度在 Jetson 的 GPU 图像处理流水线中响应更快。3.2 时序分支ST-GCN 替代 LSTM显存占用降为 1/3LSTM 处理 10 帧轨迹每帧 20 个行人 × 4 维需 1.2GB 显存而 ST-GCN 仅 380MB。其核心是将行人轨迹视为图上的时空信号空间维度用 GCN 建模交互时间维度用 TCNTemporal Convolutional Network捕获运动趋势。我们精简了原 ST-GCN 的 9 层结构只保留 3 层空间卷积→时间卷积→空间卷积并用深度可分离 TCN 替代标准 TCN。# stgcn_backbone.py import torch.nn as nn import torch.nn.functional as F class STGCNBlock(nn.Module): def __init__(self, in_channels, out_channels, A, stride1): super().__init__() self.gcn ConvGraph(in_channels, out_channels, A) # 空间卷积 self.tcn nn.Sequential( nn.Conv1d(out_channels, out_channels, 3, stridestride, padding1, groupsout_channels), # 深度可分离 nn.BatchNorm1d(out_channels), nn.ReLU(inplaceTrue), nn.Conv1d(out_channels, out_channels, 1) # 逐点卷积 ) def forward(self, x): # x: [N, C, T, V] N批量, C通道, T时间步, V节点数 x self.gcn(x) # 空间建模 x self.tcn(x) # 时间建模 return x class STGCNNav(nn.Module): def __init__(self, num_node20, num_person10, in_channels4): super().__init__() # A 是预定义的邻接矩阵20×20按距离阈值构建 self.A self._build_adjacency(num_node) self.stgcn1 STGCNBlock(in_channels, 64, self.A) self.stgcn2 STGCNBlock(64, 128, self.A) self.stgcn3 STGCNBlock(128, 256, self.A) self.fcn nn.Linear(256, 2) # 输出 [v_linear, v_angular] def _build_adjacency(self, num_node): # 构建全连接邻接矩阵但对角线为0无自环 A torch.ones(num_node, num_node) - torch.eye(num_node) return A def forward(self, x): # x: [B, 4, 10, 20] B批量, 4特征, 10时间步, 20最多行人 x self.stgcn1(x) x self.stgcn2(x) x self.stgcn3(x) # 全局平均池化时间维度 x F.adaptive_avg_pool2d(x, (1, 20)).squeeze(2) # [B, 256, 20] x x.mean(dim2) # [B, 256] 聚合所有行人特征 return self.fcn(x)避坑点self._build_adjacency返回的A必须是torch.Tensor类型不能是numpy.ndarray否则ConvGraph中的torch.mm(A, x)会报错。另外F.adaptive_avg_pool2d(x, (1,20))的(1,20)不可写成(1, -1)——PyTorch 1.13 不支持负数尺寸会触发RuntimeError: invalid argument 2: size should be greater than 0。4. 训练策略与损失设计如何让模型学会“绕开但不逃跑”人群导航最怕两种失败一是过度保守见人就停效率归零二是过度激进强行穿插引发碰撞。这本质是多目标优化问题既要最小化到目标点的距离又要最大化与行人的最小距离还要保持运动平滑。直接加权求和如loss w1*dist_loss w2*social_loss w3*smooth_loss效果差——三个 loss 量纲不同且w1,w2,w3手动调节像玄学。我们采用GradNorm 动态权重调整让模型自己学着平衡。4.1 三重损失函数距离、社交、平滑一个都不能少# loss_functions.py import torch import torch.nn as nn class NavigationLoss(nn.Module): def __init__(self, alpha0.5, beta0.3, gamma0.2): super().__init__() self.mse nn.MSELoss(reductionnone) self.alpha, self.beta, self.gamma alpha, beta, gamma def forward(self, pred_action, target_action, robot_state, track_states): # pred_action: [B, 2] [v_linear, v_angular] # target_action: [B, 2] 标签动作来自专家演示或仿真奖励 # robot_state: [B, 4] [x,y,vx,vy] # track_states: [B, N, 4] 行人状态 [x,y,vx,vy] # 1. 动作回归损失主监督信号 action_loss self.mse(pred_action, target_action).mean(dim1) # [B] # 2. 社交安全损失惩罚预测动作导致的未来碰撞风险 # 用简单运动学模型预测1秒后位置 dt 1.0 pred_x robot_state[:,0] pred_action[:,0] * dt * torch.cos(robot_state[:,3]) pred_y robot_state[:,1] pred_action[:,0] * dt * torch.sin(robot_state[:,3]) # 计算到最近行人的距离 dist_to_ped torch.cdist( torch.stack([pred_x, pred_y], dim1), # [B,2] track_states[:,:,:2] # [B,N,2] ).min(dim1)[0] # [B] social_loss torch.relu(0.8 - dist_to_ped) # 安全距离设为0.8m # 3. 运动平滑损失惩罚角速度突变防止机器人“甩头” smooth_loss torch.abs(pred_action[:,1] - robot_state[:,3]) # 当前角速度 vs 预测角速度 # GradNorm 动态加权核心 total_loss self.alpha * action_loss self.beta * social_loss self.gamma * smooth_loss return total_loss.mean() # GradNorm 实现简化版完整版需维护历史梯度范数 def gradnorm_step(losses, model, optimizer, alpha1.5): # losses: dict {action: tensor, social: tensor, smooth: tensor} optimizer.zero_grad() total_loss sum(losses.values()) total_loss.backward(retain_graphTrue) # 获取各 loss 的梯度范数 grad_norms {} for name, loss in losses.items(): grads torch.autograd.grad(loss, model.parameters(), retain_graphTrue, allow_unusedTrue) grad_norm torch.norm(torch.stack([g.norm() for g in grads if g is not None])) grad_norms[name] grad_norm.item() # 动态调整权重梯度范数大的 loss 权重降低 weights {name: 1.0 / (gn 1e-6) for name, gn in grad_norms.items()} weights {k: v/sum(weights.values()) for k, v in weights.items()} # 归一化 optimizer.zero_grad() weighted_loss sum(weights[name] * loss for name, loss in losses.items()) weighted_loss.backward() optimizer.step() return weights参数说明social_loss中的0.8是安全距离阈值经 200 次真实场景压力测试确定——小于 0.7m 时行人本能后退引发连锁反应大于 0.9m 则导航效率下降 35%smooth_loss中robot_state[:,3]是当前角速度yaw rate不是朝向角否则模型会学习“缓慢转向”而非“平滑转向”导致响应延迟。实测若用朝向角机器人在拐角处平均多耗时 2.3 秒。4.2 数据增强不是加噪声而是加“人群逻辑”常规图像增强旋转、裁剪对热图无效。我们设计三类语义增强行人密度扰动随机屏蔽 30% 的行人热图通道模拟检测漏检但保留距离通道迫使模型不依赖单一信号运动模糊模拟沿速度方向对热图做 3 像素线性模糊cv2.blur模拟高速移动时的传感器拖影社会力注入在热图上叠加高斯核σ2中心位于预测行人未来 0.5 秒位置模拟“预判性避让”# data_augmentation.py def semantic_augment(heatmap: np.ndarray, track_states: list) - np.ndarray: # heatmap: (32,32,3), track_states: [{id:0,x:1.2,y:0.5,vx:0.3,vy:0.1},...] aug_hm heatmap.copy() # 1. 行人密度扰动随机屏蔽通道2行人置信度 if np.random.rand() 0.3: aug_hm[:,:,2] 0 # 2. 运动模糊沿速度方向模糊 if len(track_states) 0: avg_vx np.mean([t[vx] for t in track_states]) avg_vy np.mean([t[vy] for t in track_states]) angle np.arctan2(avg_vy, avg_vx) kernel_size 3 kernel np.zeros((kernel_size, kernel_size)) center kernel_size // 2 # 沿角度方向设置高斯权重 for i in range(kernel_size): for j in range(kernel_size): dx, dy i-center, j-center dist_along dx*np.cos(angle) dy*np.sin(angle) kernel[i,j] np.exp(-0.5*(dist_along/1.0)**2) kernel / kernel.sum() aug_hm[:,:,0] cv2.filter2D(aug_hm[:,:,0], -1, kernel) # 只模糊距离均值通道 # 3. 社会力注入在预测位置加高斯核 for t in track_states: pred_x t[x] t[vx] * 0.5 pred_y t[y] t[vy] * 0.5 # 映射到32x32栅格 gx int((pred_x 5) / 10 * 31) gy int((pred_y 5) / 10 * 31) if 0 gx 32 and 0 gy 32: # 创建高斯核 gauss np.zeros((32,32)) for i in range(32): for j in range(32): dist np.sqrt((i-gy)**2 (j-gx)**2) gauss[i,j] np.exp(-0.5*(dist/2.0)**2) aug_hm[:,:,2] np.clip(aug_hm[:,:,2] gauss * 0.3, 0, 1) # 叠加到行人置信度通道 return aug_hm避坑点cv2.filter2D的-1参数表示输出与输入同类型但若aug_hm[:,:,0]是float32必须确保kernel也是float32否则 OpenCV 内部类型转换会引入 0.02 的系统性偏差导致模型在低速场景下持续右偏。实测未强制kernel kernel.astype(np.float32)时100 次直线行走测试中平均偏航角达 1.8°。5. 部署与避坑Jetson Orin 上的 12 个血泪教训模型在服务器上训得好不等于能在机器人上跑得稳。我在 Jetson Orin32GB RAM, 2048-core GPU上部署时踩过 12 个坑这里只列最关键的 5 个每个都附带现象、原因和解决命令。别跳过——它们会让你少熬 3 个通宵。5.1 现象ROS 节点启动后 GPU 显存占用 98%但nvidia-smi显示无进程原因PyTorch 默认启用 CUDA 图形缓存CUDA GraphOrin 的 16GB GPU 显存被torch.cuda.memory_reserved()预占但未被任何进程使用导致后续节点无法分配显存。解决在__main__.py开头强制禁用图形缓存并手动释放缓存import torch torch.backends.cuda.enable_mem_efficient_sdp(False) # 关闭SDP torch.backends.cuda.enable_flash_sdp(False) # 关闭Flash SDP torch.cuda.empty_cache() # 启动时清空 # 在模型加载后立即执行 model.to(cuda) torch.cuda.synchronize() # 确保加载完成5.2 现象导航时机器人突然原地打转/cmd_vel输出angular.z2.5远超安全限值原因模型输出未做裁剪且 ROS 的twist_mux未配置限幅。当热图中出现异常高亮如反光地板误检为人腿模型输出失控角速度。解决在动作解码层硬限幅并配置 ROS 限幅节点!-- twist_mux_config.yaml -- topics: - name: nav_controller topic: /nav/cmd_vel timeout: 0.5 priority: 10 angular: z: min: -1.2 # rad/s max: 1.2 linear: x: min: 0.0 max: 0.8并在 Python 解码中pred_action torch.clamp(pred_action, mintorch.tensor([-0.8, -1.2]), maxtorch.tensor([0.8, 1.2]))5.3 现象白天导航正常夜间红外模式下频繁误刹原因Realsense D435 的红外图像在低光下噪声激增YOLOv5s 检测框抖动导致热图中行人置信度通道通道2剧烈闪烁GNN 输入不稳定。解决在红外模式下关闭热图的行人置信度通道仅用距离均值方差通道并启用cv2.createBackgroundSubtractorMOG2做运动前景检测if is_infrared_mode: # 用 MOG2 替代 YOLO 检测运动目标 fgmask bg_subtractor.apply(rgb_frame) # rgb_frame 为红外灰度图 contours, _ cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: if cv2.contourArea(cnt) 500: # 过滤小噪声 x,y,w,h cv2.boundingRect(cnt) # 将矩形框映射到32x32热图坐标置信度设为0.7 gx int((x w/2) / 640 * 32) gy int((y h/2) / 480 * 32) if 0gx32 and 0gy32: heatmap[gy,gx,2] 0.7 # 清空原始行人置信度通道 heatmap[:,:,2] 05.4 现象多机器人协同时A 机器人避让 B 机器人B 却直冲 A原因各机器人独立运行模型未共享状态形成“博弈困境”。A 认为 B 是障碍物B 却认为 A 是障碍物双方都选择“绕开对方”结果相向而行。解决引入轻量级协商协议——每台机器人广播自身 ID、位置、预测轨迹3 帧收到后更新本地 GNN 的节点列表。关键代码# 在 ROS 回调中接收其他机器人状态 def other_robot_callback(msg): # msg: RobotStateStamped, 包含 id, pose, twist if msg.id ! self.robot_id: # 忽略自己 # 将其他机器人作为额外节点加入 track_states self.track_states.append({ id: msg.id, x: msg.pose.position.x, y: msg.pose.position.y, vx: msg.twist.linear.x, vy: msg.twist.linear.y }) # 发送自身状态10Hz self.state_pub.publish(self.self_state_msg)注意track_states最大长度设为 2010 个行人 10 个机器人超过则按距离剔除最远的。实测若不限制G本文还有配套的精品资源点击获取
返回列表