[Documentation] [TitleIndex] [WordIndex


Designators are Common Lisp objects for describing various parameters in the CRAM Plan Language. To the user, designators are objects containing sequences of key-value pairs of symbols. For example, a location to see a specific object would be created as follows (the variable object must be bound to an object designator already:

(make-designator 'location `((to see) (obj ,object)))

The current implementation supports three different designator types, action designators as inputs for process modules, location designators for describing locations under constraints and object designators for describing objects on a symbolic level. The three designator types are derived from the same class but they are handled differently in the system.

Basic Concepts

While the three currently supported designator types vary in the way they are resolved, they share some common properties:

User API

The designators package provides a few API functions for constructing, equating and accessing designators. This API is mostly useful for the end-user. Besides the user API, the system provides different APIs for resolving the three different kinds of designators.

Designator Resolution

As already mentioned, the three currently supported designator classes action, object and location are resolved differently. Resolution means to generate real parameters for executing actions from the symbolic key-value pairs of a designator.

Object Designators

Object designators are a direct interface between CRAM plans and the perception subsystem used on a robot. The key-value-pairs of in the designator's properties describe the object that is to be perceived. Finding the right object based on them is a perception subsystem's task so the system does not provide any sophisticated mechanism for resolving an object designator. There is only one constraint on object designators: effective designators must bind the data object to an instance of object-designator-data or a class derived from it. Essentially, the following steps have to be implemented by the perception subsystem:

  1. Parse the designator's properties for a description of the object to be found.
  2. Find the described object.
  3. Create a new effective designator with the data object bound to an instance of object-designator-data containing all relevant information about the object.

  4. Equate the new effective designator with the original designator.

Action Designators

Action designator resolution is normally based on using an inference engine, namely prolog, to convert symbolic action descriptions to actual ROS action goals or similar data structures. To implement resolution for an action designator, the user has to provide definitions for the predicate action-desig ?designator ?solution. For instance, if we want to implement navigation, the corresponding action designator could be constructed like this:

(let ((goal-location 
         (make-designator 'location 
           `((pose ,(make-pose-stamped 
                     "base_footprint" 0.0 
                     (make-3d-vector 1.0 0.0 0.0) 
  (make-designator 'action '((type navigation) (goal ,goal-location)))

This designator describes the action of moving one meter forward. The only important information it contains, besides defining the actual action (type navigation) is the goal pose. For resolution it might be sufficient to just get the goal. The corresponding predicate action-desig can be defined as follows:

(def-fact-group navigation-action-designator (action-desig)
  (<- (action-desig ?designator ?goal)
    (desig-prop ?designator (type navigation))
    (desig-prop ?designator (goal ?goal))))

Location Designators

The most complex designator resolution mechanism currently is the implementation of location designators. It is that complex because it requires great flexibility to implement the computation of poses such as "a location to stand for opening a drawer". Essentially, the resolution of location designators is done in two steps:

  1. Generation of a lazy list of pose candidates.
  2. Verification if a generated pose candidate is actually a solution.

That way, generation can be kept very general and filters can be applied to remove invalid solutions. When the reference method is called on a location designator, the system first executes so-called generator functions to generate the sequence of pose candidates. Generator functions are priorized, i.e. they are ordered and some must be executed before others. Each generator function must be a function that gets a location designator as input and returns the (lazy) list of possible solutions for this designator. The system then appends all lists and tries to validate one solution after the other until either a solution is valid or a maximal number of solutions has been tried. In that case, the designator cannot be resolved and an error is thrown. Validation functions are functions with two parameters, the designator that is to be resolved and the pose candidate to validate. Depending on their result a solution can either be accepted, immediately rejected or rejected if no other validation function accepts it.

The following list describes the two API functions for registering generators and validation functions:

As mentioned already, generator functions must return (lazy) lists. Since the system supports lazy lists, it is possible to return sequences of poses with infinite length. However, if a generator returns such an infinite list, its priority should be chosen high to ensure that it is processed last. Otherwise, generators that return finite lists would not be processed at all.

Validation functions must return one of the following values:

A solution candidate is only accepted when all validation functions either returned :unknown or :accept, or, if at least one returned :maybe-reject, at least one returned :accept.

Handling of Symbols and Packages

Designator properties are lisp lists of pairs of symbols. Let's consider the following example of a designator description for an object designator:

'((type cup) (color red))

The user might want to use the same designator properties across different lisp packages that not necessarily know about each other. For instance, if implementing two libraries that could be used independent of each other and that use the same property symbols, we need a way to make sure that both designator properties are really eq in terms of Common Lisp comparison functions. We achieve that by enforcing all designator properties to be in the same package and by interning these symbols in all packages that declare the properties. In other words, a package that uses a specific set of designator properties needs to explicitly declare the corresponding symbols as designator properties. The underlying system then interns them in the package DESIG-PROPS and imports them in the package that contains the declarations. cram_designators provides a macro that wraps Common Lisp's DEFPACKAGE for implementing that behavior. To declare the above properties and a few more as designator properties in package foo, we use the following package definition for creating foo (instead of DEFPACKAGE):

(in-package :cl-user)

(desig-props:def-desig-package foo
  (:use #:common-lisp)
  (:export some-awesome-functon some-even-more-awesome-function)
  (:desig-properties type cup pot plate color red blue black green))

Please note that all packages that are either using or implementing designator properties are required to declare them properly.

2024-07-20 13:20