Behavior Modulations#
This tutorial shows how to introduce a BehaviorModulation to modulate a behavior.
Behavior modulations are a more modular alternative to subclassing behaviors. Let’s say that you want to modify the parameters of the behavior dynamically, like
class MyBehavior(BehaviorParent):
def compute_cmd_internal(self, time_step: float, frame: core.Frame) -> core.Twist2:
# modify params
return super().compute_cmd_internal(time_step, frame)
Then, instead of a subclass, you can define a modulation like
class MyModulation(core.BehaviorModulation):
def pre(self, behavior: Behavior, time_step: float) -> None:
# modify params of `behavior`
...
and add it to any behavior:
behavior = BehaviorParent()
behavior.add_modulation(MyModulation())
The result will be the same as MyModulation.pre
will be executed just before the evaluation of BehaviorParent.compute_cmd
. Similarly, MyModulation.pre
will execute after the evaluation of BehaviorParent.compute_cmd
and may modify the computed command.
Let us look a simple example where we make the agents slow down when crowded.
[1]:
from navground import core, sim
import numpy as np
class SlowdownWhenCrowded(core.BehaviorModulation, name="Slowdown"):
_max_crowdedness = 20.0
@property
@core.register(20.0, "Minimal crowdedness to almost stop")
def max_crowdedness(self) -> float:
return self._max_crowdedness
@max_crowdedness.setter
def max_crowdedness(self, value: float) -> None:
return max(0.1, self._max_crowdedness)
def pre(self, behavior: core.Behavior, time_step: float) -> None:
crowdedness = sum(1 / np.linalg.norm(n.position - behavior.position) for n in behavior.environment_state.neighbors)
self._default_optimal_speed = behavior.optimal_speed
behavior.optimal_speed *= 1 - np.clip(0, 0.99, crowdedness / self.max_crowdedness)
def post(self, behavior: core.Behavior, time_step: float, cmd: core.Twist2) -> core.Twist2:
behavior.optimal_speed = self._default_optimal_speed
return cmd
Like behaviors, modulations are also registered and can be instatiated from YAML. Let us try it in a scenario where half of the agents (gold) are using the modulation while the other half not.
[2]:
scenario = sim.load_scenario("""
type: Cross
agent_margin: 0.1
side: 6
target_margin: 0.1
tolerance: 0.5
groups:
-
type: thymio
number: 20
radius: 0.08
control_period: 0.1
speed_tolerance: 0.02
kinematics:
type: 2WDiff
wheel_axis: 0.094
max_speed: 0.166
behavior:
type: HL
optimal_speed: 0.12
horizon: 5.0
safety_margin: 0.02
modulations:
- type: Slowdown
enabled: [true, false]
max_crowdedness: 30.0
color: [gold, cyan]
state_estimation:
type: Bounded
range: 5.0
""")
[3]:
from navground.sim.ui.video import display_video
world = sim.World()
scenario.init_world(world)
display_video(world, time_step=0.1, duration=60, factor=20, width=1200, display_width=600)
[3]: