为对象配置行为#

仿真中的每个对象都可以独立指定行为以模拟不同场景。通过在 YAML 配置文件中设置行为参数即可完成配置。

IR-SIM 支持两种行为配置类型:

  • behavior:控制单个对象的运动(如 dash、rvo、sfm)

  • group_behavior:协调群组中所有对象的行为(如 orca)

行为配置参数#

内置的个体行为包括 dash(直接朝目标移动)、rvo(互惠速度障碍,用于多智能体避障)以及 sfm(社会力模型——反应式行人风格避障)。默认情况下,移动对象没有行为,会保持静止,除非外部下发控制指令。

行为按机动学注册,并非每一种(机动学,行为)组合都存在:

机动学

可用行为

diff

dash、rvo、sfm

omni

dash、rvo、sfm

omni_angular

dash

acker

dash

若为某种机动学指定了未注册的行为(例如给 acker 指定 rvo),对象构造将报错。如需补足缺失组合,请使用自定义行为。

下面示例展示了 RVO 行为:

import irsim

env = irsim.make()

for i in range(1000):
    env.step()
    env.render(0.05)

    if env.done():
        break

env.end()
world:
  height: 10  # the height of the world
  width: 10   # the width of the world

robot:
  - number: 10
    distribution: {name: 'circle', radius: 4.0, center: [5, 5]}  
    kinematics: {name: 'diff'}
    shape: 
      - {name: 'circle', radius: 0.2} 
    behavior: {name: 'rvo', vxmax: 1.5, vymax: 1.5, acce: 1.0, factor: 1.0}
    vel_min: [-3, -3.0]
    vel_max: [3, 3.0]
    color: 'cycle'   # one palette color per robot, in order
    arrive_mode: position
    goal_threshold: 0.15
    plot:
      show_trail: true
      show_goal: true
      trail_fill: true
      trail_alpha: 0.2
      show_trajectory: false

关键行为参数说明#

通用参数:

  • name: 行为类型('dash'、'rvo'、'sfm' 或自定义名称)

  • wander: 抵达当前目标后是否随机生成下一个目标(默认 False)

  • loop: 抵达最后一个目标点后是否循环导航所有路径点(默认 False)

  • target_roles: 行为作用对象筛选('all'、'robot'、'obstacle')

RVO 专用参数:

  • vxmax/vymax: x/y 方向最大速度(默认 1.5)

  • acce: 每步速度的最大变化量(默认 1.0)。候选速度在当前速度 ± acce 内采样,并裁剪到 vxmax/vymax;已超出限幅的速度会减速到最近的可行值

  • factor: 碰撞惩罚权重(默认 1.0,越大越保守)

  • mode: 算法模式——'rvo'(默认)、'hrvo' 或 'vo'

  • neighbor_threshold: 邻近目标检测范围(默认 3.0 米)

SFM 专用参数(各向异性的 Moussaid-Helbing 2009 变体):

  • vmax: 力积分后施加的速度上限(默认 1.5)

  • neighbor_threshold: 社会力交互的距离截断(默认 10.0)

  • relaxation_time: 目标牵引时间常数 tau(默认 0.5)

  • force_factor_desired: 目标牵引力权重(默认 1.0)

  • force_factor_social: 智能体间互斥力权重(默认 2.1)

  • force_factor_obstacle: 障碍物互斥力权重(默认 10.0)

  • sigma_obstacle: 障碍物力的指数衰减长度(默认 0.8)

  • lambda_importance: 在交互方向上速度相对位置的权重(默认 2.0)

  • gamma: 决定交互范围 B = gamma * ||t||(默认 0.35)

  • n_angular/n_velocity: 侧向力与减速力的角度锐度指数(默认分别为 2.0、3.0)

Dash 专用参数:

  • angle_tolerance: diff/acker 机动学对准方向的容差(默认 0.1 弧度)

完整的行为参数列表请参见 YAML 配置。

SFM(社会力模型)#

sfm 是一种反应式避障行为,将每个智能体视为受三种力作用的牛顿粒子——目标牵引力、各向异性的邻居互斥力(位于智能体运动方向上的人产生的反作用力强于身后的人),以及指数衰减的障碍物互斥力。实现采用 Moussaid、Helbing 等人 (2009) 提出的各向异性变体。

sfm 与 rvo/orca 互为补充:RVO 与 ORCA 是几何方法,在其假设下可证明无碰撞,而 SFM 是行为方法,生成更接近真人的轨迹——包括偶发的擦碰、犹豫,以及对前后邻居不对称的反应。需要群体真实感时使用 SFM;以无碰撞为优先时使用 RVO/ORCA。

