3주 동안 학습했는데도 걷지 못한 이유 — 보행 문제가 아니라 학습 문제를 잘못 정의했다

강화학습을 3주 넘게 돌렸는데도 휴머노이드가 제대로 걷지 못했다면, 단순히 학습 시간이 부족했던 걸까요? 저도 처음엔 더 오래 학습시키거나 보상 계수를 조금씩 고치면 어느 순간 걸음이 나올 거라고 생각했습니다. 그런데 실험 기록을 처음부터 다시 대조해보니, 학습기가 게을렀던 게 아니었습니다. 제가 원한 ‘보행’과 학습기가 실제로 풀도록 만든 문제가 서로 달랐던 겁니다.

이 글은 실패한 모델의 변명이 아니라, 같은 실수를 반복하지 않기 위한 원인 분석입니다. 3주 동안 쌓인 실행 횟수를 성과처럼 세는 대신, 관측·액추에이터·보상·위상·행동 권한·평가가 어떤 순서로 잘못 연결됐는지 하나씩 짚어보겠습니다. 결론부터 말하면 계산 자원이 부족해서 실패한 게 아니었습니다. 잘못 정의된 문제를 정교하게 최적화하는 데 시간을 썼던 겁니다.

이 시기 즈음의 체크포인트가 Isaac Sim에서 균형을 잃고 무너지는 6프레임 캡처
글에서 설명하는 착각들이 실제로 로봇 화면에서는 이렇게 보였습니다 — 몸이 뒤틀리고 팔이 마구 휘저어지는 모습입니다.
잘못된 접촉 판정
발이 바닥에 닿았는지를 높이로 추정해 실제 교대 지지를 보지 못했습니다.
어긋난 보상과 위상
시간표와 실제 발 접촉이 달라도 점수를 받을 수 있었습니다.
잘못된 안정 상태
두 발을 오래 붙이고 버티거나 한쪽 동작만 반복하는 해법이 강화됐습니다.
학습이 멈춘 게 아니라, 측정과 보상이 허용한 더 쉬운 해법으로 수렴한 것이었습니다.

첫 번째 착각 — 발 접촉을 제대로 측정하고 있다고 생각했습니다

보행을 평가하려면 어느 발이 바닥을 지지하고 어느 발이 공중으로 이동하는지 알아야 합니다. 초기 환경에서는 발바닥 높이가 일정 값보다 낮으면 접촉으로 간주했습니다. 구현하기 쉽고 별도 센서가 필요 없다는 이유로 이 방법을 썼는데, 발이 바닥 가까이에 있기만 해도 접촉으로 판정되는 문제가 있었습니다. 그 결과 실제로는 좌우 발을 번갈아 떼는 정책도 평가기 안에서는 두 발을 계속 붙인 동작처럼 보일 수 있었습니다.

이 문제는 외부에 공개된 G1 보행 정책을 같은 환경에서 실행해보고서야 드러났습니다. 힘 센서를 쓴 원래 환경에서는 좌우 단독 지지가 뚜렷했는데, 제가 쓰던 높이 판정은 같은 동작을 거의 전부 양발 지지로 기록하고 있었습니다. 발에 작용하는 힘을 직접 읽는 접촉 센서로 바꾸자 그제서야 외부 정책의 걸음이 정상적으로 측정됐습니다. 그 전까지 접촉 보상과 보행 합격 기준은 같은 잘못된 신호를 공유하고 있었던 셈입니다.

여기서 얻은 교훈은, 지표가 일관된다고 해서 물리적으로 옳은 건 아니라는 점이었습니다. 보상과 평가가 같은 대리 지표를 쓰면 둘이 서로 동의하기 때문에 오류를 발견하기가 오히려 더 어려워집니다. 앞으로는 새 보상을 설계하기 전에, 알려진 정책이나 수동 동작으로 센서가 실제 현상을 제대로 구별하는지부터 확인하려 합니다. 학습 전에 측정기를 검증하는 단계가 통째로 빠져 있었습니다.

두 번째 착각 — 같은 G1이면 같은 제어 조건일 거라고 생각했습니다

PD 제어기는 목표 관절 각도와 실제 각도의 차이를 스프링처럼 당기고, 관절 속도를 댐퍼처럼 줄이는 가장 기본적인 관절 제어 방식입니다. 외부 G1 정책을 처음 실행했을 때 몇 초 안에 넘어져서, 정책 자체가 지금 환경과 맞지 않는 건가 의심했습니다. 그런데 공개 배포 설정에 포함된 관절별 강성과 감쇠를 그대로 적용하자, 같은 정책이 넘어지지 않고 걸었습니다. 모델보다 액추에이터 계약이 먼저 틀려 있었던 겁니다.

로봇 이름과 관절 수가 같아도 제어 주기, 관절 순서, 목표값 스케일, 강성, 감쇠와 토크 제한이 다르면 정책이 경험하는 ‘몸’은 완전히 달라집니다. 저는 한동안 이 차이를 보상 함수로 보정하려 했습니다. 하지만 물리적으로 약한 팔과 허리, 다른 응답 속도를 가진 다리를 보상 계수만으로 원래 정책의 몸처럼 만들 수는 없더군요. 앞으로 외부 정책이나 모션을 가져올 때는, 파일을 실행하기 전에 관측·행동·액추에이터 계약을 표로 맞춰보는 걸 우선하려 합니다.

