시뮬레이션 속도 제어와 타임스텝
Gazebo 시뮬레이션에서의 시간 개념
Gazebo에서 시뮬레이션을 구동할 때 고려해야 할 시간은 크게 실제 시간(real time)과 시뮬레이션 시간(simulation time) 두 가지가 있다. 실제 시간은 우리가 일상에서 경험하는 시계의 시간이고, 시뮬레이션 시간은 가상 세계에서 경과하는 시간을 의미한다. 이 두 시간축이 일치하면, 곧 1초의 실제 시간 경과에 1초의 시뮬레이션 시간이 흐른다는 뜻이며, 이를 통해 로봇 시뮬레이션 실험을 마치 실제 물리 환경에서 진행하듯이 관찰할 수 있다.
하지만 연산 부하가 큰 센서 시뮬레이션이나 물리 엔진이 복잡하게 동작하는 상황에서는, 1초의 실제 시간을 지났더라도 내부적으로는 1초 이상의 시뮬레이션 시간이 흐르지 못하는 경우가 생길 수 있다. 또는 반대로 간단한 시뮬레이션이라면 실제 시간보다 빠르게 시뮬레이션 시간을 진행시킬 수도 있다. 이러한 차이를 제어하고 모니터링할 때 매우 중요한 지표가 Real Time Factor(RTF)이다.
Real Time Factor(RTF)
RTF는 시뮬레이션 속도가 실제 시간에 비해 어느 정도 비율로 동작하고 있는지를 나타내는 지표다. 예를 들어 RTF가 1.0이면 시뮬레이션 시간이 실제 시간과 정확히 동일하게 흐름을 의미한다. 만약 RTF가 0.5라면 시뮬레이션 1초가 실제 시간 2초 동안에 흘러간다는 뜻이고, RTF가 2.0이라면 시뮬레이션이 실제 시간의 2배 속도로 진행됨을 의미한다.
Gazebo에서 RTF를 확인하는 방법은 크게 두 가지다. 첫 번째는 시뮬레이터의 GUI(그래픽 인터페이스) 하단에 표시되는 RTF를 참조하는 것이고, 두 번째는 특정 토픽이나 ROS2 명령어로 시뮬레이션 상태를 확인하는 방식이다. RTF를 제어하기 위해서는 물리 엔진 설정, 최대 스텝 크기(max step size), 업데이트 주기, 플러그인 설정, 그리고 CPU의 성능 등이 종합적으로 관여한다.
시뮬레이션 스텝(Simulation Step)
물리 엔진에서 시뮬레이션을 진행할 때는 일정한 시간 간격 $\Delta t$을 기준으로 물리량을 업데이트한다. 예를 들어 시뮬레이션 시간 $t$에서의 물체 상태를 $\mathbf{x}(t)$라 하고, 물리 엔진이 풀고자 하는 미분방정식을 $\mathbf{f}(\mathbf{x}, t)$라 할 때, 가장 단순한 오일러(Euler) 적분 방식으로는 다음과 같이 상태가 업데이트된다.
물론 실제 물리 엔진에서는 이보다 더 정교한 적분 방식을 사용하여 안정성과 정확도를 높인다. Gazebo에서는 이 $\Delta t$을 ‘타임스텝(time step)’으로 정의하며, 이를 ‘max step size’로 설정하여 물리 계산에 사용한다.
타임스텝 설정과 정확도
타임스텝이 너무 크면 시뮬레이션이 빠르게 진행될 수 있지만 물리 엔진의 불안정성이나 부정확한 결과가 발생할 가능성이 높아진다. 예를 들어, 물체 충돌 시 단위 시간당 계산해야 할 물리량이 많아지는데, $\Delta t$이 크면 이 과정을 충분히 세밀하게 계산하지 못해 ‘물체가 서로 관통’해 버리거나 ‘튀어오르는(bouncing)’과 같은 비물리적 현상이 발생하기 쉽다.
반면 타임스텝을 너무 작게 설정하면 더 정확해질 수 있지만, 연산 부하가 커져 시뮬레이션 전체가 매우 느려질 수 있다. 따라서 원하는 정확도와 연산 자원 사이에서 균형점을 찾는 것이 중요하다. 실험을 통해 적절한 타임스텝을 찾고, 또 시뮬레이션을 반복적으로 관찰하여 물리적 거동이 타당한지 확인하는 과정이 필요하다.
타임스텝의 수학적 표현
물리 엔진에서 다루는 대부분의 방정식은 미분방정식 형태이며, 이를 시뮬레이션 시간 단위로 이산화(discretize)하여 적분한다. 일반화하면 다음과 같은 형태의 동적 시스템을 생각할 수 있다.
이때 시뮬레이션에서 사용하는 $\Delta t$ 간격에 따라 여러 수치적분 방법(Explicit Euler, RK4 등)이 적용된다. 예컨대, 4차 룬게-쿠타(Runge-Kutta) 방식일 경우, 내부적으로는 $\Delta t$를 4단계로 쪼개서 중간 기울기를 여러 번 평가한 뒤 최종 상태를 계산한다. 이러한 수치적 기법을 통해 실제 환경을 근사하는 것이 Gazebo 물리 엔진의 핵심 메커니즘이다.
max_step_size
Gazebo 시뮬레이션에서 ‘타임스텝(time step)’은 앞서 언급한 $\Delta t$이며, Gazebo의 세계(world) 설정 파일에서 ‘max_step_size’라는 이름으로 제어한다. 예시로 world 설정 파일(.sdf 등) 내부를 살펴보면 다음과 같은 형태를 볼 수 있다.
여기서 값이 작을수록 더 자주(고빈도) 물리 계산을 수행하게 되고, 그만큼 시뮬레이션이 더 많은 연산을 요구한다. 반대로 크게 설정하면 계산 횟수가 줄어들어 시뮬레이션 속도는 빨라질 수 있지만, 물리적 정확도가 떨어지거나 안정성이 저하될 수 있다. 프로젝트 요구사항(센서 정밀도, 로봇 이동속도 등)에 따라 적절한 max_step_size를 설정해야 한다.
real_time_update_rate
Gazebo 물리 설정에서 ‘real_time_update_rate’ 파라미터는 ‘1초의 실제 시간 동안 몇 번의 시뮬레이션 스텝을 돌릴 것인가’를 결정하는 파라미터다. 다음과 같은 형태로 지정한다.
$\texttt{real_time_update_rate} \times \texttt{max_step_size} \approx 1$ 인 상황이라면 시뮬레이션이 실제 시간과 동일한 속도로 진행된다.
예를 들어 max_step_size가 0.001(1ms)이고 real_time_update_rate를 1000으로 설정한다면,
실제 1초 동안 1000번의 물리 스텝을 수행한다는 의미이다.
따라서 시뮬레이션에서 1초간 진행할 계산 횟수(스텝 수)는 실제 1초와 일치하고, 이론적으로 RTF는 1.0에 가까워진다.
하지만 실제로는 CPU 부하나 플러그인 동작, 그래픽 처리 등에 의해 RTF가 설정값과 다를 수 있다. 따라서 시뮬레이션이 의도대로 진행되는지 수시로 모니터링하는 것이 좋다.
RTF 계산식과 파라미터 연관성
RTF(Real Time Factor)는 일반적으로 다음과 같이 계산된다.
여기서
$\Delta t_\text{simulation}$은 시뮬레이션 시간의 경과량,
$\Delta t_\text{real}$은 실제 시간의 경과량이다.
Gazebo에서
$\Delta t_\text{simulation}$은 ‘max_step_size’의 누적,
$\Delta t_\text{real}$은 실제 CPU 시계가 측정한 시간,
‘real_time_update_rate’는 1초 동안 몇 번의 타임스텝을 목표로 하는지를 결정한다.
다음과 같은 관계를 직관적으로 생각해볼 수 있다:
하지만 실제 RTF는 시스템 성능, 플러그인 로직, 시뮬레이션에서 사용되는 모델 복잡도 등에 의해 달라진다.
시뮬레이션 속도 가속 또는 감속하기
실시간보다 느리게 진행(실험 관찰 용도)
복잡한 센서(예: 3D LiDAR, 카메라 등)나 대규모 로봇 군집을 시뮬레이션할 때, 실제 시간과 동일 속도로 계산하기가 어려울 수 있다.
real_time_update_rate를 줄이거나 max_step_size를 늘리면 시뮬레이션 속도를 실시간보다 느리게(또는 비슷하게) 맞출 수 있다.
실시간보다 빠르게 진행(알고리즘 검증 속도 단축 용도)
알고리즘 검증을 위해 긴 시간의 주행 테스트가 필요한 경우, 물리 계산이나 그래픽 비용이 크지 않다면 시뮬레이션 속도를 높여 빠르게 실험 결과를 얻을 수 있다.
real_time_update_rate를 높이거나 max_step_size를 줄이는 방식으로 시뮬레이션 시간이 실제보다 빨리 흐르도록 설정할 수 있다.
다만, 지나치게 시뮬레이션을 가속하면 오히려 CPU나 GPU 자원이 급격히 소모되어 결과적으로 느려질 수 있으므로 주의해야 한다.
Gazebo 물리 엔진별 특징과 타임스텝
Gazebo는 내부 물리 엔진으로 ODE(Open Dynamics Engine), Bullet, DART, Simbody 등을 지원한다. 기본적으로는 ODE가 많이 사용되지만, 다른 엔진을 사용하면 특정 상황에서 시뮬레이션 정확도나 연산 효율이 달라질 수 있다. 물리 엔진마다 내부적으로 다음과 같은 차이점이 있으므로, 타임스텝 설정 및 시뮬레이션 속도 제어 시 고려해야 한다.
ODE (Open Dynamics Engine)
기본 물리 엔진으로, 충돌 감지와 조인트 해석에 특화된 구조를 갖춤.
하위 버전의 Gazebo에서 기본 선택이며, 비교적 문서와 사례가 풍부.
max_step_size와 real_time_update_rate를 통해 충돌 반복 계산 횟수와 물리 적분 횟수를 제어한다.
Bullet
게임 엔진 등에 널리 쓰이는 Bullet 라이브러리를 기반으로 하며, 강체(rigid body) 물리 시뮬레이션에 적합.
조인트(joint) 표현과 소프트 바디(soft body) 해석이 가능해, 특정 로봇 시뮬레이션에서 사용되는 경우도 많음.
타임스텝에 대해 반응이 민감할 수 있으므로, 충돌 관련 매개변수와 함께 세심한 조정이 필요하다.
DART (Dynamic Animation and Robotics Toolkit)
역학적 시뮬레이션에 뛰어난 정밀도를 제공하고, 조인트 로봇 시뮬레이션에서 높은 정확도를 기대할 수 있음.
타임스텝을 촘촘하게 설정할수록 해석 정확도가 높아지지만, 연산 시간이 급증할 수 있다.
복잡한 멀티 조인트(인간형 로봇 등)의 역학 시뮬레이션을 상세하게 다룰 때 유리하다.
Simbody
다물체 동역학(multi-body dynamics)에 강점이 있는 물리 엔진.
수치적분 알고리즘의 정밀도가 높은 편이라 큰 타임스텝을 줘도 비교적 안정적인 결과를 얻을 수 있으나, 세부 튜닝이 필요.
복잡한 운동학 해석에 장점이 있어 생체역학 등에 사용되기도 한다.
물리 엔진 내부 반복(Iterations)과 충돌 처리
Gazebo에서는 기본 타임스텝 설정뿐 아니라, 물리 엔진 내부에서 추가적으로 ‘반복(iterations)’ 파라미터를 설정할 수 있다. 예를 들어 ODE 엔진의 경우, 충돌 계산 후 조인트나 접촉 제약(contact constraints)을 해석하기 위해 일정 횟수의 내부 반복 계산을 수행한다.
iterations 수 증가
충돌 계산 및 제약 해석 과정이 더 정밀해져, 충돌 시 관통이나 튀어오름 현상을 줄일 수 있다.
연산 비용이 증가하므로, 시뮬레이션 속도가 느려질 수 있다.
iterations 수 감소
연산 비용이 줄어 시뮬레이션 속도가 빨라지지만, 충돌이나 조인트 운동 해석이 부정확해질 위험이 있다.
결국 타임스텝과 물리 엔진 내부 반복 횟수는 트레이드오프(trade-off) 관계이며, 실험 목적과 자원 한도를 고려하여 세팅해야 한다.
센서 데이터와 시간 동기화
ROS2에서 센서 데이터를 사용할 때는 시뮬레이션 시간을 기준으로 토픽에 Time Stamp를 붙이고, 이를 ROS2 노드에서 받아서 처리하게 된다. 이때 실제 시간과 시뮬레이션 시간이 불일치하면, 센서 데이터의 타임스탬프가 실제 시간대(real-world clock)를 반영하지 않으므로 주의가 필요하다.
use_sim_time:
ROS2 노드가 “/clock” 토픽을 참조하여 시뮬레이션 시간을 기준으로 동작하도록 할 수 있다.
이를 통해 센서 데이터, TF(Transform), 각종 메시지 등이 동일한 시간축을 공유하게 된다.
/clock 토픽 모니터링:
다음과 같이 시뮬레이션에서 발행되는 시간을 확인할 수 있다.
시뮬레이션 시간이 얼마나 빠르게 또는 느리게 흐르는지, 그리고 실제 시간과 얼마나 동기화되고 있는지 수시로 확인할 수 있다.
실제 시간 vs. 시뮬레이션 시간의 구분
실제 시간: CPU와 OS가 제공하는 시스템 시계(clock)를 의미하며, 우리가 일상에서 사용하는 시간.
시뮬레이션 시간: Gazebo 물리 엔진이 내부적으로 카운트하며, max_step_size 단위로 증가시켜 나가는 가상 시간.
ROS2 환경에서 시뮬레이션을 활용할 경우, 주로 시뮬레이션 시간(use_sim_time)을 참조하여 노드와 센서 데이터를 동기화한다. 다만, 데이터 로깅(logging)이나 UI 이벤트 처리는 실제 시간을 참조하는 경우도 있으므로, 상황에 맞게 주의 깊게 다뤄야 한다.
물리 시뮬레이션과 모델링 정확도
시뮬레이션 타임스텝과 반복(iterations) 파라미터를 적절히 조정하더라도, 실제 물리계를 100% 재현하기는 어렵다. 따라서 Gazebo나 다른 시뮬레이터를 사용할 때는 정확도와 계산 리소스 사이의 균형점을 찾는 일이 매우 중요하다.
물리 파라미터 세팅의 중요성:
마찰 계수, 관성 모멘트, 질량 등 물리 파라미터를 실제 로봇에 맞게 최대한 정확히 반영해야 한다.
센서(카메라, LiDAR 등)도 노이즈 파라미터를 적용해 실제 센서 동작을 최대한 비슷하게 만들어야 한다.
시뮬레이션 vs. 실제 테스트:
시뮬레이션에서 로봇이 안정적으로 움직이더라도, 실제 물리 환경에서 동일한 거동을 보장하지 않을 수 있다.
따라서 시뮬레이션 결과를 참고하되, 실제 실험을 통해 검증하는 과정이 필수적이다.
시뮬레이션 모델 복잡도와 타임스텝
시뮬레이션 대상이 복잡해질수록(예: 멀티 조인트 로봇, 여러 대의 이동 로봇, 복잡한 지형 지물 등) 한 스텝마다 계산할 물리량이 기하급수적으로 늘어난다. 이때 타임스텝을 너무 작게 잡으면 물리 계산이 정교해지지만 시뮬레이션 전체의 진행 속도가 심각하게 느려질 수 있다.
비선형 및 충돌 이벤트:
로봇 팔이 복잡한 조인트 운동을 할 때, 특정 자세에서 조인트에 가해지는 힘이 급격히 변할 수 있다.
물체 간 충돌이 발생하면 충돌 처리 루틴(collision response)으로 인해 계산 부하가 순간적으로 증가한다.
실제 현상의 물리적 의미를 잘 해석하여, 작은 타임스텝이 필요한 구간에서만 세밀하게 시뮬레이션하는 전략(Adaptive Time Stepping)을 고려할 수도 있다.
GPU 가속과 병렬화:
일부 물리 엔진(예: Bullet)은 GPU 가속을 지원하며, 이를 통해 시뮬레이션 계산 속도를 크게 높일 수 있다.
복수 개의 코어를 사용하는 멀티스레딩(multi-threading)을 지원할 수도 있으므로, 하드웨어 사양을 적극 활용하도록 세팅해야 한다.
예시: 특정 프로젝트에서의 타임스텝 튜닝
아래는 가상의 예시다. 로봇 팔 2대를 시뮬레이션하면서 각 로봇에 3D LiDAR 센서를 달았고, 1초에 수십 회 이상의 충돌 체크가 발생한다고 가정해 보자.
초기 세팅:
시뮬레이션에서 1초 동안 1000번의 스텝, 각 스텝 당 1ms 단위 계산, 충돌 및 조인트 제약 해석을 위해 50회의 내부 반복을 설정했다고 가정한다.
RTF 확인:
실제 측정된 RTF가 약 0.8 정도로 나타난다고 하면, 실제 1초 동안 시뮬레이션 시간은 약 0.8초가 흐른다는 뜻이다.
GPU나 CPU 부하를 줄이거나, 충돌 계산을 단순화하지 않는 이상 실시간에 근접시키기 어려울 수 있다.
옵션 조정:
max_step_size를 0.002(2ms)로 살짝 올려서, 내부적으로 물리 계산 빈도를 절반 정도로 줄인다.
iters(내부 반복 횟수)를 20 정도로 낮춰서, 충돌 계산을 최소한으로 한다.
real_time_update_rate는 500으로 조정하여, 시뮬레이션이 실제 시간보다 약간 느리게 진행되도록 한다.
이렇게 조정했을 때 시뮬레이션 정확도가 다소 떨어질 수 있지만, RTF를 0.9 이상으로 끌어올릴 수도 있다. 결국 프로젝트 목적(자율주행 알고리즘 검증, 로봇 팔 제어 패스 테스트 등)에 따라 타협점을 찾는 과정이 필요하다.
Last updated