下面的示例为 usage/23sfm_world/sfm_world.yaml:24 个行人穿越由两条 6.6 m 走廊构成的 + 字形交叉口。每条车道承载一对相向通行的行人(东西向蓝/红,南北向绿/橙),L 形墙角作为线段障碍进入 SFM,使行人沿墙弯绕而非切角通过。

备注

请将 YAML 配置文件与主 Python 脚本放在同一目录下。

import irsim

env = irsim.make()

while not env.done():
    env.step()
    env.render(0.01)

env.end()
world:
  height: 20
  width: 20
  step_time: 0.1
  offset: [-10, -10]
  collision_mode: 'unobstructed'   # SFM is reactive; allow brief overlap
  control_mode: 'auto'

robot:
  - number: 24
    distribution: {name: 'manual'}
    kinematics: {name: 'diff'}
    shape: [{name: 'circle', radius: 0.25}]
    # 12 horizontal head-on pairs + 12 vertical head-on pairs, interleaved
    # so adjacent indices share a lane (ir-sim steps objects sequentially,
    # so the pair coupling depends on adjacency in env.objects).
    state:
      - [-9, -2.6, 0]
      - [ 9, -2.601, 3.14]
      - [-9, -1.6, 0]
      - [ 9, -1.601, 3.14]
      # ... (six horizontal lanes + six vertical lanes, see usage file)
    goal:
      - [ 9, -2.6, 0]
      - [-9, -2.601, 3.14]
      - [ 9, -1.6, 0]
      - [-9, -1.601, 3.14]
      # ...
    color: 'cycle'   # one palette color per pedestrian, in order
    behavior:
      name: 'sfm'
      loop: true
      vmax: 0.7
      neighbor_threshold: 5.0
      force_factor_social: 6.0
      force_factor_obstacle: 8.0       # walls must hold during loop-reset
      lambda_importance: 1.0           # cleaner repulsion along line of centres
      gamma: 0.5
      sigma_obstacle: 0.6              # wall felt across most of the 6.6 m corridor
      n_angular: 1.5                   # agents commit to a side earlier
      n_velocity: 2.5
      safety_radius: 0.12              # 0.24 m personal bubble
    vel_min: [-1.5, -3.0]
    vel_max: [ 1.5,  3.0]
    arrive_mode: position
    goal_threshold: 1.0                # loop-reset fires before the wall stalls the goal approach
    plot: {show_trail: true, show_goal: true, trail_fill: true, trail_alpha: 0.2, keep_trail_length: 25}

obstacle:
  - number: 4
    distribution: {name: 'manual'}
    state: [0, 0, 0]
    unobstructed: true
    shape:
      - {name: 'linestring', vertices: [[-10,  3.3], [-3.3,  3.3], [-3.3,  10]]}
      - {name: 'linestring', vertices: [[ 10,  3.3], [ 3.3,  3.3], [ 3.3,  10]]}
      - {name: 'linestring', vertices: [[-10, -3.3], [-3.3, -3.3], [-3.3, -10]]}
      - {name: 'linestring', vertices: [[ 10, -3.3], [ 3.3, -3.3], [ 3.3, -10]]}

完整可运行文件见 usage/23sfm_world/(24 个智能体的十字走廊,以及另一个双机器人迎面交会示例)。若要模拟人群,sfm 也可作为群组行为使用,它一次性推进所有成员,并能模拟结伴而行的行人。

RVO 与线段障碍物#

rvo 除了避让圆形智能体之外,还会自动避让 linestring 形状的障碍物。linestring 对象中相邻顶点之间的每一段线段都会被视为静态速度障碍,因此 RVO 智能体无需额外配置即可绕开墙体或折线:

robot:
  - number: 6
    kinematics: {name: 'diff'}
    shape: {name: 'circle', radius: 0.2}
    distribution: {name: 'circle', radius: 4.0, center: [5, 5]}
    behavior: {name: 'rvo'}

obstacle:
  - shape: {name: 'linestring', vertices: [[2, 2], [2, 8], [8, 8]]}

该机制对 omni 与 diff 机动学都生效;非 linestring 障碍物仍按邻居智能体参与计算。邻居是带有自身当前速度的圆盘(静止时速度为零),无论机器人本身在运动还是静止,构造的速度锥都相同。

群组行为#

behavior 控制单个对象的运动,而 group_behavior 则用于协调同一群组内所有对象的行为。群组行为在单步计算中为所有成员生成动作,比逐个计算个体行为更高效。