세 번째 착각 — 앞으로 가면 걷고 있는 거라고 보상했습니다

초기 보상은 넘어지지 않고 목표 속도에 가까워지는 정책을 찾도록 짜여 있었습니다. 문제는, 로봇 몸을 기준으로 한 전진 속도와 세계 좌표에서 실제 목표 방향으로 이동하는 게 항상 같지 않다는 데 있었습니다. 일부 정책은 몸의 방향이 틀어지거나 뒤집힌 뒤에도 자기 몸 기준의 전진 점수를 계속 받았습니다. 학습 보상은 높았지만, 긴 평가로 보면 방향이 무너지고 실제 이동은 오히려 나빠지고 있었습니다.

이 현상을 확인한 뒤 전진·방향·생존 보상을 여러 방식으로 다시 조합해봤습니다. 그런데 한 항을 추가하면 다른 쉬운 우회로가 생겼고, 초반엔 점수가 높다가 중·후반에 방향과 속도가 함께 붕괴하는 패턴이 반복됐습니다. 이건 계수가 조금 부족한 문제가 아니라, 보상이 원하는 행동을 충분히 특정하지 못한 문제였습니다. “점수가 올랐는가”보다 “이 점수를 보행이 아닌 방식으로도 얻을 수 있는가”를 먼저 물었어야 했습니다.

네 번째 착각 — 시간에 맞춘 발 동작이 실제 걸음과 맞을 거라 생각했습니다

좌우 발을 번갈아 움직이게 하려고, 에피소드 시간을 기준으로 왼발과 오른발의 스윙 구간을 정했습니다. 위상은 반복 운동의 현재 위치를 나타내는 시계라고 생각하면 됩니다. 이 방식은 단순한데, 발이 실제로 땅을 디딘 순간과 상관없이 시계가 계속 흘러간다는 게 문제였습니다. 한 번 타이밍이 어긋나면 “왼발을 들 시간”이라는 목표와 실제로 체중을 지지하는 발이 정반대가 되는 일이 생겼습니다.

잔차 동작의 크기를 키워보면 위상 문제인지 단순한 동작 부족인지 구분할 수 있을 거라 판단했습니다. 그런데 동작을 강하게 만들어도 없던 쪽의 단독 지지는 나타나지 않았고, 이동 거리와 안전성만 나빠졌습니다. 추적 결과를 보니 시간 기반 목표가 기존 정책의 접촉 모드와 아예 반대로 정렬돼 있었습니다. 발을 떼는 순서는 시계가 아니라, 실제 발 접촉 사건에 맞춰 갱신했어야 했습니다.

다섯 번째 착각 — 앞뒤 다리 동작만 만들면 체중 이동은 따라올 거라 생각했습니다

발을 앞으로 내미는 동작은 눈에 잘 보여서 무릎과 엉덩이의 앞뒤 움직임을 먼저 가르치려 했습니다. 대신 기존 정책의 안정성을 지키려고 좌우 방향의 엉덩이와 발목 제어는 고정해뒀습니다. 안전한 선택처럼 보였는데, 한 발을 들려면 먼저 몸의 무게를 반대편 발 위로 옮겨야 한다는 걸 놓치고 있었습니다. 좌우 체중 이동을 담당하는 관절의 권한을 고정한 채로 스윙 동작만 추가한 셈이었습니다.

실험해보니 다리 동작의 진폭을 키워도 같은 쪽 접촉만 조금 늘었을 뿐, 반대쪽 단독 지지는 끝내 생기지 않았습니다. 더 강한 동작은 보행을 열어주지 못하고 오히려 기존 이동 능력과 안전성만 깎아냈습니다. 이 결과로 보행은 관절 파형의 모양보다 지지 전환의 순서가 먼저라는 게 분명해졌습니다. 다음 구조에서는 접촉 사건에 맞춘 위상과, 제한된 좌우 체중 이동 권한을 함께 검증해야 합니다.

기존 접근
시간 위상 결정 → 스윙 관절 목표 → 보상 합산 → PPO 업데이트
필요한 접근
접촉 사건 확인 → 지지발 위 체중 이동 → 반대발 스윙 → 착지 검증 → 다음 위상
발을 흔드는 파형이 아니라, 실제 접촉으로 닫히는 상태 전이가 보행 루프의 기준이 되어야 했습니다.

여섯 번째 착각 — 선생 동작을 보상으로 보여주면 정책이 따라올 거라 생각했습니다

보상만으로 교대 보행이 나오지 않자, 좌우 발 동작의 기준 파형을 만들고 정책이 그 행동에 가까워지도록 점수를 줬습니다. 그런데 초기 정책과 목표 행동의 차이가 너무 커서 오차 점수가 바닥에서 포화돼 버렸습니다. 포화된 보상은 아주 나쁜 행동과 조금 덜 나쁜 행동을 구분하지 못합니다. 정책 입장에서는 목표 방향으로 한 걸음 가까워져도 보상이 거의 달라지지 않았던 거죠.

