자율주행 시뮬레이터에서 본 차량이 전방의 도로 시설이나 다른 차량을 감지하고, 거리에 따라 알아서 속도를 줄였다가 통과 후 복원하는 컴포넌트를 구현
대상은 둘
- 과속방지턱 (정적): 발견하면 고정 속도(800cm/s)로 줄임
- NPC 차량 (동적): 거리와 NPC 속도를 보고 따라가거나 정지
어떻게 식별할 건지 고민하다가 태그를 박고 태그로 찾기로 함
식별용 태그 이름도 EditAnywhere로 노출
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Autopilot|SpeedBump")
FName SpeedBumpTag = TEXT("SpeedBump");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Autopilot|Vehicle")
FName NPCVehicleTag = TEXT("NPCVehicle");
1차 방어는 본 차량에 태그 안 박기, 2차 방어로 검사 함수 안에 자기 제외 가드 한 줄
if (Candidate == Owner) continue;
어디서 처리할 것인가...
위험 감지 컴포넌트(HazardDetector)에 두거나, 자율주행 컴포넌트(SplineFollower)에 두거나
위험 감지는 의도치 않은 사고를 잡는 시스템.
과속방지턱은 정상 도로 시설이고 의도치 않은 사고가 아님 → 위험 감지 안에 두는 건 의미가 안 맞음
자율주행이 결국 속도를 어떻게 정할 것인가의 결정이므로 SplineFollower에 두기로 함
전방 검색 거리를 고정 값으로 두지 않고 속도에 비례하게 작성
const float ScanDist = ScanBase + VehicleSpeed * ScanSpeedFactor;
이유는 같은 거리라도 진입 속도가 높으면 줄일 시간이 부족하기 때문
시속 30km로 5m 앞에서 발견 → 줄일 시간 있음
시속 70km로 같은 거리 → 0.3초만에 도달, 거의 급정거
거리는 같아도 시간이 다름!
과속방지턱은 발견 즉시 반환
float USplineFollowerComponent::CheckSpeedBumpAhead(float VehicleSpeed) const
{
AActor* Owner = GetOwner();
if (!Owner) return -1.f;
const float ScanDist = BumpScanBase + VehicleSpeed * BumpScanSpeedFactor;
const FVector OwnerLoc = Owner->GetActorLocation();
const FVector ForwardDir = Owner->GetActorForwardVector();
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
AActor* Candidate = *It;
if (!Candidate || !Candidate->Tags.Contains(SpeedBumpTag)) continue;
const FVector ToBump = Candidate->GetActorLocation() - OwnerLoc;
const float ForwardDot = FVector::DotProduct(ToBump, ForwardDir);
if (ForwardDot <= 0.f || ForwardDot > ScanDist) continue;
const FVector LateralComp = ToBump - ForwardDir * ForwardDot;
if (LateralComp.Size() > 800.f) continue;
return BumpSlowSpeed;
}
return -1.f;
}
ForwardDot: 차 정면 방향과 액터까지의 방향의 내적. 양수면 차 앞, 음수면 차 뒤. 같은 도구를 위험 감지 시스템에서 미끄러짐 측정에도 썼음.
LateralComp: 옆 차선 액터 무시용 가로 방향 필터. 우리 차선이 아닌 곳의 시설에 반응하면 안 됨
const 함수: 의도된 약속. 이 함수는 검사만 하고 멤버 상태를 바꾸지 않음. 함수 시그니처로 그 사실을 드러냄
NPC는 거리에 따라 속도를 보간
NPC는 움직임. NPC가 멈춰 있으면 본 차량도 멈춰야 하고, NPC가 30km/h로 가면 본 차량도 비슷한 속도로 따라가야 자연스러움
거리에 따라 세 구간으로 나눔
ClosestDist <= StopGap (4m) → 0 정지에 가깝게
ClosestDist >= MinFollowGap (8m) → NPCSpeed NPC 속도에 맞춤
그 사이 → 보간 선형 보간
NPC가 정지해 있으면 NPCSpeed가 0이라 어느 구간이든 결국 0 → 본 차량도 자연스럽게 정지
NPC가 30km/h로 가면 본 차량도 거리에 따라 0~30km/h 사이로 따라감 → 차간거리가 자연스럽게 유지됨
const float NPCSpeed = ClosestNPC->GetVelocity().Size2D();
if (ClosestDist <= StopGap) return 0.f;
if (ClosestDist >= MinFollowGap) return NPCSpeed;
const float Ratio = (ClosestDist - StopGap) / (MinFollowGap - StopGap);
return NPCSpeed * Ratio;
NPC는 여러 대 있을 수 있어서 루프를 끝까지 돌면서 가장 가까운 한 대만 추적.
멀리 있는 NPC 보고 천천히 가다가 가까운 NPC에 부딪치면 안 되기 때문
SplineFollower의 UpdateTargetSpeed는 곡률 안전속도, 도로 자체 제한속도, 도로 끝 미리 감속 등 여러 제한을 비교하면서 가장 작은 값을 채택하는 구조
SpeedLimit = FMath::Min(SpeedLimit, RoadSpeedLimit);
SpeedLimit = FMath::Min(SpeedLimit, EndSpeedLimit);
같은 패턴 안에 한 줄로 끼워 넣으면 됨
const float VehicleSpeedNow = OwnerPawn.IsValid()
? OwnerPawn->GetVelocity().Size2D() : 0.f;
const float BumpSpeed = CheckSpeedBumpAhead(VehicleSpeedNow);
if (BumpSpeed > 0.f)
{
SpeedLimit = FMath::Min(SpeedLimit, BumpSpeed);
}
const float VehicleAheadSpeed = CheckVehicleAhead(VehicleSpeedNow);
if (VehicleAheadSpeed >= 0.f)
{
SpeedLimit = FMath::Min(SpeedLimit, VehicleAheadSpeed);
}
위 코드에서 과속방지턱은 > 0.f, NPC는 >= 0.f임
과속방지턱의 BumpSlowSpeed는 항상 800 이상이라 0이 나올 일이 없어 >0으로 충분
그러나 NPC가 정지해 있으면 CheckVehicleAhead가 정확히 0을 반환함.
그 0이 정지하라는 의미 있는 신호
0으로 하면 이 정지 신호가 무시돼 본 차량이 NPC에 박힘

