The CRAM plan language is the core of the CRAM framework. It
provides the basic functionality to write flexible and robust
robot control programs.
The cram-language is a set of macros and functions on top of Common Lisp. It is a re-implementation of Drew McDermott's RPL on a modern Common Lisp compiler (sbcl), heavily utilizing operating system threads. The cram-language provides special language forms to easily create threads, synchronize on them and suspend them and provides clear exception semantics even across multiple threads.
This document contains the API documentation for the high-level language forms, extracted from the common lisp doc strings. The ROS wiki documents concepts and provides a tutorial.
Forms for defining plans.
— Macro: def-plan name lambda-list &rest body
Defines a plan. All functions that should appear in the task-tree must be defined with def-plan.
— Macro: def-top-level-plan name args &body body
Defines a top-level plan. Every top-level plan has its own episode-knowledge and task-tree.
— Macro: def-goal
(name &rest pattern
) &body body
Defines a new goal. Goals always have the form
where patterns can be arbitrary expressions containing variables (indicated by a ?-prefix) and symbols. Example: (achieve (loc ?obj ?location)) When defining goals with similar expressions (e.g. (loc Robot ?l) and (loc ?a ?b)) the most specific expression must be defined first. Otherwise, the previously defined expression will be overwritten.
— Function: fail &rest args
Like the common lisp function error but throws a simple-plan-error per default.
— Macro: with-failure-handling clauses &body body
Macro that replaces handler-case in cram-language. This is necessary because error handling does not work across multiple threads. When an error is signaled, it is put into an envelope to avoid invocation of the debugger multiple times. When handling errors, this envelope must also be taken into account.
We also need a mechanism to retry since errors can be caused by plan execution and the environment is highly non-deterministic. Therefore, it is possible to use the function `retry' that is lexically bound within with-failure-handling and causes a re-execution of the body.
When an error is unhandled, it is passed up to the next failure handling form (exactly like handler-bind). Errors are handled by invoking the retry function or by doing a non-local exit. Note that with-failure-handling implicitly creates an unnamed block, i.e. `return' can be used.
— Macro: top-level &body body
Creates a new task, executes body in it and waits until it is finished. All plan macros can only be used within the dynamic scope of a top-level form.
Executes forms sequentially. Fails if one fail. Succeeds if all succeed.
Executes forms in parallel. Fails if one fails. Succeeds if all succeed.
— Macro: pursue &body forms
Execute forms in parallel. Succeed if one succeeds, fail if one fails.
— Macro: try-all &body forms
Try forms in parallel. Succeed if one succeeds, fail if all fail. In the case of a failure, a condition of type 'composite-failure' is signaled, containing the list of all error messages and data.
— Macro: try-in-order &body forms
Execute forms sequentially. Succeed if one succeeds, fail if all fail. In case of failure, a composite-failure is signaled.
— Macro: with-tags &body body &environment lexenv
Execute body with all tags bound to the corresponding lexically bound variables.
— Macro: partial-order
) &body orderings
Specify ordering constraints for `steps'. `steps' are executed in an implicit
form. `orderings' is a list of orderings. An ordering always has the form:
(:order <contstraining-task> <constrained-task>)
`constraining-task' and `constrained-task' are task objects. That means, they can be either be defined in the current lexical environment (over a :tag) or by either using the function
task to reference the task by its absolute path or the function
sub-task to reference it by its path relative to the
— Macro: with-task-suspended task &body body
Execute body with 'task' being suspended.
— Function: make-fluent &rest args &key name &allow-other-keys
— Generic Function: wait-for fluent &key timeout value-changed wait-status handle-missed-pulses
Method to block the current thread until the value of `fluent' becomes non-nil. When it is already true and `value-changed' is nil,
imediately returns. Otherwise it waits for at least timeout. The parameter `wait-status' indicates the status of the task when it is waiting. The parameter `handle-missed-pulses' can be either
:never means that missed pulses are ignored,
:once means that independent of the number of missed pulses, they are handled only once, and
:always means that the internal pulse count is only incremented by one, allowing for being triggered on every missed pulse.
The handling of missed pulses is performed on a per-thread basis.
— Macro: whenever
(condition-fluent &key wait-status handle-missed-pulses
) &body body
Executes `body' whenever `condition-fluent' is pulsed or non-nil. The `body' forms are executed in an implicit block named
. The parameters `wait-status' and `handle-missed-pulses' have the same meaning as in
— Generic Function: pulse fluent
Method to trigger the fluent, i.e. notifying all waiting threads, but without actually changing the fluent value.
Fluents can be combined to fluent networks. The standard comparison operators <, >, =, eq, eql, +, -, * and / are redefined to return a fluent network whenever they are called with at least one fluent as a parameter. In addition, the following special fluent operators are defined.
— Function: fl-and &rest fl-args393
The and-operator for fluents. It is fundamentally different to the definition of common-lisp's and in that it is not implemented as a macro. That means, all args are evaluated when using fl-and.
— Function: fl-funcall &rest fl-args454
Generic fluent-operator. Applys args to function whenever a fluent in args changes.
— Function: fl-or &rest fl-args413
The or-operator for fluents. For more information on why it is a function please refere to the documentation of fl-and.
— Function: fl-pulsed &rest fl-args434
Returns true and is invoked whenever one of its argument fluents gets pulsed.
— Macro: suspend-protect form &body protection-forms
When the current task is suspended during the execution of `form', execute `protection-forms' just before suspending.
— Macro: without-suspension &body body
Execution of `body' cannot be interrupted by a suspension. If a suspension
— Macro: with-suspension &body body
Explicitly allows suspension. To be used within the dynamic context of
— Macro: on-suspension when-suspended-form &body body
Executes `when-suspended-form' whenever a suspension signal occurs while `form' is being executed.