오차 범위를 넓히고 좌우 목표를 분리해봐도, 정책은 한쪽 동작이나 양발 지지라는 기존의 안정 상태에서 빠져나오지 못했습니다. 이 과정에서 “선생이 있다”와 “배울 수 있는 학습 신호가 있다”는 서로 다른 조건이라는 걸 알게 됐습니다. 현재 상태에서 목표까지 이어지는 경사가 없으면, PPO는 기준 동작을 발견하기 어렵습니다. 모방학습이나 모션 추적 정책으로 기본 운동 능력을 먼저 만들고, 강화학습은 과제 적응을 맡는 구조가 더 타당해 보입니다.

일곱 번째 착각 — 더 많은 자동 실험이 더 많은 학습이라고 생각했습니다

Codex 에이전트는 제안, 검토, 실행, 평가와 기록을 빠르게 반복해줬습니다. 덕분에 잘못된 체크포인트를 배포하거나 실패를 성공으로 기록하는 위험은 크게 줄었습니다. 반면 버전 번호와 작업량이 빠르게 늘면서, 실제 PPO 업데이트가 일어난 실험과 평가 도구를 고친 작업, 실행 수명주기를 진단한 작업이 한 흐름 안에서 비슷하게 보이기 시작했습니다. 많은 사이클이 돌아갔지만, 전부가 보행 능력을 학습한 사이클은 아니었던 겁니다.

실제로 최근엔 300회의 실제 업데이트를 수행해서 ‘학습 부족’ 가설을 직접 확인해봤습니다. 안정적으로 서고 이동하던 기준 정책은 100회 업데이트 시점부터 낙상과 동작 품질이 크게 나빠졌고, 200회와 300회에서는 더 악화됐습니다. 목표 궤적과의 오차도 학습이 진행될수록 커졌습니다. 더 오래 돌린다고 해결되는 문제가 아니라, 새 보상이 이미 확보한 안정 상태를 파괴하는 방향으로 정책을 밀고 있었던 겁니다.

그래서 지금까지 얻은 건 무엇일까요

아직 제가 학습한 정책을 보행 성공이라고 부를 수는 없습니다. 다만 외부의 검증된 정책을 정확한 접촉 센서와 액추에이터 조건으로 실행했을 때, G1이 같은 시뮬레이터에서 안정적으로 걸을 수 있다는 사실은 확인했습니다. 그러니 로봇 모델이나 시뮬레이터가 원천적으로 불가능한 건 아니었던 거죠. 실패 범위는 결국 로컬 학습 문제를 어떻게 정의하고 전달했는지로 좁혀졌습니다.

또 하나 확인한 건, 안정적인 기준 정책에 새 목표를 붙이는 것만으로 원하는 전신 동작이 저절로 생기지는 않는다는 점입니다. 목표가 이미 만족된 관절 행동을 다시 모방하게 하거나, 실제 접촉과 어긋난 기준을 추적하게 하면 학습은 기준 정책을 보존하지 못합니다. 다음 가설은 오차가 실제로 남아 있고 정책 행동으로 줄일 수 있는, 딱 하나의 기하학적 문제만 골라야 한다는 겁니다. 시작부터 보행 전체를 한 번에 해결하려던 설계를 버려야 했습니다.

같은 실수를 막기 위해 학습 순서를 바꿉니다

다음 학습에서는 먼저 힘 기반 접촉과 액추에이터 계약을 고정하고, 알려진 정책으로 측정기가 올바른지부터 확인하려 합니다. 그다음 접촉 사건으로 좌우 위상을 갱신하고, 기존 정책이 실패하는 딱 한 가지 동작만 선택할 겁니다. 이 동작은 학습 전에 기준 상태에서 오차가 포화되지 않는지, 정책의 행동으로 실제 줄일 수 있는지를 먼저 확인해야 합니다. 이 두 조건을 만족하지 못하는 목표에는 GPU 시간을 쓰지 않으려 합니다.

학습은 100회 단위의 짧은 안전 평가로 중간 상태를 확인하고, 낙상·동작 크기·목표 오차가 동시에 나빠지면 즉시 중단할 계획입니다. 기준 정책보다 나빠진 결과를 다음 학습의 출발점으로 삼지 않을 겁니다. 걷는 모양을 직접 만들어야 한다면 보상 항을 계속 쌓는 대신, 모션 기반 기준 정책이나 추적기를 먼저 구성하려 합니다. 강화학습은 이미 존재하는 운동 능력을 과제와 환경에 맞게 조정하는 역할로 한정하려 합니다.

이번 실패에서 배운 한 줄은 이겁니다. “학습기가 답을 못 찾은 게 아니라, 제가 보행이 아닌 문제에 답을 요구했다.” 다음 질문은 더 좋은 보상 계수가 무엇이냐가 아닙니다. 실제 발 접촉으로 닫히는 지지 전환을, 어떤 기준 동작과 행동 권한으로 학습시킬 것인지부터 다시 정의해야 합니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