로봇의 발은 바닥을 어떻게 느끼는가: G1의 접촉·마찰·single-support 판정

G1 v92 정책은 slow command 평가에서 15초 동안 넘어지지 않고 +4.748m를 이동했습니다. 이 숫자만 보면 보행이 시작된 것처럼 보입니다. 그러나 air time은 0초, step frequency는 0Hz, single-support는 0이었습니다. 15초 trace의 모든 snapshot에서 8개 환경 모두 양발이 바닥에 닿아 있었습니다.

로봇은 걸은 것이 아니라 양발을 붙인 채 미끄러졌습니다.

이 결과를 이해하려면 ‘발이 닿았다’는 말을 세 층으로 나눠야 합니다. collider와 물성은 실제 지지력과 마찰을 만듭니다. sensor 또는 proxy는 그 접촉을 숫자로 관측합니다. 마지막으로 시간에 따른 좌우 접촉 배열을 flight, single-support, double-support와 step event로 해석합니다. 한 층이 정상처럼 보여도 다음 층의 보행 성공이 보장되지는 않습니다.

G1 발바닥 collider와 마찰에서 sole 높이 접촉 proxy, single-support와 double-support 분류, 보행 평가까지 이어지는 접촉 처리 과정과 실제 실패 사례
현재 G1은 발바닥 아래 다섯 점의 최저 높이로 접촉을 추정합니다. collider가 존재하는 것, 접촉 신호가 참인 것, single-support가 많이 발생한 것은 각각 서로 다른 증거입니다.

collider·contact signal·support phase는 같은 것이 아닙니다

Collider는 PhysX가 충돌을 계산하는 형상입니다. 발바닥 collider가 ground collider와 만나야 법선 방향 지지력과 접선 방향 마찰이 생깁니다. 화면에 보이는 발 mesh만 있고 collision geometry가 없으면 로봇은 시각적으로 바닥 위에 있어도 물리적으로는 통과할 수 있습니다.

Contact signal은 그 물리 상태를 controller나 평가 코드가 읽을 수 있게 만든 관측입니다. 접촉 센서는 힘을 줄 수 있고, 간단한 환경은 발 위치가 특정 높이 아래인지로 접촉을 추정할 수도 있습니다. 두 방식은 의미가 다릅니다. 높이 proxy는 발이 바닥 근처라는 사실만 말하며, 실제로 체중을 지지하는 법선력이 있는지까지 증명하지 않습니다.

Support phase는 좌우 두 접촉 신호를 시간축에서 해석한 상태입니다. 양발 중 몇 개가 닿았는지 세어 flight, single-support, double-support로 나누고, 공중에 있던 발이 다시 닿는 순간을 step event로 셉니다. 정책이 걷는지 판단하려면 이 시간 구조와 몸체 이동·생존을 함께 봐야 합니다.

초기 G1 자산에는 발바닥을 지지할 collision geometry가 부족했습니다

초기 자체 변환 G1 자산을 점검했을 때 source URDF에는 각 발에 반지름 0.005m인 작은 sole contact sphere 네 개만 있었습니다. runtime USD에는 *_ankle_*_link/collisions Xform이 있었지만 실제 collision-like prim은 0개로 검사됐습니다. zero-policy가 nominal pose를 유지하도록 두어도 평균 약 0.50초 만에 무너졌기 때문에 reward보다 먼저 자산과 접촉을 의심했습니다.

양쪽 ankle_roll_link 아래에 sole box collider를 추가하고 spawned stage에서 CollisionAPI가 붙은 prim이 보이는 것까지 확인했습니다. 그런데 5초 zero-policy trace의 평균 첫 reset은 0.4975초였습니다. 기존 0.495초와 사실상 같았습니다. collider prim이 존재한다는 정적 검사는 통과했지만, articulation의 실제 접촉 집합에서 usable support가 생겼다는 증거는 아니었습니다.

