Source code for pomdp_py.problems.multi_object_search.agent.belief

# Defines the belief distribution and update for the 2D Multi-Object Search domain;
#
# The belief distribution is represented as a Histogram (or Tabular representation).
# Since the observation only contains mapping from object id to their location,
# the belief update has no leverage on the shape of the sensing region; this is
# makes the belief update algorithm more general to any sensing region but then
# requires updating the belief by iterating over the state space in a nested
# loop. The alternative is to use particle representation but also object-oriented.
# We try both here.
#
# We can directly make use of the Histogram and Particle classes in pomdp_py.
import pomdp_py
import random
import copy
from ..domain.state import *


[docs] class MosOOBelief(pomdp_py.OOBelief): """This is needed to make sure the belief is sampling the right type of State for this problem.""" def __init__(self, robot_id, object_beliefs): """ robot_id (int): The id of the robot that has this belief. object_beliefs (objid -> GenerativeDistribution) (includes robot) """ self.robot_id = robot_id super().__init__(object_beliefs)
[docs] def mpe(self, **kwargs): return MosOOState(pomdp_py.OOBelief.mpe(self, **kwargs).object_states)
[docs] def random(self, **kwargs): return MosOOState(pomdp_py.OOBelief.random(self, **kwargs).object_states)
[docs] def initialize_belief( dim, robot_id, object_ids, prior={}, representation="histogram", robot_orientations={}, num_particles=100, ): """ Returns a GenerativeDistribution that is the belief representation for the multi-object search problem. Args: dim (tuple): a tuple (width, length) of the search space gridworld. robot_id (int): robot id that this belief is initialized for. object_ids (dict): a set of object ids that we want to model the belief distribution over; They are `assumed` to be the target objects, not obstacles, because the robot doesn't really care about obstacle locations and modeling them just adds computation cost. prior (dict): A mapping {(objid|robot_id) -> {(x,y) -> [0,1]}}. If used, then all locations not included in the prior will be treated to have 0 probability. If unspecified for an object, then the belief over that object is assumed to be a uniform distribution. robot_orientations (dict): Mapping from robot id to their initial orientation (radian). Assumed to be 0 if robot id not in this dictionary. num_particles (int): Maximum number of particles used to represent the belief Returns: GenerativeDistribution: the initial belief representation. """ if representation == "histogram": return _initialize_histogram_belief( dim, robot_id, object_ids, prior, robot_orientations ) elif representation == "particles": return _initialize_particles_belief( dim, robot_id, object_ids, robot_orientations, num_particles=num_particles ) else: raise ValueError("Unsupported belief representation %s" % representation)
def _initialize_histogram_belief(dim, robot_id, object_ids, prior, robot_orientations): """ Returns the belief distribution represented as a histogram """ oo_hists = {} # objid -> Histogram width, length = dim for objid in object_ids: hist = {} # pose -> prob total_prob = 0 if objid in prior: # prior knowledge provided. Just use the prior knowledge for pose in prior[objid]: state = ObjectState(objid, "target", pose) hist[state] = prior[objid][pose] total_prob += hist[state] else: # no prior knowledge. So uniform. for x in range(width): for y in range(length): state = ObjectState(objid, "target", (x, y)) hist[state] = 1.0 total_prob += hist[state] # Normalize for state in hist: hist[state] /= total_prob hist_belief = pomdp_py.Histogram(hist) oo_hists[objid] = hist_belief # For the robot, we assume it can observe its own state; # Its pose must have been provided in the `prior`. assert robot_id in prior, "Missing initial robot pose in prior." init_robot_pose = list(prior[robot_id].keys())[0] oo_hists[robot_id] = pomdp_py.Histogram( {RobotState(robot_id, init_robot_pose, (), None): 1.0} ) return MosOOBelief(robot_id, oo_hists) def _initialize_particles_belief( dim, robot_id, object_ids, prior, robot_orientations, num_particles=100 ): """This returns a single set of particles that represent the distribution over a joint state space of all objects. Since it is very difficult to provide a prior knowledge over the joint state space when the number of objects scales, the prior (which is object-oriented), is used to create particles separately for each object to satisfy the prior; That is, particles beliefs are generated for each object as if object_oriented=True. Then, `num_particles` number of particles with joint state is sampled randomly from these particle beliefs. """ # For the robot, we assume it can observe its own state; # Its pose must have been provided in the `prior`. assert robot_id in prior, "Missing initial robot pose in prior." init_robot_pose = list(prior[robot_id].keys())[0] oo_particles = {} # objid -> Particageles width, length = dim for objid in object_ids: particles = [ RobotState(robot_id, init_robot_pose, (), None) ] # list of states; Starting the observable robot state. if objid in prior: # prior knowledge provided. Just use the prior knowledge prior_sum = sum(prior[objid][pose] for pose in prior[objid]) for pose in prior[objid]: state = ObjectState(objid, "target", pose) amount_to_add = (prior[objid][pose] / prior_sum) * num_particles for _ in range(amount_to_add): particles.append(state) else: # no prior knowledge. So uniformly sample `num_particles` number of states. for _ in range(num_particles): x = random.randrange(0, width) y = random.randrange(0, length) state = ObjectState(objid, "target", (x, y)) particles.append(state) particles_belief = pomdp_py.Particles(particles) oo_particles[objid] = particles_belief # Return Particles distribution which contains particles # that represent joint object states particles = [] for _ in range(num_particles): object_states = {} for objid in oo_particles: random_particle = random.sample(oo_particles[objid], 1)[0] object_states[_id] = copy.deepcopy(random_particle) particles.append(MosOOState(object_states)) return pomdp_py.Particles(particles) """If `object oriented` is True, then just like histograms, there will be one set of particles per object; Otherwise, there is a single set of particles that represent the distribution over a joint state space of all <objects. When updating the particle belief, Monte Carlo simulation is used instead of computing the probabilities using T/O models. This means one must sample (s',o,r) from G(s,a). If this belief representation if object oriented, then you have N particle sets for N objects. Thanks to the fact that in this particular domain, objects are static, you could have si' = si if i is an object. However, if robot state sr' needs to consider collision with other objects, then it can't be obtained just from sr. This means eventually you would have to build an s by sampling randomly from the particle set for each object. More details on the non-object-oriented case: Since it is extremely difficult to provide a prior knowledge over the joint state space when the number of objects scales, the prior (which is object-oriented), is used to create particles separately for each object to satisfy the prior; That is, particles beliefs are generated for each object as if object_oriented=True. Then, `num_particles` number of particles with joint state is sampled randomly from these particle beliefs. """