1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
| import torch import torch.nn as nn import numpy as np from dataclasses import dataclass
@dataclass class Occupant3D: """乘员3D状态""" occupant_id: int seat_position: str category: str height_est: float weight_est: float joints_3d: np.ndarray is_oop: bool oop_type: str seat_recline: float headrest_present: bool seat_position: float
class Unified3DPerception(nn.Module): """ 统一3D座舱感知模型 架构: 1. 多摄像头特征提取 2. 跨摄像头融合 3. 3D重建 4. 多任务输出 """ def __init__(self, n_cameras: int = 3, n_joints: int = 17): super().__init__() self.n_cameras = n_cameras self.n_joints = n_joints self.backbone = nn.Sequential( nn.Conv2d(3, 16, 3, stride=2, padding=1), nn.BatchNorm2d(16), nn.ReLU6(), nn.Conv2d(16, 32, 3, stride=2, padding=1), nn.BatchNorm2d(32), nn.ReLU6(), nn.Conv2d(32, 64, 3, stride=2, padding=1), nn.BatchNorm2d(64), nn.ReLU6(), nn.Conv2d(64, 128, 3, stride=2, padding=1), nn.BatchNorm2d(128), nn.ReLU6(), nn.Conv2d(128, 256, 3, stride=2, padding=1), nn.BatchNorm2d(256), nn.ReLU6(), nn.AdaptiveAvgPool2d((4, 4)), ) self.fusion = nn.Sequential( nn.Linear(256 * 4 * 4 * n_cameras, 1024), nn.ReLU(), nn.Linear(1024, 512), ) self.det_head = nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 7 * 5) ) self.pose_head = nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 7 * n_joints * 3) ) self.oop_head = nn.Sequential( nn.Linear(512, 128), nn.ReLU(), nn.Linear(128, 7 * 5) ) self.seat_head = nn.Sequential( nn.Linear(512, 128), nn.ReLU(), nn.Linear(128, 7 * 3) ) self.obj_head = nn.Sequential( nn.Linear(512, 128), nn.ReLU(), nn.Linear(128, 7 * 4) ) def forward(self, camera_inputs): """ Args: camera_inputs: list of (B, 3, H, W) tensors, one per camera Returns: detections, poses, oop, seats, objects """ cam_feats = [] for cam in camera_inputs: feat = self.backbone(cam) cam_feats.append(feat.flatten(1)) fused = torch.cat(cam_feats, dim=1) fused = self.fusion(fused) det = self.det_head(fused).view(-1, 7, 5) pose = self.pose_head(fused).view(-1, 7, self.n_joints, 3) oop = self.oop_head(fused).view(-1, 7, 5) seat = self.seat_head(fused).view(-1, 7, 3) obj = self.obj_head(fused).view(-1, 7, 4) return { 'detection': det, 'pose_3d': pose, 'oop': oop, 'seat_config': seat, 'objects': obj, }
class OOPClassifier: """OOP异常姿态分类器""" def __init__(self): self.thresholds = { 'reclining': 30.0, 'feet_dash': 0.3, 'near_airbag': 30.0, 'slouching': 15.0, 'leaning': 15.0, } def classify(self, occupant: Occupant3D) -> dict: """判定OOP类型""" oop_results = {} if occupant.seat_recline > self.thresholds['reclining']: oop_results['reclining'] = True if len(occupant.joints_3d) >= 17: left_ankle = occupant.joints_3d[15] if len(occupant.joints_3d) > 15 else None right_ankle = occupant.joints_3d[16] if len(occupant.joints_3d) > 16 else None if left_ankle is not None and left_ankle[2] > self.thresholds['feet_dash']: oop_results['feet_dash'] = True chest = occupant.joints_3d[8] if chest[1] < self.thresholds['near_airbag']: oop_results['near_airbag'] = True shoulder_mid = occupant.joints_3d[1] hip_mid = occupant.joints_3d[9] lean_angle = np.arctan2( shoulder_mid[0] - hip_mid[0], shoulder_mid[2] - hip_mid[2] ) * 180 / np.pi if abs(lean_angle) > self.thresholds['slouching']: oop_results['slouching'] = True return oop_results
if __name__ == "__main__": model = Unified3DPerception(n_cameras=3, n_joints=17) cameras = [torch.randn(1, 3, 480, 640) for _ in range(3)] with torch.no_grad(): outputs = model(cameras) print("=== 统一3D座舱感知 ===") for key, val in outputs.items(): print(f" {key}: {val.shape}") params = sum(p.numel() for p in model.parameters()) print(f"\n总参数: {params:,}") print(f"模型大小: {params * 4 / 1024 / 1024:.1f} MB (FP32)") print(f"量化后: {params * 2 / 1024 / 1024:.1f} MB (FP16)") oop_cls = OOPClassifier() test_occupant = Occupant3D( occupant_id=1, seat_position='front_pass', category='adult', height_est=175, weight_est=70, joints_3d=np.random.randn(17, 3) * 100, is_oop=False, oop_type='', seat_recline=35.0, headrest_present=True, seat_position=0.6, ) oop = oop_cls.classify(test_occupant) print(f"\nOOP检测结果: {oop}")
|