같은 sole box를 유지한 채 1×1 generated terrain 대신 infinite plane으로 바꾸자 평균 첫 reset은 0.4975초에서 1.9000초로 늘었습니다. 여전히 5초/0-reset gate에는 실패했지만, ground 표현도 실제 지지에 관여하는 blocker였다는 점은 확인했습니다. 이 과정은 URDF와 USD가 같은 G1을 다르게 움직이게 하는 이유에서 다룬 asset contract의 발바닥 사례입니다.

현재 G1은 접촉 힘 대신 발바닥 최저 높이를 사용합니다

Isaac Lab 2.1.1 Contact Sensor는 지정한 rigid body에 작용하는 net contact force를 제공합니다. data API에는 force history뿐 아니라 current·last air time과 contact time도 있습니다. 하지만 당시 G1 articulation data 경로에서는 발 contact force tensor를 사용할 수 없었습니다. 학습과 평가가 서로 다른 임시 판정을 쓰지 않도록 공통 helper _get_feet_contact()를 만들었습니다.

현재 helper는 각 발바닥 box 아래의 네 모서리와 중앙, 모두 다섯 점을 사용합니다. 점은 link local frame에서 정의되고 발 quaternion으로 world frame에 회전된 뒤 link world position에 더해집니다. 그중 가장 낮은 z가 threshold 아래면 접촉으로 봅니다.

sample_pos_w = rotate(foot_quat_w, sole_samples_b) + foot_pos_w
sole_z = min(sample_pos_w[..., 2])
feet_in_contact = sole_z < sole_contact_height

origin/main@ee4d96e의 현재 설정은 sole box center (0.04, 0.0, 0.025), half extents (0.11, 0.045, 0.02), contact threshold 0.11m입니다. 이 값은 공식 G1 stack의 자연 standing foot z가 약 0.083m인 조건에서 서 있는 발을 접촉으로 등록하기 위한 구현값입니다. 실제 force threshold를 측정해 얻은 물성값은 아닙니다.

접촉 threshold 하나가 gait metric 전체를 바꿀 수 있었습니다

old ankle-link z proxy는 pitch-over 중에도 접촉을 과하게 유지해 평균 contact fraction 0.985를 기록했습니다. 같은 물리 trajectory에서 sole-bottom threshold를 0.03m로 두면 평균 0.056으로 너무 엄격했고, 0.09m와 0.10m는 1.000으로 너무 관대했습니다. 당시 자체 변환 asset에서는 0.08m가 평균 0.173으로 가장 덜 왜곡된 후보라 판단됐습니다.

이 scan은 접촉의 정답을 알아낸 실험이 아닙니다. 어느 threshold가 기존 실패 trace를 덜 잘못 분류하는지 비교한 것입니다. 이후 official asset과 spawn pose로 전환하면서 현재 threshold는 0.11m가 됐습니다. 자산·초기 자세·발바닥 box가 바뀌면 threshold 의미도 다시 검증해야 합니다.

이 구분을 놓치면 reward와 evaluation이 같은 오류를 공유할 수 있습니다. 둘 다 동일한 proxy를 쓰면 숫자는 일관돼 보이지만, 실제 접촉 힘과의 일치는 별도로 검증되지 않습니다. 학습과 평가의 판정을 맞춘 것은 구현 진전이고, contact sensing이 물리적으로 validated됐다는 뜻은 아닙니다.

0·1·2개의 접촉으로 flight·single·double-support를 구분합니다

현재 코드는 좌우 발 접촉 boolean을 더해 contact_count를 만듭니다. 0이면 두 발이 모두 공중인 flight, 1이면 한 발로 지지하는 single-support, 2이면 양발 지지인 double-support입니다.

contact_count = sum(feet_in_contact)
flight_phase  = contact_count == 0
single_support = contact_count == 1
double_support = contact_count >= 2

