参考内容
Simulink仿真时间、步长、精度和解法器设置_simulink时间步长-CSDN博客
Simulink学习笔记5--Simulink仿真设置_downsample simulink 设置-CSDN博客
Isaac Sim Speedup Cheat Sheet — Omniverse IsaacSim
Are there parameters to speed up the simulation? - Isaac Sim - NVIDIA Developer Forums
Speed up the simulation without affecting the physics - Isaac Sim - NVIDIA Developer Forums
文章内代码均来自于 Isaac 论坛
注意:本文中的修改传感器帧率的方式是一种作弊方式,通过拉长仿真的一秒流速来变相提高传感器的帧率。也就是说仿真内的一秒可能是现实时间的三秒、四秒..这个对应关系是动态变化的,因此所有节点的时间都必须使用仿真时间戳。
仿真时间的时间概念与真实的时间并不一样,是仿真环境中中对时间的一种表示,比如10秒的仿真时间,如果采样步长定为0.1,则需要执行100步,若把步长减小,则采样点数增加,那么实际的执行时间就会增加。一般仿真开始时间设为0,而结束时间视不同的因素而选择。
模拟的速度主要由物理步长和最小模拟帧速率决定。这里的影响因素包括:场景的复杂度、物理对象的数量和可用的计算资源等。
在 MatLab Simunlink 中也有仿真步长这个定义,但因为我自己没使用过,只能靠他人笔记,这里先放上他人的博客链接,之后自己如果使用到了 Matlab 的仿真步长再来看。
Simulink学习笔记5--Simulink仿真设置_downsample simulink 设置-CSDN博客
Simulink仿真时间、步长、精度和解法。器设置_simulink时间步长-CSDN博客
回到 Isaac Sim 的话题,在这个仿真软件中有着大量与时间有关的参数:
- (Edit - Preference - Animation - Playback Settings):ALL
- (Edit - Preference - Developer - Throttle Rendering): UI FPS Limit , Present Thread FPS Limit
- (Edit - Preference - Stage - New Stage): Default Animation Rate(TimeCodesPerSecond)
- (Window - Simulation - Settings - Simulator): Min Simulation Frame Rate
- (Create - Physics - Physics Scene)->(Stage - PhysicsScene - Property - Physics - Scene): Time Steps Per Second
1:播放设置
- Fixed Time Step:固定时间步长(用于控制物理更新的频率)
- Play Every Frame:每帧播放(使动画或模拟在每一帧都更新)
- Wall Clock Time Delay Compensation:实时时钟时间延迟补偿(用于调整因时间误差造成的播放不同步问题)
- Frames Per Frame:每帧的帧数(可能是指每帧更新的次数,用于更精细的控制)
- Snap Timeslider to Frame:时间滑块对齐到帧(将时间轴上的播放头对齐到最近的帧)
2:限制渲染操作的频率
- UI FPS Limit:用户界面更新的帧率,如果一个应用程序的UI FPS Limit设置为30,那么UI元素将每秒钟更新30次
- Present Thread FPS Limit:限制渲染线程(即负责将图像呈现到屏幕上的线程)的帧率。如果设置为60,那么渲染线程将尝试每秒钟呈现60帧图像到屏幕
3:舞台设置
- Default Animation Rate(TimeCodesPerSecond):每一秒所包含的时间码数,即每秒播放多少帧或者多少个时间码
4:模拟
- Min Simulation Frame Rate:最小模拟帧率。指模拟器每秒更新画面或计算一次模拟数据的频率,单位为帧每秒。可以影响仿真时间的推进速度。调整帧率能修改传感器数据响应实时性。它定义了物理学应该有多少子步的下限,否则如果一般帧速率不够高,模拟会不断减慢整个应用程序的速度。
5:物理
- Time Steps Per Second:每秒时间步数。Time Step是计算物体运动状态变化的最小时间单位。在一秒钟内,系统能够处理或计算多少个这样的时间步。这个数值越高,表示模拟的更新频率越高,通常意味着更平滑的动画或更精确的模拟结果。
在不同需求需要修改哪些参数,修改为什么,需要完全了解这些时间参数的含义。
在这些时间参数中,重要而又容易混淆概念的有三个:
- Default Animation Rate (TimeCodesPerSecond)
- Time Steps Per Second
- Min Simulation Frame Rate
1. Default Animation Rate (TimeCodesPerSecond)
-
功能:
- 定义动画的时间分辨率,即每秒钟有多少帧用于插值或驱动动画播放
- 通常与渲染动画的时间相关,主要影响视觉效果而非物理仿真的精度
-
典型值:
- 24(电影帧率)或 30(标准动画帧率)
- 如果与仿真同步,可设为与仿真帧率一致:60 ~ 100 等
-
作用:
- 主要用于动画或渲染的平滑度。如果只关注物理仿真性能,可以忽略该参数
2. Time Steps Per Second
-
功能:
- 定义物理引擎的仿真时间步长,即每秒计算多少次物理步进。
- 直接决定物理仿真的精度和稳定性:
- 高值:更高的精度,但计算更慢。
- 低值:较快,但可能导致不稳定或不准确。
-
典型值:
- 通常设置为 100 或 1000,具体取决于仿真需求
- 100(0.01 秒步长)是常见默认值,用于大多数中等复杂度的仿真
- 1000(0.001 秒步长)用于需要高精度(例如机器人控制)的仿真
-
是否关键:
- 物理仿真高度依赖。需要根据仿真精度和硬件性能调整
3. Min Simulation Frame Rate
-
功能:
- 定义仿真和现实时间同步时的最小帧率
- 物理引擎的运行频率(由 Time Steps Per Second 决定)和渲染帧率(由 Default Animation Rate 决定)之间存在时间步长的关系,Min Simulation Frame Rate 确保仿真不会因过低帧率而失真
-
典型值:
- 通常设置为 60(与人眼可见的平滑度一致)
- 如果硬件资源紧张,可以降低该值以减少对渲染帧率的压力,但不能低于物理步长的频率
-
是否关键:
- 当仿真性能不足时,该值可以调整来缓解硬件压力
修改仿真环境内的传感器帧率
移步到之前的文章:Isaac Sim 15 帧率控制_issac sim 播放帧率-CSDN博客
记得使用这条命令
rosparam set use_sim_time true
加快仿真速度
目的是在 Isaac Sim 中运行大量移动机器人同步进行测试,使用轮式机器人和轮式里程计。但是实际使用发现,仿真中的运行速度比实际速度慢,也就是仿真时间滞后于实际时间(仿真时间1秒约等于现实时间3秒)。设置传感器帧率后,用 rosbag 查看会发现是类似于快进的。
可以通过增加时间步长(“每秒更新”更小)来加快模拟速度,但这会使模拟变得不稳定,碰撞检测会变得特别糟,机身不停抖动,无法进行方向控制。每秒时间步长的选择会对仿真稳定性产生影响,因为它允许在碰撞之间进行更多迭代,并更准确地捕获对象相互穿透及其产生的排斥力,因此具有更精确的脉冲响应。
不可控抖动的修改方式: (Window - Simulation - Settings - Collision):Cylinders As Custom Geometry
如何在不影响物理特性的情况下加快仿真速度,是本文的主要内容。
轮式机器人主要使用传感器:odom, imu, camera。在使用多个传感器时,首先需要的是足够优秀的 GPU, 才能提高 FPS。GPU 使用推荐查看 Isaac sim 官方手册:
Isaac Sim Requirements — Omniverse IsaacSim
使用 Python 脚本设置仿真速度
import sys
import time
import signal
from omni.isaac.kit import SimulationApp
real_frame_per_second = 60.0
internal_frame_per_second = 60.0
kit = None
is_processing = False
def scheduler(signum, frame):
global kit
global is_processing
if is_processing == False:
is_processing = True
kit.update()
is_processing = False
def main():
global kit
kit = SimulationApp({"renderer": "RayTracedLighting", "headless": False, "open_usd": usd_path})
import omni
from omni.isaac.core import World
my_world = World(stage_units_in_meters = 1.0, physics_dt = 1.0 / internal_frame_per_second, rendering_dt = 1.0 / internal_frame_per_second)
# Get stage handle
stage_handle = omni.usd.get_context().get_stage()
signal.signal(signal.SIGALRM, scheduler)
signal.setitimer(signal.ITIMER_REAL, 1/real_frame_per_second, 1/real_frame_per_second)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
# Shutdown and exit
omni.timeline.get_timeline_interface().stop()
kit.close()
if __name__ == '__main__':
main()
将物理步长和渲染步长分离
def update(self, dt=0.0, physics_dt=None, physics_substeps=None):
"""Render one frame. Optionally specify dt in seconds, specify None to use wallclock.
Specify physics_dt and physics_substeps to decouple the physics step size from rendering
For example: to render with a dt of 1/30 and simulate physics at 1/120 use:
- dt = 1/30.0
- physics_dt = 1/120.0
- physics_substeps = 4
Args:
dt (float): The step size used for the overall update, set to None to use wallclock
physics_dt (float, optional): If specified use this value for physics step
physics_substeps (int, optional): Maximum number of physics substeps to perform
"""
# dont update if exit was called
if self._exiting:
return
if physics_substeps is not None and physics_substeps > 0:
self.kit_settings.set("/physics/maxNumSteps", int(physics_substeps))
if dt is not None:
if self.kit_settings and dt > 0.0:
if physics_dt is None or physics_dt <= 0.0:
self.kit_settings.set("/physics/timeStepsPerSecond", float(1.0 / dt))
else:
self.kit_settings.set("/physics/timeStepsPerSecond", float(1.0 / physics_dt))
self.app.update(dt)
else:
time_now = time.time()
dt = time_now - self.last_update_t
self.last_update_t = time_now
if self.kit_settings and dt > 0.0:
if physics_dt is None or physics_dt <= 0.0:
self.kit_settings.set("/physics/timeStepsPerSecond", float(1.0 / dt))
else:
self.kit_settings.set("/physics/timeStepsPerSecond", float(1.0 / physics_dt))
self.app.update(dt)
在GUI界面中的位置未找到,可能已经删去。
可以用/physics/timeStepsPerSecond来设置物理步长
计算每次渲染要运行的模拟步骤数:Max num steps
/physics/maxNumSteps
用于强制执行与渲染或时间轴按钮分离的物理时间步长:
from pxr import Usd, UsdGeom
import omni.usd
from omni.physx import acquire_physx_interface
stage = omni.usd.get_context().get_stage()
print(stage)
physx = acquire_physx_interface()
dt = 1/60.0
physx.reset_simulation()
physx.start_simulation()
# Simulate 20 steps, increase start_time if the desired initial step time is not at zero
start_time = 0.0
for i in range(20):
physx.update_simulation(dt, start_time+ i*dt)
physx.update_transformations(False, True)
# To stop the simulation and reset the scene:
def stop():
physx.reset_simulation()
physx.update_transformations(False, True)
如果想以精确的 10 倍挂钟时间运行模拟,需要的是某种定时循环(例如,每 1/600 秒进入一次循环)
period = 1/600.0
while True:
start_time = time.time()
do_stuff() # a function where you call the physx update simulation and everything you need to do
end_time = time.time()
threading.sleep(period - (end_time -start_time)) # Sleep this loop for the period minus the time it took to execute the code