Source code for tbp.monty.simulators.habitat.sensors

# Copyright 2025 Thousand Brains Project
# Copyright 2022-2024 Numenta Inc.
#
# Copyright may exist in Contributors' modifications
# and/or contributions to the work.
#
# Use of this source code is governed by the MIT
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.

import uuid
from dataclasses import dataclass, field
from typing import List, Tuple

import quaternion as qt
from habitat_sim.sensor import CameraSensorSpec, SensorSpec, SensorType

__all__ = [
    "RGBDSensorConfig",
    "SemanticSensorConfig",
    "SensorConfig",
]

Vector3 = Tuple[float, float, float]
Quaternion = Tuple[float, float, float, float]
Size = Tuple[int, int]


[docs]@dataclass(frozen=True) class SensorConfig: """Sensor configuration. Every habitat sensor will inherit from this class. Attributes: sensor_id: Optional sensorID unique within the sensor module. If given then observations made by this sensor will be prefixed by this id. i.e. "`sensor_id`.data" position: Sensor position relative to :class:`HabitatAgent`. Default (0, 0, 0) rotation: Sensor rotation quaternion. Default (1, 0, 0, 0) """ sensor_id: str = field(default_factory=lambda: uuid.uuid4().hex) position: Vector3 = (0.0, 0.0, 0.0) rotation: Quaternion = (1.0, 0.0, 0.0, 0.0)
[docs] def get_specs(self) -> List[SensorSpec]: """Returns List of Habitat sensor specs to be passed to `habitat-sim`.""" return []
[docs] def process_observations(self, sensor_obs): """Callback used to process habitat raw sensor observations. Args: sensor_obs: Sensor raw habitat-sim observations Returns: dict: The processed observations grouped by agent_id """ return sensor_obs
[docs]@dataclass(frozen=True) class RGBDSensorConfig(SensorConfig): """RGBD Camera sensor configuration. Use this class to configure two different Habitat sensors simultaneously, one for RGBA observations and another for depth. Both sensors will use the same pose, resolution and zoom factor. RGB observations are named "rgba", depth observations are named "depth". Attributes: sensor_id: Sensor ID unique within the sensor module. If given then observations made by this sensor will be prefixed by this id. i.e. "`sensor_id`.rgba" resolution: Camera resolution (width, height). Default (64, 64) position: Sensor position relative to :class:`HabitatAgent`. Default (0, 0, 0) rotation: Sensor rotation quaternion. Default (1, 0, 0, 0) zoom: Camera zoom multiplier. Use >1 to increase, 0<factor<1 to decrease. Default 1.0, no zoom """ resolution: Size = (64, 64) zoom: float = 1.0
[docs] def get_specs(self) -> List[SensorSpec]: """Returns List of Habitat sensor specs to be passed to `habitat-sim`.""" orientation = qt.as_rotation_vector(qt.quaternion(*self.rotation)) # Configure RGBA camera rgba = CameraSensorSpec() rgba.uuid = f"{self.sensor_id}.rgba" rgba.sensor_type = SensorType.COLOR rgba.resolution = self.resolution rgba.position = self.position rgba.orientation = orientation # TODO: Make a parameter in config # rgba.noise_model = "GaussianNoiseModel" # rgba.noise_model_kwargs = dict(sigma=1) # Configure depth camera depth = CameraSensorSpec() depth.uuid = f"{self.sensor_id}.depth" depth.sensor_type = SensorType.DEPTH depth.resolution = self.resolution depth.position = self.position depth.orientation = orientation # This noise model doesn't seem to work with our sensor patch resolution. # depth.noise_model = "RedwoodDepthNoiseModel" # depth.noise_model_kwargs = dict(noise_multiplier=1) return [rgba, depth]
[docs]@dataclass(frozen=True) class SemanticSensorConfig(SensorConfig): """Semantic object sensor configuration. Use this class to configure a sensor to observe known objects in the scene returning their semantic IDs (ground truth) at each XY position. Semantic observations are named "semantic". Attributes: sensor_id: Optional sensor ID unique within the sensor module. If given then observations made by this sensor will be prefixed by this id. i.e."`sensor_id`.semantic" resolution: Camera resolution (width, height). Default (64, 64) position: Sensor position relative to :class:`HabitatAgent`. Default (0, 0, 0) rotation: Sensor rotation quaternion. Default (1, 0, 0, 0) zoom: Camera zoom multiplier. Use >1 to increase, 0<factor<1 to decrease. Default 1.0, no zoom """ resolution: Size = (64, 64) zoom: float = 1.0
[docs] def get_specs(self) -> List[SensorSpec]: """Returns List of Habitat sensor specs to be passed to `habitat-sim`.""" orientation = qt.as_rotation_vector(qt.quaternion(*self.rotation)) # Configure semantic camera semantic = CameraSensorSpec() semantic.channels = 1 semantic.uuid = f"{self.sensor_id}.semantic" semantic.sensor_type = SensorType.SEMANTIC semantic.resolution = self.resolution semantic.position = self.position semantic.orientation = orientation return [semantic]