일반적인 보행에서는 single-support와 double-support가 교대로 나타납니다. 그러나 single-support가 무조건 좋고 double-support가 나쁘다는 뜻은 아닙니다. 정지 명령에서는 양발 지지가 자연스럽고, 보행 중에도 발을 바꾸는 순간 짧은 double-support가 생길 수 있습니다. 반대로 double-support가 오래 지속되면서 몸만 움직이면 미끄러짐일 가능성이 큽니다.

현재 reward는 보행 명령에서 single-support를 보상하고, double-support가 0.16초 grace를 넘으면 시간 패널티를 누적합니다. 0.45초 뒤에도 움직이는 double-support가 계속되면 별도의 lock penalty를 적용합니다. flight에는 더 강한 penalty가 들어갑니다. 반면 zero command에서는 double-support stance를 보상합니다. 같은 접촉 상태도 명령과 시간 조건에 따라 의미가 달라집니다.

air time은 발이 떨어진 순간이 아니라 다시 닿는 순간에 확정됩니다

각 발의 air-time counter는 policy step마다 증가하다가 접촉하면 0으로 reset됩니다. 공중 시간이 0보다 큰 발에서 현재 또는 직전 접촉이 참이 되는 순간을 first_contact로 잡습니다. 현재 접촉과 직전 접촉을 OR하는 filter는 한 step의 접촉 흔들림으로 touchdown event를 놓치는 일을 줄입니다.

contact_filt = feet_in_contact | last_contacts
first_contact = (feet_air_time > 0) & contact_filt
feet_air_time += policy_dt
feet_air_time *= ~contact_filt

평가 코드도 contact history에서 공중 구간이 시작됐다가 다시 접촉으로 바뀐 길이를 air time으로 계산하고, 검출된 touchdown 횟수를 전체 시간으로 나눠 step frequency를 만듭니다. 따라서 trace가 공중 상태로 끝난 경우 마지막 미완료 swing은 step으로 확정되지 않습니다. 단순히 한 frame에서 발이 높다고 해서 한 걸음을 셌다고 보면 안 됩니다.

마찰은 접촉을 만드는 값이 아니라 접촉 중 미끄러짐을 제한합니다

발과 바닥이 맞닿으면 normal force가 체중을 지지하고, 마찰이 접선 방향 미끄러짐을 제한합니다. 개념적으로 정지 마찰은 필요한 접선력이 한계 안에 있을 때 발을 붙잡고, 한계를 넘어서 미끄러지면 동마찰 영역으로 들어갑니다. 실제 PhysX는 contact patch와 solver 설정을 포함하므로 단일 식만으로 결과를 예측할 수는 없습니다.

현재 flat G1 환경은 ground material의 static friction과 dynamic friction을 모두 1.0, restitution을 0.0, friction combine mode를 average로 설정합니다. 그러나 실제 접촉의 유효 계수는 ground 하나가 아니라 발 material과의 조합으로 정해집니다. Omni Physics 문서에서 average는 두 material 값의 평균이며, 서로 다른 combine mode가 만나면 우선순위가 높은 mode가 사용된다고 설명합니다.

항목 현재 flat 환경 직접 결정하는 것 결정하지 못하는 것
static friction ground 1.0 미끄러지기 전 접선 저항의 material 입력 실제 발-ground 유효값 전체
dynamic friction ground 1.0 미끄러지는 접촉의 material 입력 정책이 발을 드는지 여부
restitution ground 0.0 충돌 후 튀어 오르는 성향 보행 timing
combine mode average 두 material 값을 결합하는 규칙 접촉 sensor의 threshold

마찰을 높이면 보행이 자동으로 좋아진다고 말할 수도 없습니다. 너무 낮으면 stance foot가 밀리고, 지나치게 높은 접선 구속은 잘못된 발 자세에서 몸체 회전이나 충격을 키울 수 있습니다. 현재 실험 기록에는 동일 checkpoint를 여러 friction 조건에서 비교한 robustness sweep이 없습니다. 그래서 ground coefficient 1.0을 실물 바닥과 일치한다고 주장하지 않습니다. 이 차이는 G1 sim-to-real 검증에서 따로 닫아야 합니다.