行为与群组行为对比#

特性

behavior

group_behavior

作用域

单个对象

群组中的所有对象

计算方式

每步逐对象计算

一次性计算所有成员

使用场景

简单导航

多智能体协调

可用行为

dash、rvo、sfm(取决于运动学模型 —— 见上方矩阵)

orca、sfm(omni、diff)

ORCA(最优互惠碰撞避免)#

ORCA 是一种经典的内置群组级碰撞避免算法,可同时为多个智能体计算最优速度。即使有数百个智能体,也能确保平滑无碰撞的导航。

ORCA 同时支持 omni 和 diff 运动学模型。它为每个成员规划一个完整(全向)速度 (vx, vy):omni 机器人直接使用该速度,而 diff 机器人会将其映射为 (线速度, 角速度) 指令,因此差速机器人会朝规划方向转向,并在尚未对准时减速。在群组上设置 kinematics: {name: 'diff'} 即可使用差速变体;下方的所有 ORCA 参数保持不变。每个成员的期望速度指向其目标,大小为该智能体的速度上限:成员在任意方向都能保持的最高速度(omni 取 vel_max 中较小的分量,diff 取线速度限幅),若设置了 maxSpeed 则再降到该值,因此在无遮挡时成员以全速行进。

备注

ORCA 需要 pyrvo 库,它是 ORCA C++ 算法的 Python 绑定。使用以下命令安装:

pip install pyrvo

基本 ORCA 配置#

import irsim

env = irsim.make()

while True:
    env.step()
    env.render()

    if env.done():
        break

env.end()
world:
  height: 290
  width: 290
  step_time: 0.1
  sample_time: 0.7
  offset: [-140, -140]

robot:
  - number: 200
    color: 'cycle'   # one palette color per robot, in order
    kinematics: {name: 'omni'}
    shape: {name: 'circle', radius: 1.0}
    distribution: {name: 'circle', radius: 140}
    goal_threshold: 0.1
    vel_max: [2, 2]
    vel_min: [-2, -2]
    plot:
      show_trajectory: True
      show_goal: True
      keep_traj_length: 50
    group_behavior:
      name: 'orca'
      neighborDist: 15.0
      maxNeighbors: 10
      timeHorizon: 20.0
      timeHorizonObst: 10.0
      safe_radius: 0.5
      maxSpeed: 2.0

ORCA 参数说明#

参数

类型

默认值

描述

name

str

-

必须为 'orca'

neighborDist

float

15.0

将其他智能体视为邻居的最大距离

maxNeighbors

int

10

碰撞避免考虑的最大邻居数量

timeHorizon

float

20.0

智能体间碰撞避免的时间视野(秒)

timeHorizonObst

float

10.0

智能体与障碍物碰撞避免的时间视野(秒)

safe_radius

float

0.1

添加到智能体半径的额外安全边距

maxSpeed

float

无

每个智能体的速度上限,也是其朝目标行进的速度;不超过机器人在任意方向都能保持的最高速度(omni 取 vel_max 中较小的分量,diff 取线速度限幅),默认即为该速度

wander

bool

False

到达当前目标时生成随机目标

loop

bool

False

抵达最后一个目标点后循环导航所有路径点

range_low

list

-

随机目标生成的下界 [x, y, theta]

range_high

list

-

随机目标生成的上界 [x, y, theta]

带随机漫游的 ORCA#

用于智能体到达目标后继续移动的持续仿真:

import irsim

env = irsim.make()

for i in range(1000):
    env.step()
    env.render()

    if env.done():
        break

env.end()
world:
  height: 290
  width: 290
  step_time: 0.1
  sample_time: 0.7
  offset: [-140, -140]

robot:
  - number: 100
    color: 'cycle'   # one palette color per robot, in order
    kinematics: {name: 'omni'}
    shape: {name: 'circle', radius: 1.0}
    distribution: {name: 'random', range_low: [-100, -100, -3.14], range_high: [100, 100, 3.14]}
    goal_threshold: 0.1
    vel_max: [3, 3]
    vel_min: [-3, -3]
    plot:
      show_trajectory: True
      show_goal: True
      keep_traj_length: 50
    group_behavior:
      name: 'orca'
      neighborDist: 15.0
      maxNeighbors: 10
      timeHorizon: 20.0
      timeHorizonObst: 10.0
      safe_radius: 0.5
      maxSpeed: 2.0
      wander: True
      range_low: [-100, -100, -3.14]
      range_high: [100, 100, 3.14]

路点循环导航#

适用于机器人持续巡逻、按预定义路点循环导航的场景:

