주행 위험 감지 컴포넌트를 새로 추가하면서 네 건의 트러블슈팅이 함께 발생함
디테일 패널 값을 바꿨는데 동작이 안 변함
위험 감지를 테스트하려면 차량이 충분히 미끄러져야 해서 미끄러뜨리려고 SplineFollower의 디테일 패널에서 MaxSpeed를 2배로 올렸는데 속도가 안 변함
값이 안 먹는다면 그 값을 누군가 덮어쓰고 있다는 뜻이라고 판단
이전에 구현한 터널/날씨 시스템은 들락거릴 때 부동소수점 오차를 막으려고 베이스라인 캐싱 패턴을 썼다
BeginPlay 시점에 MaxSpeed의 원래 값을 BaselineMaxSpeed에 한 번 복사해두고 이후로는 항상 그 베이스라인 기준으로 계산하는 구조
BeginPlay
→ BaselineMaxSpeed = MaxSpeed (디테일 패널 값을 여기서 복사)
→ 이후 모든 속도 계산은 BaselineMaxSpeed 기준
디테일 패널에서 MaxSpeed를 4000으로 변경
→ 코드는 이미 복사해둔 BaselineMaxSpeed(원래 값)를 사용
→ 변경이 반영되지 않음
결국 또 하나의 변수를 두 시스템이 만지면 충돌한 거였음
그때는 터널과 날씨가 MaxSpeed를 두고 충돌했고 이번엔 베이스라인 캐싱과 디테일 패널 입력이 충돌
그렇다고 베이스라인 캐싱 로직을 수정하면 터널 시스템이 깨질 수도 있으니 그냥 노면 마찰을 낮추는 쪽으로 위험 감지 테스트를 진행하기로 함 (미끄러짐은 속도만으로 생기는 게 아니라 타이어가 노면을 못 붙잡아서도 생기니까)
근데 또 문제 발생
노면을 미끄럽게 할수록 자율주행이 더 안전하게 달림...
자율주행이 눈길인 줄 알고 운행하는데, 실제 도로는 예상보다 더 미끄러운 상황 같은 걸 연출하려고 했는데
(사람으로 치면 빙판인 줄 모르고 평소 속도로 진입하는 경우
인지와 현실의 괴리야말로 자율주행 안전 검증의 핵심 시나리오라고 봤기 때문에)
도로가 피지컬 머티리얼로 구성되어 있으니 PM의 friction 값을 낮춰서 구현하려고 보니 도로의 물리 마찰을 낮춰 더 미끄럽게 만들수록 자율주행이 오히려 더 천천히, 더 안전하게 달림
-> 의도한 위험 상황이 만들어지지 않았음
노면이 미끄러우면 미끄러져야 하는데, 자율주행이 그걸 미리 감지하고 회피해버린 것...
그치만 현실에서는 도로의 마찰을 순간마다 판단할 수 없을 것이고, 그러니 기본적으로 눈길이라는 것만 인지한 상태로 자율주행이 실행될 텐데 지금 시스템은 도로의 마찰을 그때그때 읽어버리니까 현실성이 떨어짐
// 차량에는 날씨 "종류"만 전달
Vehicle->OnWeatherChangedDelegate.Broadcast(CurrentWeather);
// 도로에는 물리 "재질"을 따로 교체
Road->SetRoadPhysicsMaterial(Preset->RoadPhysicsMaterial);
자율주행이 참고할 정보(날씨 종류)와 실제 물리(도로 재질)가 처음부터 분리되어 있었음
문제는 이 분리를 자율주행 컴포넌트가 무너뜨리고 있었다는 점
if (UWeatherSubsystem* WeatherSub = World->GetSubsystem<UWeatherSubsystem>())
{
if (UWeatherPresetDataAsset* Preset = WeatherSub->GetCurrentWeatherPreset())
{
FrictionScale = Preset->LateralFrictionScale; // 여기
}
}
LateralFriction = BaselineLateralFriction * FrictionScale;
자율주행 컴포넌트가 자발적으로 날씨 시스템의 DataAsset을 뒤져서 마찰 배율까지 가져다 쓰고 있었음
그 마찰 배율이 도로 물리와 연동된 값이었기 때문에, 도로를 미끄럽게 만들면 그 값이 자율주행의 안전 속도 계산까지 흘러들어가 "더 미끄러우니 더 천천히"가 되어버린 것
-> 자율주행이 DataAsset을 직접 읽는 부분을 걷어내고 날씨 시스템이 정식으로 전달해주는 날씨 종류만 보고 마찰을 결정하게 함
switch (EffectiveWeather)
{
case EWeather::Rain: LateralFriction = 0.5f; break;
case EWeather::Snow: LateralFriction = 0.25f; break;
default: LateralFriction = 0.8f; break; // Clear
}
이렇게 하면 자율주행이 참고하는 마찰은 날씨 종류 세 가지로만 고정됨
도로의 물리 재질이 아무리 미끄러워도 자율주행은 "지금 눈이니 눈길 기준으로" 정도만 판단함!
실제 노면이 그 가정보다 더 미끄러우면 그 괴리가 슬립이나 횡방향 G 위험으로 드러나게 함