contact가 참인 발의 수평 속도로 slide를 찾습니다

현재 rew_foot_slide는 접촉으로 판정된 발의 world-frame 수평 속도 크기를 더합니다. 발이 바닥에 닿아 있는데 x·y로 빠르게 움직일수록 음의 보상을 받습니다.

foot_horiz_speed = norm(foot_lin_vel_w[..., :2])
foot_slide = sum(foot_horiz_speed * feet_in_contact)
reward_slide = foot_slide * rew_foot_slide

이 항은 v92 같은 double-support slide를 직접 겨냥합니다. v92 slow displacement +4.748m에서 v94가 slide penalty를 포함하자 slow displacement는 +1.692m로 줄었고 nominal은 +2.311m가 됐습니다. 그러나 air time 0, step frequency 0, double-support 1.0은 그대로였습니다. 미끄러지는 해의 이익을 낮췄지만 발을 드는 해를 만들어 주지는 못했습니다.

또한 현재 접촉이 force가 아니라 높이 proxy이므로 한계가 있습니다. 발바닥 점이 threshold 아래에 있지만 실제 하중을 거의 받지 않는 발의 수평 이동도 slide로 셀 수 있습니다. 반대로 순간적인 접촉 높이 오차가 있으면 실제 stance foot의 slip을 놓칠 수 있습니다. force-based contact와 접선 속도를 함께 비교해야 이 항의 의미를 검증할 수 있습니다.

v92는 이동 거리가 보행을 증명하지 못한다는 사례였습니다

v91은 nominal에서 15초를 생존했지만 -1.409m 뒤로 이동했습니다. v92에서 heading alignment 계수를 0.6에서 2.0으로 높이자 slow command의 fall rate는 0%, displacement는 +4.748m, velocity error는 0.190이 됐습니다. 방향과 생존은 크게 좋아졌습니다.

그런데 v92부터 v94까지 세 번 연속으로 air time 0, step frequency 0, single-support 0, double-support 1.0이었습니다. reward scalar를 조금씩 바꾸는 자동 루프는 이 지점에서 중단했습니다. 다음 시도는 phase-conditioned swing·unload 같은 구조적 gait signal로 바뀌었습니다. reward가 높아도 걷지 않는 이유의 대표 사례지만, 이번 글에서 중요한 점은 실패를 드러낸 것이 contact history였다는 사실입니다.

single-support가 많아도 안정적인 보행이라고 단정할 수 없습니다

반대 방향의 오판도 가능합니다. v161 nominal 15초 trace에서는 6,000행 가운데 4,351행이 single-support였고, 평가의 nominal single-support fraction은 0.7038이었습니다. fall rate도 0%였습니다. 그러나 몸이 명령 방향 반대로 돌아간 yaw-flipped 해였고 world displacement는 -2.834m였습니다. 접촉 교대는 생겼지만 task direction을 만족하지 못했습니다.

v168 nominal은 single-support fraction 0.707, gait score 0.883을 기록했지만 fall rate 100%, 평균 episode 2.069초였습니다. 15초 trace의 첫 termination은 1.32초에 lateral failure로 발생했습니다. 이번에는 single-support가 '걷지 않음'을 해결한 것이 아니라 불안정한 stepping pressure와 함께 나타났습니다.

그래서 contact metric은 필요조건을 보여 주는 증거로 사용합니다. double-support 1.0이면 정상 보행이 아니라는 판단은 강해지지만, single-support 0.7만으로 좋은 보행이라고 승인하지는 않습니다. 방향, 속도, 좌우 대칭, 몸체 tilt, 발 교차, fall과 episode duration이 같이 통과해야 합니다. 보행 평가 gate 전체는 보행 정책이 살아남아도 좋은 보행은 아닌 이유에 정리돼 있습니다.