robot:
  kinematics: {name: diff}
  goal: [[1, 1], [5, 1], [5, 5], [1, 5]]
  behavior:
    name: dash
    loop: true

当 loop: true 时,机器人会依次经过所有路点,到达最后一个路点后从第一个路点重新开始。

小技巧

大多数情况下在 YAML 中使用 group_behavior,它会自动处理初始化。若需要完全的控制 —— 自定义碰撞逻辑或外部规划器 —— 则不要设置 behavior,而是通过向 env.step(action) 传入速度指令来自行驱动每个机器人。

作为群组行为的 SFM#

sfm 也可以作为 omni 与 diff 运动学的群组行为使用。它与逐对象的 sfm 行为采用相同的物理模型和参数名,由 SocialForceModelBatch 一次性为所有成员求解:

  • 同步更新。 所有成员读取同一份状态快照,因此人群行为不再依赖对象在 YAML 中的排列顺序。单个成员的轨迹与 behavior: sfm 下完全一致。

  • 向量化。 成对的社会力通过一次 NumPy 运算覆盖所有成员对,而不是逐成员的 Python 循环,因此大规模人群的开销依然很低。

  • 社会群组。 social_groups 可选地把成员划分为结伴而行的行人,从而加入 Moussaid 等人(2010)的三种群组力:朝群组质心的凝聚拉力、两名成员的圆盘相触时的组内排斥力,以及当群组其余成员离开视野时让该成员减速的注视制动力。

群组之外的对象仍会参与其中:linestring 障碍物作为墙壁进入模型,其他所有对象则作为圆形邻居——它们会推开成员,但不会被成员推动。target_roles、loop 与 wander 的用法与逐对象行为相同。没有目标的成员会原地站立,但仍会为邻居让路。

批量求解与社会群组力遵循 PySocialForce(扩展社会力模型的 NumPy 实现),并采用 pedsim_ros 的力定义。

下面的示例是 usage/23sfm_world/sfm_group_world.yaml:两对两人组与两组三人组在走廊中相向而行,彼此重叠一条车道,另有两名单独行人从中间穿过。每个群组作为整体侧向移动以避让迎面而来的群组。

import irsim

env = irsim.make()

while not env.done():
    env.step()
    env.render(0.01)

env.end()
world:
  height: 10
  width: 20
  step_time: 0.1
  sample_time: 0.1
  offset: [-10, -5]
  collision_mode: 'unobstructed'   # SFM is reactive; let pedestrians overlap briefly
  control_mode: 'auto'

robot:
  - number: 12
    distribution: {name: 'manual'}
    kinematics: {name: 'diff'}
    shape: [{name: 'circle', radius: 0.25}]
    state:
      - [-9,  1.5, 0]
      - [-9,  2.2, 0]
      - [-9, -1.5, 0]
      - [-9, -2.2, 0]
      - [-9, -2.9, 0]
      - [ 9, -2.4, 3.14]
      - [ 9, -3.1, 3.14]
      - [ 9,  2.4, 3.14]
      - [ 9,  3.1, 3.14]
      - [ 9,  3.8, 3.14]
      - [-9,  0.3, 0]
      - [ 9, -0.3, 3.14]
    goal:
      - [ 9,  1.5, 0]
      - [ 9,  2.2, 0]
      - [ 9, -1.5, 0]
      - [ 9, -2.2, 0]
      - [ 9, -2.9, 0]
      - [-9, -2.4, 3.14]
      - [-9, -3.1, 3.14]
      - [-9,  2.4, 3.14]
      - [-9,  3.1, 3.14]
      - [-9,  3.8, 3.14]
      - [ 9,  0.3, 0]
      - [-9, -0.3, 3.14]
    color: 'cycle'   # one palette color per pedestrian, in order
    group_behavior:
      name: 'sfm'
      vmax: 1.0
      neighbor_threshold: 5.0
      force_factor_social: 4.0         # head-on stiffness for a 12-agent corridor
      force_factor_obstacle: 5.0
      sigma_obstacle: 0.3              # walls only matter within ~1 m
      gamma: 0.5
      safety_radius: 0.15              # 0.3 m personal bubble for vmax=1.0
      social_groups: [[0, 1], [2, 3, 4], [5, 6], [7, 8, 9]]
      force_factor_coherence: 2.0      # pull toward the group centre
      force_factor_group_repulsion: 1.0
      group_repulsion_threshold: 0.7   # members push apart once their discs touch
      force_factor_gaze: 3.0           # leader brakes when the group leaves its view
      gaze_vision_angle: 90.0
    vel_min: [-1.5, -3.0]
    vel_max: [ 1.5,  3.0]
    arrive_mode: position
    goal_threshold: 0.5
    plot:
      show_trail: true
      show_goal: true
      trail_fill: true
      trail_alpha: 0.2
      keep_trail_length: 25


