로봇 시뮬레이션의 시간 간격을 줄이면 위치 계산은 왜 달라질까
같은 로봇을 같은 가속도로 똑같이 1초 동안 움직였는데, 계산된 위치가 0.5 m이기도 하고 0.75 m이기도 하다. 로봇 시뮬레이션의 시간 간격을 줄이면 위치 계산은 왜 달라질까. 원인은 모사한 시간이 아니라, 연속해서 변하는 속도를 몇 번에 나눠 어떤 값으로 대표했느냐에 있다.
여기서는 초기 위치 0 m, 초기 속도 0 m/s인 물체가 2 m/s²의 일정한 가속도를 받는 1차원 예제만 다룬다. 사칙연산과 속도·가속도의 단위만 알면 계산을 따라갈 수 있다. 2026년 9월 20일 KST에 Python 3.12.3으로 산술을 검산했지만, 물리 엔진이나 실물 로봇을 실행한 실험은 아니다.
시간 간격은 움직임을 다시 계산하는 간격이다
현실의 시간은 끊기지 않지만 컴퓨터는 유한한 횟수로 상태를 갱신한다. 시간 간격(time step, Δt)은 한 번의 갱신이 대표하는 시간이다. Δt=0.5 s로 1초를 계산하면 두 번, Δt=0.25 s이면 네 번 갱신한다. 전체 모사 시간은 어느 쪽도 1초로 같다.
시간에 따른 변화율을 이렇게 유한한 간격으로 나눠 상태값을 근사하는 계산을 수치 적분(numerical integration)이라고 한다. 이 글에서 사용할 규칙은 전진 오일러법(forward Euler 또는 explicit Euler)이다. 현재 위치를 x, 현재 속도를 v, 가속도를 a라고 하면 한 단계는 다음 순서로 진행한다.
x_next = x + v × Δt
v_next = v + a × Δt
첫 줄은 현재 속도 v가 이번 구간 내내 유지됐다고 보고 위치를 계산한다. 그다음에야 가속도로 늘어난 속도를 다음 단계에 반영한다. Drake 공식 문서가 정의한 명시적 1차 적분 x(t+h)=x(t)+h f(t,x(t))을 위치와 속도 상태에 적용한 형태다.
단위부터 확인하면 두 식을 잘못 섞는 일을 줄일 수 있다. vΔt의 단위는 (m/s)×s=m이므로 위치에 더하고, aΔt는 (m/s²)×s=m/s이므로 속도에 더한다. ROS REP-103도 ROS 내부 길이와 시간의 기본 단위로 meter와 second를 정하지만, 이 단위 규약이 계산의 정확도를 보장하는 것은 아니다.
0.5초와 0.25초 계산에서 빠지는 거리가 다르다
먼저 연속시간의 기준값을 구해 두자. 일정한 가속도에서는 1초 뒤 속도와 위치가 다음과 같다.
v(1) = v₀ + aT = 0 + 2×1 = 2 m/s
x(1) = x₀ + v₀T + ½aT² = 0 + 0 + ½×2×1² = 1 m
전진 오일러법으로 같은 조건을 계산하면 단계별 값은 이렇게 달라진다. 표의 위치와 속도는 각 단계의 갱신이 모두 끝난 값이다.
Δt |
단계 끝 시간(s) | 위치 x(m) |
속도 v(m/s) |
|---|---|---|---|
| 0.5 | 0.5 | 0.000 | 1.000 |
| 0.5 | 1.0 | 0.500 | 2.000 |
| 0.25 | 0.25 | 0.000 | 0.500 |
| 0.25 | 0.50 | 0.125 | 1.000 |
| 0.25 | 0.75 | 0.375 | 1.500 |
| 0.25 | 1.00 | 0.750 | 2.000 |
Δt=0.5 s에서는 첫 구간의 대표 속도가 0 m/s, 두 번째 구간은 1 m/s다. 그래서 이동거리는 0×0.5 + 1×0.5 = 0.5 m가 된다. Δt=0.25 s에서는 0, 0.5, 1.0, 1.5 m/s를 차례로 사용해 0.75 m를 얻는다.
둘 다 최종 속도는 정확히 2 m/s지만 위치는 기준값 1 m보다 작다. 일정한 가속도를 속도에 더한 총량 aT는 두 계산에서 같지만, 위치 계산에는 각 구간 안에서 계속 증가하는 속도가 아니라 구간 시작 속도 하나만 들어갔기 때문이다. 상태 일부가 정확하다고 전체 상태까지 정확한 것은 아니다.