복원 로직을 따로 짜지 않았음
매 프레임 검사 함수가 전방에 있나를 묻고, 통과하고 나면 같은 자리에 더 이상 없으므로 자연히 -1 반환
그러면 SpeedLimit이 다른 제한들로 다시 결정되고, 자율주행은 원래 목표 속도로 가속
상태를 따로 기억하지 않고 매 프레임 현재 상황만 판단하면 되는 구조
→ 진입 감지와 통과 감지가 별개 로직이면 두 판정이 어긋나는 순간 차가 영영 감속 상태에 머물거나 영영 원래 속도로 못 돌아오는 사고가 날 수 있음
매 프레임 같은 질문을 던지는 방식은 그런 어긋남이 구조적으로 발생할 수 없음
위험 감지 시스템에서 진입과 해제 두 사건을 따로 방송한 것과는 의도적으로 다른 선택
- 위험 감지: "사건"이 분석 단위 → 시작과 끝을 각각 기록할 필요 있음
- 전방 감지: "현재 상황"이 결정 단위 → 상태를 기억할 필요 없음
'프로젝트 > 자율주행 위험구간 분석 시뮬레이터' 카테고리의 다른 글
| 언리얼 엔진으로 만든 자율주행 위험구간 분석 시뮬레이터 (0) | 2026.05.26 |
|---|---|
| [트러블슈팅] 과속방지턱 감지 - Lateral 필터·검색 거리·시간 압축 (0) | 2026.05.25 |
| [트러블슈팅] 캐싱·결합·잘못 짚은 변수·가중 지수 (0) | 2026.05.19 |
| 주행 위험 감지 시스템 로직 구성 (0) | 2026.05.19 |
| [트러블슈팅] 터널과 날씨 시스템의 충돌 (0) | 2026.05.19 |