이 변경에서 한 가지 더 신경 쓴 것!
DataAsset은 다른 팀원이 만든 영역이라 자율주행이 그 마찰 필드를 안 읽게 바꾸면 그 팀원의 코드가 깨지지 않을지 따져야 했음
날씨 시스템 코드를 직접 다시 읽어보니 그 시스템은 도로 재질 교체에 RoadPhysicsMaterial만 사용했고 자율주행이 읽던 LateralFrictionScale 필드는 쓰지 않음 -> 자율주행 컴포넌트만 읽던 값이니 안전하게 끊을 수 있다는 판단을 함
또 문제 발견!
값을 키워도 코너에서 계속 경로를 벗어남
맑은 날 자율주행이 코너에서 자꾸 경로를 벗어남
(차가 커브를 그리려던 선을 못 따라가고 바깥으로 밀려나는 증상)
원인을 코너 감속 타이밍으로 봄.
커브를 더 일찍 보고 미리 감속하면 해결될 거라고 판단하고 전방을 얼마나 멀리서부터 보고 감속을 시작하는지 정하는 배율(PreviewScale)을 키움
1.0에서 1.4로 변경 -> 변화가 거의 없음
1.6으로 변경 -> 그래도 거의 그대로
또 캐싱이 값을 선점했나?
if (!bWeatherBaselineCached)
{
BaselineBrakePreviewDist = BrakePreviewDist;
...
bWeatherBaselineCached = true;
}
캐싱은 bWeatherBaselineCached 플래그로 보호되어 단 한 번만 실행되고 있었음
이후 PreviewScale로 값을 아무리 바꿔도 이 블록이 다시 덮어쓰지 않음 -> 캐싱은 정상
그럼 PreviewScale이 원인이 아닌가보군!
그럼 맑은 날만 코너에서 미끄러지는거니까 맑은 날 속도가 너무 빠른가? 싶었음
default: // Clear
WeatherMaxSpeed = 2200.f; // 주석엔 ~80km/h
그래서 맑은 날 최고속도를 2100부터 한 단계씩 내리며 확인했고 동시에 PreviewScale은 원래 값으로 되돌림
해결됨!!! 속도가 너무 빠른 게 원인이었다!
마지막으로 해결한 문제
코너 직전에 급감속하는 것
차량의 속도가 코너 한참 전부터 서서히 줄지 않고 코너 직전까지 속도를 유지하다 급격히 깎는 거동을 보임
(커브 코앞에서야 브레이크를 콱 밟는 초보 운전 패턴)
로그 찍어봤는데 분명 전방을 일정 거리까지 스캔해 곡률을 보고 있었음
뭐가 문제지? 코드 확인
for (float Scan = 0.f; Scan <= PreviewDist; Scan += ScanStep)
{
const float Curv = EstimateCurvature(Scan);
const float DistRatio = Scan / PreviewDist; // 0(코앞)~1(끝)
const float Weight = FMath::Pow(1.f - DistRatio, Falloff);
MaxWeightedCurv = FMath::Max(MaxWeightedCurv, Curv * Weight);
}
의도는 가까운 커브를 강하게, 먼 커브를 약하게 반영해 부드러운 감속 곡선을 만드는 것이었는데 문제는 Falloff 값
이 값이 2.0이면 가중치는 거리 비율의 제곱으로 떨어짐
거리 비율 0.0 (코앞) → 가중치 (1-0.0)^2 = 1.00
거리 비율 0.5 (중간) → 가중치 (1-0.5)^2 = 0.25
거리 비율 0.8 (먼 곳) → 가중치 (1-0.8)^2 = 0.04
먼 커브의 곡률이 0.04배까지 깎임
스캔은 멀리까지 하고 있지만 가중을 거치고 나면 먼 커브는 사실상 0으로 뭉개져 전방에 커브 없음으로 판단되는 것
차량이 커브에 거의 다 와서 거리 비율이 작아져야 비로소 가중치가 커지고 그제서야 감속을 시작하는 코드였음
멀리 본다는 것과 멀리 본 것을 의미 있게 반영한다는 것은 다른 문제였던 것
Falloff를 2.0에서 1.0으로 낮춤 -> 지수가 1이면 가중치가 거리 비율에 선형으로 비례하니까
거리 비율 0.5 → 가중치 (1-0.5)^1 = 0.50 (제곱일 때 0.25, 2배)
거리 비율 0.8 → 가중치 (1-0.8)^1 = 0.20 (제곱일 때 0.04, 5배)
먼 커브를 5배 더 크게 반영하게 되어 차량이 커브를 훨씬 일찍 인지하고 미리 감속을 시작함
(코너 직전 급감속이 점진 감속으로 바뀜)
알게된 것
1. "값이 안 먹는다 = 누가 덮어쓰고 있다"는 진단 회로
- 1번: MaxSpeed가 안 먹음 → 베이스라인 캐싱이 덮어쓰고 있었음
- 3번: PreviewScale을 키워도 안 먹음 → 캐싱 의심부터 함 (이번엔 캐싱이 결백했고, 진짜 원인은 다른 변수였지만, "안 먹는다 → 덮어쓰는 놈을 찾자"는 회로가 작동한 덕분에 빠르게 후보를 소거할 수 있었음)
2. "시스템 간 책임 분리"가 무너지는 두 가지 패턴
- 변수 공유로 인한 충돌 (1번): 터널/날씨 시스템과 디테일 패널이 같은 MaxSpeed를 만짐. 한쪽이 BeginPlay에서 캐싱해버리면 다른 쪽 입력이 사라짐.
- 경계 침범으로 인한 결합 (2번): 자율주행 컴포넌트가 자기 영역 밖인 날씨 DataAsset을 직접 읽어버려서 도로 물리 변경이 자율주행 판단까지 흘러들어감.
'프로젝트 > 자율주행 위험구간 분석 시뮬레이터' 카테고리의 다른 글
| [트러블슈팅] 과속방지턱 감지 - Lateral 필터·검색 거리·시간 압축 (0) | 2026.05.25 |
|---|---|
| 전방 감지 기반 속도 조절 시스템 (과속방지턱과 NPC 차량) (0) | 2026.05.22 |
| 주행 위험 감지 시스템 로직 구성 (0) | 2026.05.19 |
| [트러블슈팅] 터널과 날씨 시스템의 충돌 (0) | 2026.05.19 |
| [트러블슈팅] 날씨 별 속도가 동일, 마찰력 줄어들면 코너에서 추락 (0) | 2026.05.18 |
