创建环境#
IR-SIM 仿真由 YAML 场景文件通过 irsim.make() 创建,并由一段简短的 Python 循环(env.step() → env.render() → env.done())驱动。本页介绍如何构建环境、核心循环、状态控制以及动态对象管理。若需同时运行多个场景,请参阅多环境。
Python 脚本与 YAML 配置文件#
要开始仿真,需要先创建一个环境。环境是仿真中所有对象的容器,并负责在每个时间步更新仿真状态。
可以使用简单的 Python 脚本创建环境:
import irsim
env = irsim.make("basic_world.yaml")
make 函数会根据配置文件创建环境,支持的参数包括:
world_name(str,可选):世界 YAML 配置文件路径projection(str,可选):投影类型(“3d” 表示 3D 环境,None 表示 2D)step_mode("internal"或"external",可选):覆盖当前环境的world.step_mode;若省略,则使用 YAML 中的值。display(bool):是否显示环境可视化(默认 True)save_ani(bool):是否将仿真保存为动图(默认 False)log_level(str):环境日志级别(默认 "INFO")seed(int,可选):IR-SIM 随机数引擎的种子。提供后,IR-SIM 生成的随机内容即可复现;若省略或为None,则使用未设种子的生成器(不可复现)。若自定义扩展仍使用np.random或 Pythonrandom,需改用 IR-SIM RNG 或另行设定种子。
更多信息见 EnvBase 类文档。
在 YAML 文件(basic_world.yaml)中定义一个最小可运行世界:
world:
height: 10 # the height of the world (meters)
width: 10 # the width of the world (meters)
step_time: 0.1 # simulation time step (seconds) - 10Hz
sample_time: 0.1 # rendering frequency (seconds) - 10Hz
offset: [0, 0] # the offset of the world origin [x, y]
step_mode: 'internal' # state advancement: 'internal' or 'external'
control_mode: 'auto' # control mode: 'auto', 'keyboard'
collision_mode: 'stop' # collision behavior: 'stop', 'unobstructed', 'unobstructed_obstacles'
obstacle_map: null # path to obstacle map file (optional)
robot:
kinematics: {name: diff}
shape: {name: circle, radius: 0.2}
state: [1, 1, 0]
goal: [9, 9, 0]
behavior: {name: dash}
color: g
plot:
show_trajectory: true
show_goal: true
配置文件定义世界和机器人,主循环将机器人从起点推进到目标。
重要参数说明#
世界配置#
world:定义仿真环境属性的主段height与width:以米为单位指定世界尺寸step_time:控制仿真精度与速度(越小越精确但更慢)sample_time:控制渲染频率(越大仿真越快,可视化越不平滑)offset:以米为单位偏移世界坐标系原点 [x, y]step_mode:决定由谁推进对象状态'internal':IR-SIM 根据动作或已配置的行为积分更新状态'external':外部代码提供状态,IR-SIM 同步派生数据
control_mode:决定仿真控制方式'auto':自动执行仿真'keyboard':手动键盘控制
collision_mode:定义碰撞检测行为'stop':发生碰撞时停止仿真(默认)'unobstructed':忽略所有碰撞'unobstructed_obstacles':仅忽略障碍物碰撞
obstacle_map:可选。障碍物地图图像的路径,或生成器配置(例如{ name: perlin, ... })。参见 配置栅格地图。
性能考量#
更小的
step_time:物理更精确但仿真更慢更大的
sample_time:仿真更快但可视化不够流畅世界尺寸:更大的世界需要更多算力
小技巧
可以通过 sample_time 控制渲染频率、提升仿真速度。默认情况下 sample_time 与 step_time 相同。
更详细的参数说明参见 YAML 配置。
小技巧
自动配置检测:默认 YAML 配置文件与 Python 脚本同名。例如脚本为 test.py,IR-SIM 会在同目录自动寻找 test.yaml。
import irsim
# Automatically uses 'test.yaml' if this file is 'test.py'
env = irsim.make()
该特性避免手动指定配置文件,简化了开发流程。
基础仿真循环#
创建环境后,通常会运行如下仿真循环:
import irsim
env = irsim.make("config.yaml")
# Main simulation loop
for i in range(1000):
env.step() # Update simulation state
env.render(0.05) # Render with 0.05 second interval (20Hz)
if env.done(): # Check if simulation should end
break
env.end() # Clean up resources
核心方法说明#
env.step():让仿真前进一步env.render(interval):按设定的帧间隔刷新可视化env.done():当满足结束条件时返回Trueenv.reset(random=False):将对象恢复为初始状态。传入random=True时,会基于缓存的 YAML 解析结果重建世界,从而让随机元素(如distribution: random、随机形状生成器)按当前 RNG 状态重新采样 —— 可配合irsim.util.random.set_seed(seed)获得可复现的全新场景。此操作不会重新读取磁盘上的 YAML 文件;如需加载磁盘上的修改,请改用env.reload()。env.refresh():在不推进仿真的前提下,刷新由状态派生的属性(几何、传感器读数、碰撞树以及状态)。当你直接修改对象状态(例如robot.set_state(...))后,若希望在下一次env.step()之前让传感器和碰撞信息同步到最新状态,使用此方法。env.reload(world_name=None):重新解析 YAML 文件(可选指定其他文件)并重建世界/对象。env.get_msg():捕获无需额外依赖的 ROS 风格世界快照。env.receive_msg(...):将外部 ROS 风格里程计应用到 IR-SIM 对象,并刷新其派生状态。env.end():正确关闭环境并释放资源env.close():env.end()的别名,提供 Gym 风格的 API 兼容性
小技巧
更新顺序
环境会先推进所有对象,再更新全部传感器。这种两阶段更新保证传感器读取到的都是最新世界状态。若手动推进对象,请在 ObjectBase.step(...) 传入 sensor_step=True,或在更新对象状态后调用 obj.sensor_step()。
用于批量训练的无头模式#
传入 headless=True 即可在没有任何图形、窗口和键盘/鼠标控制的情况下运行。环境创建开销降低数倍,丢弃环境时也不会留下任何残留,这在训练循环需要创建大量环境时很重要;render()、各绘图辅助函数和 save_figure() 都会变成空操作,因此同一脚本无需修改即可运行:
env = irsim.make("config.yaml", headless=True)
disable_all_plot=True 作为别名保留。如果需要离屏渲染并仍然保存动画或图片,请改用 display=False。
内部与外部步进模式#
默认的 internal 模式保持 IR-SIM 的常规循环。可以显式提供动作,也可以省略动作,由已配置的行为生成动作:
env = irsim.make("config.yaml")
env.step(action=[1.0, 0.0], action_id=0)
动作的传入方式遵循常见的单智能体 / 多智能体惯例:
env.step([1.0, 0.5]):把一个动作(列表、元组或 ndarray)施加给第一个机器人。env.step(action, action_id=2):作用于 id 为2的对象(即obj.id;机器人最先创建,因此 id0..n-1就是机器人)。也可以用名字,如"robot_2"。env.step([a0, a1, a2]):按顺序给每个机器人一个动作,或从action_id开始依次施加。env.step(actions, action_id=[0, 3]):动作与给定的 id 一一对应。env.step({"robot_0": a0, "robot_3": a3}):以机器人名字为键的字典。
多余的动作会被丢弃并给出警告;不存在的 id 或名字会抛出 ValueError。
在 external 模式下,状态由另一个仿真器或外部系统更新。请先提供新的状态和速度,再调用不带动作的 env.step():
env = irsim.make("config.yaml", step_mode="external")
robot = env.robot
while not env.done():
state, velocity = external_system.read_robot()
robot.set_state(state)
robot.set_velocity(velocity)
env.step()
外部步进不会执行 IR-SIM 的运动学或行为逻辑。它基于同一状态快照刷新所有对象的几何信息、重建碰撞索引、更新传感器和状态、记录轨迹,并推进世界时钟。在此模式下向 env.step() 传入 action 会引发 ValueError,以防意外混用内部和外部状态推进方式。
交换仿真消息#
当控制器、日志记录器、桥接程序或学习流程需要一致的仿真器快照时,可使用 env.get_msg()。返回的 WorldState 按照常见 ROS 话题名称组织每个对象的数据:robot.odom 是 Odometry 消息,robot.scan 是其主 LaserScan 消息。
msg = env.get_msg()
print(msg.header.seq) # simulation step count
print(msg.header.stamp) # simulation time in seconds
robot = msg.robots[0]
print(robot.odom.pose.pose.position.x)
print(robot.odom.twist.twist.linear.x)
if robot.scan is not None:
print(robot.scan.ranges)
payload = msg.to_dict() # JSON-compatible lists and scalar values
消息类型提供稳定的逻辑 ros_type 提示,例如 robot.odom.ros_type == "nav_msgs/Odometry"。其中的斜杠形式仅为兼容性保留,并不表示选择 ROS 1。桥接程序负责选择原生 ROS 1 或 ROS 2 消息类,并将 IR-SIM 的浮点时间戳和序号转换为相应的 Header 结构。scans 列表包含全部 LiDAR 读数,scan 是第一项主读数的别名。
LaserScan 仅包含共享的 sensor_msgs/LaserScan 数据字段;没有强度数据时,intensities 为空数组。其角度元数据能够精确重建仿真的光束方向。由于 IR-SIM 从同一个几何快照计算全部光束,time_increment 为零;scan_time 是配置的扫描间隔。笛卡尔目标速度、FMCW 径向速度和有效性等 IR-SIM 专用测量仍可通过 sensor.get_scan() 或 env.get_lidar_scan() 获取。
里程计和扫描消息使用常见的 world、base_link 和传感器坐标系名称。ROS 桥接程序仍负责发布相应的 /tf、/tf_static 和 /clock 消息。
消息是特定时刻的副本:之后调用 env.step() 或对象设置方法,不会修改已经捕获的消息。这些消息类无需额外依赖,也不要求安装 ROS;ROS 桥接程序可在边界处将它们映射为原生 ROS 消息。
使用 env.receive_msg(...) 可由其他仿真器或 ROS 桥接程序驱动 IR-SIM。该方法接受完整的 WorldState、单个 ObjectState,或 IR-SIM/原生 ROS 风格的 Odometry。独立的里程计消息默认更新主机器人;也可以通过稳定的对象名称或 IR-SIM 对象 ID 选择其他本地对象:
external_odom = source_env.get_msg().robots[0].odom
updated = env.receive_msg(external_odom, object_name="message_robot")
assert updated == 1
对于 WorldState,传入对象会先按名称、再按 ID 与本地对象匹配。IR-SIM 应用二维位姿和机体坐标系 twist,而接收环境保留自己的配置、目标、扫描数据和仿真时钟。默认情况下,每次调用还会刷新几何、本地仿真的传感器、碰撞和到达状态。仅在批量更新时传入 refresh=False,随后统一调用一次 env.refresh()。任何对象被修改之前,所有传入更新都会先完成验证。
桥接程序也可以单独复用这一转换:from_msg() 将原生 ROS 里程计消息转换为经过校验的 IR-SIM 消息,to_state_velocity() 则返回该消息对某个对象所对应的 (state, velocity) 数组,并且不会修改该对象。二者都是 from_object() 的逆运算,因此位姿和 twist 在“捕获—应用”的往返过程中保持一致。
完整的可运行示例位于 usage/25msg_world/。
环境控制与状态#
状态管理#
import irsim
env = irsim.make("config.yaml")
# Check current status
print(f"Current status: {env.status}")
print(f"Current time: {env.time}")
# Control simulation state
env.pause() # Pause the simulation
env.resume() # Resume the simulation
# Simulation loop with status checking
for i in range(50):
env.step()
env.render(0.05)
if i < 10:
env.pause()
elif i > 20 and i < 30:
env.resume()
if env.status == "Pause":
print("Environment is paused")
if env.done():
print("Simulation completed successfully")
env.end()
运行时状态值#
IR-SIM 会在仿真运行时更新 env.status。YAML 中的 world.status 设置仅提供初始显示标签;它不会暂停、恢复或停止执行。
"Running":环境在自动控制模式运行"Running (keyboard)":环境在键盘控制模式运行"Pause":仿真已暂停"Arrived":所有机器人都到达目标"Collision":检测到碰撞"Pause (Debugging)":调试模式(按下F5时)"Reset":环境已复位"Reload":环境已重新加载"Save Figure":已保存图像"Quit":环境已退出
配置环境标题#
默认情况下,环境标题会显示仿真时间与状态。可通过设置 show_title 并调用 env.set_title() 来自定义该行为。
import irsim
env = irsim.make("config.yaml")
# Set custom title
env.set_title("Multi-Robot Navigation Simulation")
# Update title dynamically
for i in range(100):
env.step()
# Update title every 10 steps
if i % 10 == 0:
env.set_title(f"Simulation Step: {i}")
env.render(0.05)
env.end()
world:
height: 20
width: 20
control_mode: 'auto'
plot:
show_title: true # Show title with time and status
show_axis: true # Optional: show axis labels
动态对象管理#
除了在 YAML 配置文件中定义对象外,还可以在运行时通过代码动态创建和添加对象。这适用于在仿真过程中动态生成对象,例如随机生成障碍物或动态添加机器人。
通过代码创建对象#
使用 env.create_robot() 和 env.create_obstacle() 通过关键字参数创建对象:
import irsim
env = irsim.make("empty_world.yaml")
# Create a differential-drive robot
robot = env.create_robot(
kinematics={"name": "diff"},
shape={"name": "circle", "radius": 0.2},
state=[1, 1, 0],
goal=[8, 8, 0],
name="robot_0",
)
# Create a static circular obstacle
obstacle = env.create_obstacle(
shape={"name": "circle", "radius": 0.5},
state=[5, 5, 0],
name="obs_0",
)
常用关键字参数包括:
kinematics(dict):运动学模型,如{"name": "diff"}、{"name": "omni"}、{"name": "acker"}。省略或使用{"name": "static"}表示静态对象。shape(dict):形状定义,如{"name": "circle", "radius": 0.5}或{"name": "polygon", "vertices": [[0,0],[1,0],[0,1]]}。state(list):初始状态[x, y, theta, ...]。goal(list):目标状态[x, y, theta, ...]。color(str):对象颜色(默认值因运动学类型而异)。name(str):唯一对象名称,不能与已有对象重名。goal_threshold(float):到达检测的距离阈值(默认 0.1)。
完整参数列表参见 ObjectBase 类文档。
向环境添加对象#
创建对象后,使用 env.add_object() 或 env.add_objects() 将其添加到环境中:
# Add a single object
env.add_object(robot)
# Add multiple objects at once
env.add_objects([obstacle])
每个对象必须有唯一名称。添加重名对象会引发 ValueError。
另请参阅#
多环境 —— 以相互隔离的状态同时运行多个场景,适用于对比研究或强化学习训练。