속도-시간 그래프에서 이동거리는 선 아래의 넓이다. 실제 값은 밑변 1초, 높이 2 m/s인 삼각형의 넓이 1 m다. 전진 오일러는 왼쪽 끝 높이로 만든 직사각형들을 더한다. 직사각형 폭을 절반으로 줄이면 증가하는 직선 아래에서 빠뜨리는 작은 삼각형들의 합도 줄어든다. 이 그림은 일정하게 증가하는 속도를 설명하는 도구이며, 접촉과 마찰이 있는 로봇 운동 전체를 나타내지는 않는다.
오차가 절반이 되는 범위를 식으로 확인한다
이 예제에서 Δt를 절반으로 줄일 때 위치 오차도 절반이 되는 까닭은 합으로 확인할 수 있다. 전체 시간 T를 정확히 나누는 단계 수를 N=T/Δt라고 두자. n번째 구간 시작 속도는 v_n=anΔt다. 전진 오일러가 계산한 최종 위치는 다음과 같다.
x_E(T) = Σ(v_nΔt)
= aΔt²Σn
= aΔt²N(N-1)/2
= (a/2)(T²-TΔt)
연속시간 기준값 x(T)=aT²/2에서 이를 빼면 절대 오차는 aTΔt/2다. a=2 m/s², T=1 s를 넣으면 오차가 곧 Δt와 같은 수치가 된다. 따라서 0.5초 간격에서는 0.5 m, 0.25초 간격에서는 0.25 m가 부족하다.
이 비례 관계는 1차원 일정 가속도, T/Δt가 정수이고 전진 오일러법을 쓰는 현재 예제의 결론이다. 전진 오일러가 일반적으로 1차 방법이라는 사실과, 모든 로봇 시뮬레이션 결과가 시간 간격에 정확히 비례해 좋아진다는 주장은 같지 않다. 불연속적인 접촉, 강성(stiffness)이 큰 계, 제약 해법, 부동소수점 오차가 끼면 결과의 변화 양상이 달라질 수 있다.
직접 검산하면 갱신 순서까지 확인할 수 있다
이 글의 출발점이 된 사칙연산 검산은 계산 예제와 실행·검산 기록에 공개했다. 아래 반복문과 구별되는 산술 예제이며, 시간 간격별 위치와 오차의 계산 근거를 확인할 수 있다.
아래 코드는 외부 패키지를 쓰지 않는다. 검증 환경은 Ubuntu 계열 실행 환경과 Python 3.12.3이었으며, 위치를 먼저 갱신하고 속도를 나중에 갱신하는 순서가 수식과 같다.
Python 3.12 이상에서 아래 코드 전체를 euler_check.py로 저장한 뒤, 같은 폴더의 터미널에서 다음 명령으로 실행한다. 첫 명령으로 버전을 확인하고 두 번째 명령으로 검산한다.
python3 --version
python3 euler_check.py
def forward_euler(dt: float) -> tuple[float, float]:
x = 0.0 # m
v = 0.0 # m/s
a = 2.0 # m/s²
steps = int(1.0 / dt)
for _ in range(steps):
x = x + v * dt
v = v + a * dt
return x, v
for dt in (0.5, 0.25, 0.125):
x, v = forward_euler(dt)
exact_x = 1.0
closed_form = 1.0 - dt
error = abs(exact_x - x)
assert abs(x - closed_form) <= 1e-12
assert abs(v - 2.0) <= 1e-12
print(
f"dt={dt:.3f} x={x:.3f} v={v:.3f} "
f"error={error:.3f}"
)
기대 결과이자 2026년 9월 20일 실제 검산 결과는 다음과 같았고, 프로세스는 종료 상태 0으로 끝났다.
dt=0.500 x=0.500 v=2.000 error=0.500
dt=0.250 x=0.750 v=2.000 error=0.250
dt=0.125 x=0.875 v=2.000 error=0.125
assertion이 실패하거나 최종 속도가 2.0 m/s가 아니면 이 예제의 검산에 실패한 것이다. 1.0/dt가 정수가 아닌 값을 넣으면 마지막 시간을 정확히 맞추지 못할 수 있으므로 이 코드는 일반 목적 시뮬레이터가 아니다. 비교할 때는 전체 시간, 초기값, 가속도와 갱신식을 모두 고정해야 한다.
갱신 순서를 뒤집는 것도 흔한 실수다. 먼저 새 속도를 구한 뒤 그 속도로 위치를 계산하는 준암시적 오일러법(semi-implicit Euler)을 쓰면 같은 예제의 위치는 Δt=0.5 s에서 1.5 m, 0.25 s에서 1.25 m다. 이번에는 각 구간의 오른쪽 끝 속도를 써서 넓이를 크게 잡는다. 작은 시간 간격에서 기준값 쪽으로 다가가는 방향은 반대지만, “새 속도를 쓰면 바로 정답”은 아니다.
일정 가속도라는 가정을 구간 안에서도 반영한 x_next=x+vΔt+½aΔt²는 두 간격에서 모두 1 m를 준다. 그러나 실제 시뮬레이션에서는 가속도가 상태와 입력, 접촉에 따라 변할 수 있다. 한 예제에서 정확한 운동학 식을 모든 동역학 문제의 해법으로 바꿔 읽어서는 안 된다.
확인 문제: 0.125초 간격에서는 어디까지 갈까
같은 조건에서 Δt=0.125 s로 전진 오일러 계산을 하면 1초 뒤 위치와 기준값 대비 절대 오차는 얼마일까. 코드를 실행하기 전에 위의 오차 식으로 먼저 예상해 보자.
x_E=(a/2)(T²-TΔt)에 a=2, T=1, Δt=0.125를 넣으면 위치는 0.875 m다. 기준값 1 m와의 차이는 0.125 m다. 8단계를 돌리지 않았거나 새 속도로 위치를 갱신했다면 같은 비교가 아니다. 1.125 m나 1.25 m처럼 더 큰 값이 나오면 단계 수와 두 갱신문의 순서를 먼저 살펴야 한다.
이 문제에서는 숫자 하나보다 판정 방법을 배워야 한다. 시간 간격을 바꾼 비교에서는 나머지 조건을 고정하고, 연속시간 기준값이나 더 촘촘한 계산과의 차이가 줄어드는지 확인해야 한다.
작은 timestep은 수치 오차를 줄여도 모델을 고치지는 않는다
MuJoCo 2.3.3 공식 문서는 timestep 감소가 일반적으로 적분기의 정확도와 안정성을 개선할 수 있지만 계산을 느리게 하며, 너무 작은 값은 의미 있는 정확도 개선 없이 CPU 시간을 소모할 수 있다고 설명한다. 적절한 범위는 모델마다 다르다. 따라서 가능한 최소값을 고르는 대신 다음 순서로 수렴성, 즉 간격을 더 줄였을 때 관심 결과의 변화가 충분히 작아지는지를 확인하는 편이 낫다.
- 1초 뒤 위치, 에너지, 접촉 침투량처럼 비교할 값을 정한다.
- 다른 조건을 고정하고 timestep만 절반으로 줄인다.
- 결과 차이가 허용 범위 아래인지, 늘어난 계산 시간은 감당할 수 있는지 본다.
- 적분기나 solver 설정도 바꿨다면 timestep의 효과와 섞지 않고 따로 비교한다.
SciPy solve_ivp 같은 적응형 적분기는 허용 오차에 맞춰 내부 step을 선택할 수 있다. 이때 결과를 받고 싶은 시각인 t_eval과 solver가 실제 계산에 사용한 내부 step은 같지 않을 수 있다. 화면의 프레임 간격, 제어 주기, 센서 주기와 물리 적분 timestep도 서로 다른 설정일 수 있으므로 “프레임률을 높였다”는 말만으로 적분 오차가 줄었다고 판단할 수 없다.
이번 검산은 수식과 Python 산술까지다. 실제 하드웨어에는 센서 잡음과 표본주기, 통신 지연, 액추에이터 포화, 마찰, 백래시, 배터리 상태, 제어주기 지터가 더해진다. 시뮬레이션 timestep을 줄였다는 사실은 실제 위치 정확도나 안전 성능의 증거가 아니다.
실물로 후속 검증할 때는 저속·저가속도에서 시작하고, 빈 작업공간과 물리적 비상정지(E-stop)에 즉시 접근할 수 있는 조건을 마련해야 한다. 소프트웨어의 속도·토크 제한을 설정하고 관찰자를 배치한다. 예상 궤적 이탈, 통신 상실이나 제한 초과가 생기면 명령을 중단하고 E-stop으로 구동 전원을 안전 상태로 전환한다. 구체적인 정지 순서는 해당 제조사의 검증된 절차가 우선한다.
시간 간격을 줄여 위치가 달라졌다면 먼저 버그라고 단정할 일이 아니다. 어떤 적분기가 어느 시점의 속도로 위치를 갱신하는지 확인하고, 같은 조건에서 timestep을 단계적으로 줄여 관심 값이 수렴하는지 봐야 한다. 다음 단계로 Δt=0.125 s의 결과를 종이에 먼저 적은 뒤 위 코드를 실행해 예상과 실제 검산값을 맞춰보면 된다.
참고 링크
- Modern Robotics — 8.5 Forward Dynamics of Open Chains — 전진동역학의 timestep별 상태 갱신과 적분 drift 설명, 2026-09-20 확인.
- Drake — ImplicitEulerIntegrator Class Reference — 명시적·암시적 Euler 식과 오차 추정 설명, 2026-09-20 확인.
- Drake — SemiExplicitEulerIntegrator Class Reference — 준암시적 Euler의 범위, 2026-09-20 확인.
- MuJoCo 2.3.3 — Computation — timestep의 정확도·안정성·계산 비용 관계, 2026-09-20 확인.
- SciPy 1.18.0 —
solve_ivp— 초기값 문제와 적응형 오차 제어, 2026-09-20 확인. - ROS REP-103 — Standard Units of Measure and Coordinate Conventions — ROS의 SI 단위 규약, 2026-09-20 확인.
이 글에는 제휴·협찬이 없다.