Sensors
This section explains how to use (and optionally extend) sensor models in PyRoboSim.
Sensor Definitions
The pyrobosim/sensors module contains all sensor model implementations.
What to Implement in a Sensor
The sensors implemented in PyRoboSim provide general functionality to simulate a sensor, as well as data for visualization in the UI.
First, you want subclass from the Sensor class and create a constructor as follows.
from pyrobosim.sensors.types import Sensor
class MyNewSensor(Sensor):
plugin_name = "my_sensor" # Needed to register the plugin
def __init__(
self,
*,
update_rate_s: float,
initial_value: float,
) -> None:
super().__init__()
self.update_rate_s = update_rate_s
self.latest_value = initial_value
Next, you should implement update() and get_measurement() functions.
These update the internals of the sensor and return the latest measurement, respectively.
def update(self) -> None:
# Increments the sensor value by 1.
self.latest_value += 1.0
def get_measurement(self) -> float:
return self.latest_value
If you want to run the sensor automatically in the background, you can also implement a thread_function() function.
This will only take effect if your robot is created with the start_sensor_threads argument set to True.
def thread_function(self) -> None:
if self.robot is None: # This is created in the constructor!
return
# The `is_active` attribute should be used to cleanly
# stop this thread when the sensor is shut down.
while self.is_active:
t_start = time.time()
self.update()
t_end = time.time()
time.sleep(max(0.0, self.update_rate_s - (t_end - t_start)))
For visualization, you can provide setup_artists() and update_artists() methods.
from matplotlib.artist import Artist
from matplotlib.patches import Circle
from matplotlib.transforms import Affine2D
def setup_artists(self) -> list[Artist]:
"""Executes when the sensor is first visualized."""
pose = self.robot.get_pose()
self.circle = Circle(
(pose.x, pose.y),
radius=1.0,
color="r",
)
return [self.circle]
def update_artists(self) -> None:
"""Updates the artist as needed."""
pose = self.robot.get_pose()
new_tform = Affine2D().translate(pose.x, pose.y)
self.circle.set_transform(new_tform)
To serialize to file, which is needed to reset the world, you should also implement the to_dict() method.
Note the plugin_name attribute, which contains the name of the sensor you defined earlier on.
def to_dict(self) -> dict[str, Any]:
return {
"type": self.plugin_name,
"update_rate_s": self.update_rate_s,
"initial_value": self.initial_value,
}
At this point, you can import your own sensor in code and load it dynamically using the Sensor parent class.
from pyrobosim.sensors import Sensor
from my_module import MyNewSensor # Still need to import this!
sensor_class = Sensor.registered_plugins["my_sensor"]
sensor = sensor_class(update_rate_s=0.1, initial_value=42)
… or from YAML world files.
robots:
name: robot
sensors:
my_cool_sensor:
type: my_sensor
update_rate_s: 0.1
initial_value: 42
If you would like to implement your own sensor, it is highly recommended to look at the existing sensor implementations as a reference. You can also always ask the maintainers through a Git issue!