obstacle:
  - shape: {name: 'linestring', vertices: [[-10,  5], [10,  5]]}
    state: [0, 0, 0]
    unobstructed: true
  - shape: {name: 'linestring', vertices: [[-10, -5], [10, -5]]}
    state: [0, 0, 0]
    unobstructed: true

群组 SFM 参数#

所有逐对象 SFM 参数(vmax、neighbor_threshold、relaxation_time、force_factor_desired、force_factor_social、force_factor_obstacle、sigma_obstacle、lambda_importance、gamma、n_angular、n_velocity、safety_radius)保持相同的名称与默认值。群组版本额外增加:

参数

类型

默认值

描述

name

str

-

必须为 'sfm'

social_groups

list of list of int

无

结伴而行的行人的成员索引(按 YAML 群组顺序)。每个成员最多只能出现在一个群组中;省略即关闭群组力

force_factor_coherence

float

2.0

朝群组质心拉力的权重。当成员距质心超过 (size - 1) / 2 米时平滑地生效

force_factor_group_repulsion

float

1.0

两名靠得过近的群组成员之间推力的权重

group_repulsion_threshold

float

无

两名成员开始相互排斥的中心距离阈值。None 表示使用两者半径之和,即圆盘一接触就相互推开

force_factor_gaze

float

3.0

当其余成员的质心离开视野时施加的制动力 -alpha * v 的权重;alpha 为需要补足的头部转角(弧度)

gaze_vision_angle

float

90.0

以行走方向为中心的视野半角(度)

target_roles

str

'all'

成员对哪些外部对象作出反应:'all'、'robot' 或 'obstacle'

wander

bool

False

到达当前目标后生成随机目标

loop

bool

False

抵达最后一个目标点后循环导航所有路径点

自定义群组行为#

你可以创建自定义群组行为以实现多智能体协调控制。IR-SIM 提供两种注册模式:

  • @register_group_behavior:基于函数(每步调用)

  • @register_group_behavior_class:基于类(初始化一次,然后每步调用)

基于函数与基于类的行为对比#

方面

基于函数

基于类

装饰器

@register_group_behavior

@register_group_behavior_class

初始化

无(无状态)

启动时执行一次

状态

无持久状态

可在步骤间保持状态

使用场景

简单计算

需要初始化的复杂算法

示例

简单编队控制

ORCA(需要仿真器设置)

自定义群组行为示例(基于类)#

以下是一个实现简单编队控制的自定义群组行为示例:

# custom_group_methods.py
from typing import Any
import numpy as np
from irsim.lib.behavior.behavior_registry import register_group_behavior_class
from irsim.world.object_base import ObjectBase


@register_group_behavior_class("omni", "formation")
def init_formation_behavior(members: list[ObjectBase], **kwargs: Any):
    """Registered initializer returning a class-based handler."""
    return FormationGroupBehavior(members, **kwargs)


class FormationGroupBehavior:
    """
    Class-based formation group behavior.
    Maintains formation shape while moving toward goals.
    """

    def __init__(
        self,
        members: list[ObjectBase],
        formation_gain: float = 1.0,
        goal_gain: float = 0.5,
        **kwargs: Any,
    ) -> None:
        """
        Initialize formation behavior.

        Args:
            members: List of group members
            formation_gain: Weight for formation keeping
            goal_gain: Weight for goal reaching
        """
        self._formation_gain = formation_gain
        self._goal_gain = goal_gain

        # Calculate initial formation offsets (relative positions)
        if members:
            centroid = np.mean([m.state[:2, 0] for m in members], axis=0)
            self._offsets = [m.state[:2, 0] - centroid for m in members]
        else:
            self._offsets = []

    def __call__(self, members: list[ObjectBase], **kwargs: Any) -> list[np.ndarray]:
        """
        Generate velocities for all members to maintain formation.

        Args:
            members: Current group members
            **kwargs: Additional parameters

        Returns:
            List of velocity arrays for each member
        """
        if not members:
            return []

        velocities = []

        # Calculate current centroid
        centroid = np.mean([m.state[:2, 0] for m in members], axis=0)

        # Calculate target centroid (average of all goals)
        goals = [m.goal[:2, 0] for m in members if m.goal is not None]
        if goals:
            target_centroid = np.mean(goals, axis=0)
        else:
            target_centroid = centroid

        for i, member in enumerate(members):
            # Desired position in formation
            desired_pos = (
                centroid + self._offsets[i]
                if i < len(self._offsets)
                else member.state[:2, 0]
            )

            # Formation keeping velocity
            formation_vel = self._formation_gain * (desired_pos - member.state[:2, 0])

            # Goal reaching velocity
            goal_vel = self._goal_gain * (target_centroid - centroid)

            # Combined velocity
            vel = formation_vel + goal_vel

            # Clip to max speed
            speed = np.linalg.norm(vel)
            if speed > member.max_speed:
                vel = vel / speed * member.max_speed

            velocities.append(np.c_[vel])

        return velocities