접촉 문제를 재현할 때 같은 시간축에 남겨야 할 신호

contact boolean 하나만 저장하면 collider 문제, sensing 문제, gait 문제를 구분할 수 없습니다. 현재 trace에서 다음 신호를 같은 policy step에 묶어야 합니다.

  • 물리·기하: left/right sole sample z, foot pose, ground·foot material과 combine mode
  • 접촉 관측: proxy contact, 가능하면 per-foot normal contact force와 contact point
  • 미끄러짐: 접촉 중 foot x·y velocity, root velocity, commanded direction
  • 시간 구조: first contact, air time, single/double/flight fraction, left/right step count
  • 안정성: root height, projected gravity, tilt, termination cause와 first-fall time

Isaac Sim 화면도 이 로그와 같은 timestamp로 남겨야 합니다. foot collider와 sole sample point를 표시하고 contact state 색을 바꾸면, 숫자가 말하는 touchdown이 실제 발바닥 위치와 일치하는지 확인할 수 있습니다. 화면만 보거나 표만 보는 것보다 둘을 같은 frame에서 대조하는 편이 빠릅니다.

현재 구현된 것과 아직 검증되지 않은 것을 구분합니다

상태 현재 근거 아직 말할 수 없는 것
Designed collider·contact proxy·support phase·reward/eval을 분리한 접촉 처리 계약 이 설계가 실물 발 센서 계약과 일치한다는 뜻은 아님
Implemented 다섯 sole sample의 최저 z 판정, flight/single/double 분류, air time·slide·support reward와 공통 eval helper explicit per-foot force sensor가 현재 G1 reward의 정본이라는 뜻은 아님
Executed collision·terrain·threshold 진단과 v92–v94, v161, v168 PPO 평가·trace 다양한 friction material에서 robustness 평가를 실행했다는 뜻은 아님
Observed collider 추가만으로 stand gate 불변, plane에서 생존 증가, double-support slide와 unstable single-support 모두 관찰 모든 실패의 원인이 마찰 또는 contact proxy 하나라고 확정할 수 없음
Accepted 학습·평가가 동일 helper를 쓰고 contact metric을 단독 승인 기준으로 사용하지 않는 진단 규칙 현재 승인된 G1 보행 checkpoint는 없음
Deployed 실물 G1 접촉 센서·발바닥 물성 배포 기록 없음 실물 바닥에서 동일 support phase가 재현된다고 주장할 수 없음

다음 접촉 실험은 reward 계수를 먼저 바꾸는 일이 아닙니다. official G1 asset에 좌우 발별 ContactSensorCfg를 명시하고, 같은 rollout에서 sole-height proxy와 normal force 기반 contact를 나란히 기록해야 합니다. 두 판정이 갈리는 frame을 Isaac Sim 화면과 함께 모은 뒤 threshold 또는 hysteresis를 정합니다. 그 다음에야 기준보다 낮고 높은 friction 조건을 평가용으로 바꿔 stance slip과 생존 민감도를 비교할 수 있습니다.

발이 바닥에 닿았다는 한 비트는 보행 성공의 결론이 아닙니다. collider가 지지를 만들었는지, sensor가 그 지지를 제대로 읽었는지, 접촉이 좌우 교대로 이어졌는지, 그동안 로봇이 명령 방향으로 살아서 이동했는지를 차례로 확인해야 합니다. v92는 두 발로 4.748m를 미끄러졌고, v161은 single-support를 만들고도 잘못된 방향으로 걸었습니다. 이 두 실패 사이에 접촉 신호의 정확한 역할이 있습니다.

함께 읽기: G1의 관측·행동·보상 · URDF와 USD의 G1 asset 계약 · PPO action과 implicit PD · world·body·joint 좌표계 · G1 sim-to-real 검증

공식 문서: Isaac Lab 2.1.1 Contact Sensor · ContactSensorData · RigidBodyMaterialCfg · Omni Physics rigid-body material

댓글 달기

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

위로 스크롤