使用自定义群组行为#

import irsim

env = irsim.make()
env.load_behavior("custom_group_methods")

for i in range(500):
    env.step()
    env.render()

    if env.done():
        break

env.end()
world:
  height: 20
  width: 20

robot:
  - number: 5
    color: 'cycle'   # one palette color per robot, in order
    kinematics: {name: 'omni'}
    shape: {name: 'circle', radius: 0.3}
    distribution: {name: 'manual'}
    state: [[10, 13, 0], [7.15, 10.93, 0], [8.24, 7.57, 0], [11.76, 7.57, 0], [12.85, 10.93, 0]]   # a ring of radius 3 around (10, 10)
    goal: [15, 15, 0]   # shared goal: the formation moves its centroid there
    vel_max: [2, 2]
    vel_min: [-2, -2]
    group_behavior:
      name: 'formation'
      formation_gain: 1.0
      goal_gain: 0.5

基于函数的群组行为示例#

对于无需初始化的简单行为:

# custom_group_methods.py
from typing import Any
import numpy as np
from irsim.lib.behavior.behavior_registry import register_group_behavior
from irsim.world.object_base import ObjectBase


@register_group_behavior("omni", "swarm")
def beh_omni_swarm(members: list[ObjectBase], **kwargs: Any) -> list[np.ndarray]:
    """
    Simple swarm behavior - all members move toward group centroid.

    Args:
        members: List of group members
        **kwargs: Additional parameters (cohesion_gain, goal_gain)

    Returns:
        List of velocity arrays for each member
    """
    if not members:
        return []

    cohesion_gain = kwargs.get("cohesion_gain", 0.5)
    goal_gain = kwargs.get("goal_gain", 1.0)

    # Calculate centroid
    centroid = np.mean([m.state[:2, 0] for m in members], axis=0)

    velocities = []
    for member in members:
        # Cohesion: move toward centroid
        cohesion_vel = cohesion_gain * (centroid - member.state[:2, 0])

        # Goal: move toward individual goal
        if member.goal is not None:
            goal_vel = goal_gain * (member.goal[:2, 0] - member.state[:2, 0])
        else:
            goal_vel = np.zeros(2)

        vel = cohesion_vel + goal_vel

        # Clip to max speed
        speed = np.linalg.norm(vel)
        if speed > member.max_speed:
            vel = vel / speed * member.max_speed

        velocities.append(np.c_[vel])

    return velocities

重要

  • 当行为需要初始化时使用 @register_group_behavior_class(如设置外部库、计算初始状态)

  • 对于简单的无状态行为使用 @register_group_behavior

  • 第一个参数是机动学类型('omni'、'omni_angular'、'diff'、'acker')

  • 第二个参数是在 YAML 中使用的行为名称 group_behavior: {name: 'your_name'}

自定义行为的高级配置#

自定义行为函数#

若希望为对象创建自定义行为,需先在自定义 Python 脚本(如 custom_behavior_methods.py)中定义行为,如下所示:

from irsim.lib import register_behavior
from irsim.util.util import relative_position, WrapToPi
import numpy as np


@register_behavior("diff", "dash_custom")
def beh_diff_dash(ego_object, objects, **kwargs):

    state = ego_object.state
    goal = ego_object.goal
    goal_threshold = ego_object.goal_threshold
    _, max_vel = ego_object.get_vel_range()
    angle_tolerance = kwargs.get("angle_tolerance", 0.1)

    behavior_vel = DiffDash2(state, goal, max_vel, goal_threshold, angle_tolerance)

    return behavior_vel


def DiffDash2(state, goal, max_vel, goal_threshold=0.1, angle_tolerance=0.2):

    distance, radian = relative_position(state, goal)

    if distance < goal_threshold:
        return np.zeros((2, 1))

    diff_radian = WrapToPi(radian - state[2, 0])
    linear = max_vel[0, 0] * np.cos(diff_radian)

    if abs(diff_radian) < angle_tolerance:
        angular = 0
    else:
        angular = max_vel[1, 0] * np.sign(diff_radian)

    return np.array([[linear], [angular]])

ego_object 表示需要控制的对象,objects 是仿真中的对象列表。自定义行为函数需返回对象的速度,并可从对象中获取状态、目标等属性。DiffDash2 就是一个计算冲刺速度的自定义行为函数。

重要

必须使用 @register_behavior 装饰器注册自定义行为。装饰器的第一个参数为机动学名称(diff、acker、omni、omni_angular),第二个参数为在 YAML 中引用的行为名称。

注册行为#

下方给出了运行自定义行为所需的 Python 脚本与 YAML。

import irsim

env = irsim.make()
env.load_behavior("custom_behavior_methods")

for i in range(1000):
    env.step()
    env.render(0.01)

    if env.done():
        break

env.end(5)

load_behavior 函数会从 Python 脚本中加载自定义行为方法。custom_behavior_methods 是包含自定义行为函数的脚本名称。

备注

请将名为 custom_behavior_methods 的脚本与主 Python 脚本放在同一目录下。

下方最后两个机器人使用自定义 dash_custom 行为,并与内置 RVO 和 dash 机器人进行比较。

world:
  height: 10  # the height of the world
  width: 10   # the width of the world

robot:
  - number: 3
    distribution: {name: 'circle', radius: 4.0, center: [5, 5]}
    kinematics: {name: 'diff'}
    shape: [{name: 'circle', radius: 0.2}]
    behavior: {name: 'rvo', vxmax: 1.0, vymax: 1.0, acce: 1.0, factor: 1.0}
    vel_min: [-3, -3.0]
    vel_max: [3, 3.0]
    color: ['#0072B2', '#D55E00', '#009E73']
    arrive_mode: position
    goal_threshold: 0.2
    plot:
      show_trail: true
      show_goal: true
      trail_fill: true
      trail_alpha: 0.2
      show_trajectory: false

  - number: 2
    distribution: {name: 'circle', radius: 4.0, center: [5, 5]}
    kinematics: {name: 'diff'}
    shape: [{name: 'circle', radius: 0.2}]
    behavior: {name: 'dash', vxmax: 1.5, vymax: 1.5, acce: 1.0, factor: 1.0}
    vel_min: [-3, -3.0]
    vel_max: [3, 3.0]
    color: ['#E69F00', '#CC79A7']
    arrive_mode: position
    goal_threshold: 0.2
    plot:
      show_trail: true
      show_goal: true
      trail_fill: true
      trail_alpha: 0.2
      show_trajectory: false

  - number: 2
    distribution: {name: 'random'}
    kinematics: {name: 'diff'}
    shape: [{name: 'circle', radius: 0.2}]
    behavior: {name: 'dash_custom'}
    vel_min: [-3, -3.0]
    vel_max: [3, 3.0]
    color: ['#56B4E9', '#117733']
    arrive_mode: position
    goal_threshold: 0.2
    plot:
      show_trail: true
      show_goal: true
      trail_fill: true
      trail_alpha: 0.2
      show_trajectory: false

自定义行为类(有状态)#

对于需要初始化或需要在步骤间保持状态的行为,请使用 @register_behavior_class 装饰器。适用场景包括:

  • 行为需要一次性设置(如加载模型、初始化规划器)

  • 需要在仿真步骤间跟踪历史或状态

  • 希望缓存计算以提高效率

基于函数与基于类的个体行为对比#

方面

@register_behavior

@register_behavior_class

执行方式

每步调用

__init__ 执行一次,__call__ 每步调用

状态

无状态

可在步骤间保持状态

设置

无

在 __init__ 中一次性初始化

使用场景

简单反应式行为

复杂规划器、基于学习的控制

基于类的行为示例#

# custom_behavior_methods.py
from typing import Any
import numpy as np
from irsim.lib.behavior.behavior_registry import register_behavior_class


@register_behavior_class("diff", "smooth_dash")
def init_smooth_dash(object_info, **kwargs):
    """Registered initializer returning a class-based handler."""
    return SmoothDashBehavior(object_info, **kwargs)


class SmoothDashBehavior:
    """
    Class-based smooth dash behavior with velocity smoothing.
    Maintains previous velocity for smooth acceleration.
    """

    def __init__(self, object_info, smoothing: float = 0.3, **kwargs):
        """
        Initialize smooth dash behavior.

        Args:
            object_info: Object information from ObjectBase
            smoothing: Smoothing factor (0-1), higher = smoother
        """
        self._smoothing = smoothing
        self._prev_vel = np.zeros((2, 1))
        self._object_info = object_info

    def __call__(
        self,
        ego_object,
        external_objects: list,
        **kwargs: Any,
    ) -> np.ndarray:
        """
        Generate smoothed velocity toward goal.

        Args:
            ego_object: The controlled object
            external_objects: Other objects in environment
            **kwargs: Additional parameters

        Returns:
            Smoothed velocity array (2x1)
        """
        if ego_object.goal is None:
            return np.zeros((2, 1))

        # Calculate desired velocity (simple dash)
        state = ego_object.state
        goal = ego_object.goal
        _, max_vel = ego_object.get_vel_range()

        diff = goal[:2, 0] - state[:2, 0]
        distance = np.linalg.norm(diff)

        if distance < ego_object.goal_threshold:
            target_vel = np.zeros((2, 1))
        else:
            # Convert to diff drive velocities
            angle_to_goal = np.arctan2(diff[1], diff[0])
            diff_angle = angle_to_goal - state[2, 0]
            diff_angle = np.arctan2(np.sin(diff_angle), np.cos(diff_angle))

            linear = max_vel[0, 0] * np.cos(diff_angle)
            angular = (
                max_vel[1, 0] * np.sign(diff_angle) if abs(diff_angle) > 0.1 else 0
            )

            target_vel = np.array([[linear], [angular]])

        # Apply smoothing (exponential moving average)
        smoothed_vel = (
            self._smoothing * self._prev_vel + (1 - self._smoothing) * target_vel
        )
        self._prev_vel = smoothed_vel

        return smoothed_vel

使用基于类的行为#

import irsim

env = irsim.make()
env.load_behavior("custom_behavior_methods")

for i in range(500):
    env.step()
    env.render(0.05)

    if env.done():
        break

env.end()
world:
  height: 10
  width: 10

robot:
  # Robot using class-based smooth_dash behavior
  - kinematics: {name: 'diff'}
    shape: {name: 'circle', radius: 0.2}
    state: [2, 2, 0]
    goal: [8, 8]
    color: '#0072B2'
    vel_max: [2, 2]
    vel_min: [-2, -2]
    goal_threshold: 0.3
    behavior:
      name: 'smooth_dash'
      smoothing: 0.5
    plot:
      show_goal: true
      show_trajectory: true
      keep_traj_length: 200

  # Robot using regular dash behavior for comparison
  - kinematics: {name: 'diff'}
    shape: {name: 'circle', radius: 0.2}
    state: [2, 3, 0]
    goal: [8, 7]
    color: '#D55E00'
    vel_max: [2, 2]
    vel_min: [-2, -2]
    goal_threshold: 0.3
    behavior: {name: 'dash'}
    plot:
      show_goal: true
      show_trajectory: true
      keep_traj_length: 200

行为注册总结#

IR-SIM 提供四种行为注册装饰器:

装饰器

作用域

类型

使用场景

@register_behavior

个体

函数

简单的逐对象行为

@register_behavior_class

个体

类

有状态的逐对象行为

@register_group_behavior

群组

函数

简单的多智能体协调

@register_group_behavior_class

群组

类

复杂的多智能体算法(如 ORCA)

所有装饰器接受两个参数:

  1. 机动学类型:'omni'、'omni_angular'、'diff' 或 'acker'

  2. 行为名称:在 YAML 配置中使用的名称

小技巧

根据需求选择合适的装饰器:

  • 对于简单行为,从基于函数的装饰器开始(@register_behavior 或 @register_group_behavior)

  • 当需要初始化、状态或复杂算法时使用基于类的装饰器

  • 当动作依赖于多个智能体协同时使用群组行为

外部控制器示例(CBF / C3BF)#

并非所有控制器都需要通过行为注册系统接入。仓库在 usage/21cbf_world/ 中提供了独立的安全过滤控制器:

  • cbf_qp.py / cbf_world.py —— 基于距离的 控制屏障函数(CBF) 安全过滤器,通过 QP 求解。它在朝向目标的标称指令外,针对圆形障碍物施加避障约束。

  • c3bf_qp.py / c3bf_world.py —— 碰撞锥 CBF(C3BF) 变体,额外考虑相对速度,适用于动态障碍物。

两个示例均支持 omni 与 diff 机动学,可从 YAML 中读取机动学类型,并需要 cvxpy 求解器(pip install cvxpy)。完整公式、支持的障碍物类型与已知限制详见 usage/21cbf_world/README.md。