Planners-2-PDDL-Basics

Navigation : Index | << Introduction | State Space >>

Objectifs d’apprentissage

A la fin de ce notebook, vous saurez : 1. Lire et comprendre la syntaxe PDDL (Planning Domain Definition Language) 2. Ecrire un fichier domaine PDDL avec predicats et actions 3. Ecrire un fichier problème PDDL avec objets, etat initial et but 4. Utiliser unified-planning pour manipuler des modèles PDDL en Python 5. Comprendre les schemas d’action et leur instanciation

Prerequis

  • Python 3.9+
  • Connaissances basiques en logique propositionnelle
  • Avoir suivi le notebook Planners-0-Setup

Duree estimee : 40 minutes


1. Introduction a PDDL

PDDL (Planning Domain Definition Language) est le langage standard pour decrire des problemes de planification automatique. Developpe en 1998 pour la première competition internationale de planification (IPC), il est base sur le modèle STRIPS avec des extensions.

Pourquoi PDDL ?

Avantage Description
Standardisation Format commun pour tous les planificateurs
Separation Domaine (reutilisable) vs Problème (spécifique)
Expressivite Typage, predicats, actions parametrees
Extensibilite Nombreuses extensions (temporel, numérique, etc.)

Architecture PDDL

Un problème de planification en PDDL se compose de deux fichiers :

  1. Domaine (domain.pddl) : Définit les types, predicats et actions disponibles
  2. Problème (problem.pddl) : Définit les objets, l’etat initial et le but

1.1 STRIPS : La base de PDDL

PDDL est base sur le modèle STRIPS (Stanford Research Institute Problem Solver, 1971). Dans STRIPS, une action est définie par :

Composante Description
Nom Identifiant de l’action
Paramètres Variables instanciees lors de l’exécution
Preconditions Conditions necessaires pour executer l’action
Effets Changements après l’exécution (ajouts et suppressions)

Exemple STRIPS : Monde des blocs

pickup(X)
  P: gripping() ^ clear(X) ^ ontable(X)
  A: gripping(X)
  D: ontable(X) ^ gripping()

putdown(X)
  P: gripping(X)
  A: ontable(X) ^ gripping() ^ clear(X)
  D: gripping(X)

stack(X, Y)
  P: gripping(X) ^ clear(Y)
  A: on(X,Y) ^ gripping() ^ clear(X)
  D: gripping(X) ^ clear(Y)

unstack(X, Y)
  P: gripping() ^ clear(X) ^ on(X,Y)
  A: gripping(X) ^ clear(Y)
  D: on(X,Y) ^ gripping()

Notation : P = Preconditions, A = Ajouts (Add), D = Suppressions (Delete)


2. Structure d’un fichier domaine PDDL

Le fichier domaine définit la structure du monde : types d’objets, predicats (proprietes) et actions possibles.

Syntaxe générale

(define (domain nom-du-domaine)
  (:requirements <requirements...>)
  (:types <types...>)
  (:predicates <predicates...>)
  (:action nom-action
    :parameters (<params...>)
    :precondition <precond>
    :effect <effets>)
  ...
)

2.1 En-tete et requirements

(define (domain logistics)
  (:requirements :strips :typing)
  ...
)
Requirement Description
:strips Support de base STRIPS (requis)
:typing Types d’objets hiérarchiques
:negative-preconditions Preconditions avec not
:disjunctive-preconditions Preconditions avec or
:equality Test d’egalite = entre objets
:conditional-effects Effets conditionnels when
:adl Combinaison de plusieurs requirements

2.2 Types d’objets

(:types 
  location vehicle - object
  truck airplane - vehicle
  city place - location
)

Syntaxe : sous-type1 sous-type2 - type-parent

Les types permettent de contraindre les paramètres des actions et de reduire l’espace de recherche.

2.3 Predicats

Les predicats representent les proprietes et relations du monde. Ils peuvent etre vrais ou faux dans un etat donne.

(:predicates
  (at ?obj - object ?loc - location)
  (in ?obj - object ?veh - vehicle)
  (connected ?from ?to - location)
  (empty ?veh - vehicle)
)

Notation : - ?var : Variable (commence par ?) - ?var - Type : Variable avec type - Les predicats sans paramètres sont des propositions atomiques

2.4 Actions

Les actions (ou schemas d’action) definissent les transitions possibles entre etats.

(:action load
  :parameters (?obj - object ?veh - vehicle ?loc - location)
  :precondition (and (at ?obj ?loc) (at ?veh ?loc) (empty ?veh))
  :effect (and (in ?obj ?veh) (not (at ?obj ?loc)) (not (empty ?veh)))
)

Composantes d’une action

Composante Description
:parameters Variables avec leurs types
:precondition Conditions logiques (conjonction, disjonction, negation)
:effect Changements : additions et suppressions de predicats

3. Exemple complet : Domaine Logistics

Le domaine Logistics est un classique de la planification. Un robot doit deplacer des colis entre des lieux en utilisant des vehicules (camions, avions).

# Definition du domaine PDDL - Logistics
LOGISTICS_DOMAIN = """
(define (domain logistics-simple)
  (:requirements :strips :typing)
  (:types 
    location - object
    package vehicle - object
    truck - vehicle
  )
  
  (:predicates
    ;; Position des objets et vehicules
    (at-loc ?p - package ?l - location)
    (at-veh ?v - vehicle ?l - location)
    
    ;; Chargement
    (in ?p - package ?v - vehicle)
    (empty ?v - vehicle)
  )
  
  ;; Action : Charger un colis dans un vehicule
  (:action load
    :parameters (?p - package ?v - vehicle ?l - location)
    :precondition (and 
                    (at-loc ?p ?l) 
                    (at-veh ?v ?l) 
                    (empty ?v))
    :effect (and 
              (in ?p ?v) 
              (not (at-loc ?p ?l)) 
              (not (empty ?v)))
  )
  
  ;; Action : Decharger un colis d'un vehicule
  (:action unload
    :parameters (?p - package ?v - vehicle ?l - location)
    :precondition (and 
                    (in ?p ?v) 
                    (at-veh ?v ?l))
    :effect (and 
              (at-loc ?p ?l) 
              (not (in ?p ?v)) 
              (empty ?v))
  )
  
  ;; Action : Deplacer un vehicule entre deux lieux
  (:action drive
    :parameters (?v - vehicle ?from ?to - location)
    :precondition (at-veh ?v ?from)
    :effect (and 
              (at-veh ?v ?to) 
              (not (at-veh ?v ?from)))
  )
)
"""

print("Domaine PDDL defini : logistics-simple")
print("Actions disponibles : load, unload, drive")
Domaine PDDL defini : logistics-simple
Actions disponibles : load, unload, drive

Interpretation : Domaine Logistics

Sortie obtenue : Le domaine PDDL est défini avec 3 actions et 4 predicats.

Élément Type Signification
Types location, package, vehicle, truck Hiérarchie d’objets
Predicats at-loc, at-veh, in, empty Etats du monde
Actions load, unload, drive Opérations possibles

Structure des actions : - load(p, v, l) : Charge le colis p dans le vehicule v au lieu l - Preconditions: colis present, vehicule present, vehicule vide - Effets: colis dans vehicule, plus au lieu, vehicule n’est plus vide

  • unload(p, v, l) : Decharge le colis p du vehicule v au lieu l
    • Preconditions: colis dans vehicule, vehicule au lieu
    • Effets: colis au lieu, plus dans vehicule, vehicule vide
  • drive(v, from, to) : Deplace le vehicule v de from vers to
    • Preconditions: vehicule au lieu de depart
    • Effets: vehicule au lieu d’arrivee, plus au lieu de depart

Note technique : Le domaine est reutilisable pour tous les problemes de logistique (nombre de colis, lieux, vehicules variable).


4. Structure d’un fichier problème PDDL

Le fichier problème définit une instance spécifique du domaine.

4.1 Syntaxe générale

(define (problem nom-du-problème)
  (:domain nom-du-domaine)
  (:objects <objets...>)
  (:init <etat-initial...>)
  (:goal <but...>)
)
Section Description
:domain Reference au domaine utilise
:objects Instances concretes des types
:init Predicats vrais a l’etat initial
:goal Condition a atteindre

4.2 Declaration des objets

(:objects
  ;; Lieux
  depot warehouse store - location
  
  ;; Colis
  box1 box2 box3 - package
  
  ;; Vehicules
  truck1 truck2 - truck
)

Syntaxe : obj1 obj2 obj3 - type (plusieurs objets du même type)

4.3 Etat initial

L’etat initial définit les predicats qui sont vrais au debut. Tous les autres sont supposes faux (hypothese du monde ferme).

(:init
  ;; Positions initiales
  (at-loc box1 depot)
  (at-loc box2 depot)
  (at-veh truck1 warehouse)
  (at-veh truck2 store)
  
  ;; Vehicules vides
  (empty truck1)
  (empty truck2)
)

Hypothese du monde ferme : Tout predicat non mentionne dans :init est suppose faux.

4.4 But

Le but définit l’etat a atteindre. C’est une formule logique (généralement une conjonction).

(:goal (and 
  (at-loc box1 store)
  (at-loc box2 warehouse)
))

Le planificateur doit trouver une sequence d’actions qui rend tous les predicats du but vrais.


5. Exemple complet : Problème Logistics

# Definition du probleme PDDL - Logistics
LOGISTICS_PROBLEM = """
(define (problem logistics-simple-1)
  (:domain logistics-simple)
  
  (:objects
    ;; Lieux
    depot warehouse store - location
    
    ;; Colis a livrer
    box1 box2 - package
    
    ;; Vehicules
    truck1 - truck
  )
  
  (:init
    ;; Positions initiales des colis
    (at-loc box1 depot)
    (at-loc box2 depot)
    
    ;; Position initiale du camion
    (at-veh truck1 depot)
    
    ;; Le camion est vide
    (empty truck1)
  )
  
  (:goal (and
    ;; box1 doit etre au store
    (at-loc box1 store)
    
    ;; box2 doit etre au warehouse
    (at-loc box2 warehouse)
  ))
)
"""

print("Probleme PDDL defini : logistics-simple-1")
print("\nEtat initial :")
print("  - box1, box2 au depot")
print("  - truck1 au depot (vide)")
print("\nBut :")
print("  - box1 au store")
print("  - box2 au warehouse")
Probleme PDDL defini : logistics-simple-1

Etat initial :
  - box1, box2 au depot
  - truck1 au depot (vide)

But :
  - box1 au store
  - box2 au warehouse

Interpretation : Problème Logistics

Sortie obtenue : Le problème est instancie avec 6 objets (3 lieux, 2 colis, 1 camion).

Élément Valeur Signification
Objets depot, warehouse, store, box1, box2, truck1 Instances concrètes
Etat initial box1,box2@depot, truck1@depot(vide) Tout au depot
But box1@store, box2@warehouse Livraison spécifique

Complexite du problème : - Espace d’etats : Chaque colis peut etre a 3 lieux ou dans 1 camion = 4^2 = 16 etats de colis - Position camion : 3 lieux possibles - Total : ~48 etats (sans compter l’etat vide/plein du camion) - Solution optimale : 7 actions (load, drive, unload, drive, load, drive, unload)

Contraintes : - Le camion ne peut porter qu’un colis a la fois (predicat empty) - Il faut revenir au depot pour charger le deuxieme colis

Note technique : C’est un problème de “transport” classique. La difficulte vient de la coordination entre les deplacements du vehicule et les chargements/dechargements des colis.

Visualisation du problème

Objet Type Etat initial But
box1 package depot store
box2 package depot warehouse
truck1 truck depot (vide) -

Une solution possible : 1. load(box1, truck1, depot) 2. drive(truck1, depot, store) 3. unload(box1, truck1, store) 4. drive(truck1, store, depot) 5. load(box2, truck1, depot) 6. drive(truck1, depot, warehouse) 7. unload(box2, truck1, warehouse)


6. PDDL avec unified-planning

La bibliotheque unified-planning permet de manipuler des modèles PDDL directement en Python, sans ecrire de fichiers PDDL manuellement.

# Verification de unified-planning
try:
    import unified_planning as up
    from unified_planning.shortcuts import *
    print(f"unified-planning version: {up.__version__}")
    UP_AVAILABLE = True
except ImportError:
    print("ERREUR: unified-planning non installe")
    print("Installez avec: pip install unified-planning")
    UP_AVAILABLE = False
unified-planning version: 1.3.0

Interpretation : Verification unified-planning

Sortie obtenue : unified-planning version 1.3.0 est correctement installe.

Composant Version Signification
unified-planning 1.3.0 Version stable et recente

Points cles : 1. Cette version supporte tous les planificateurs modernes 2. L’API raccourcie (from unified_planning.shortcuts import *) simplifie la modelisation 3. Les types, fluents et actions peuvent etre définis programmatiquement

Note technique : unified-planning est le fruit d’un effort europeen (AI Plan4EU) pour standardiser l’interface de planification en Python.

6.1 Definition des types et predicats

if UP_AVAILABLE:
    from collections import OrderedDict
    
    # Definition des types
    Location = UserType('Location')
    Package = UserType('Package')
    Vehicle = UserType('Vehicle')
    Truck = UserType('Truck', father=Vehicle)  # Truck herite de Vehicle
    
    # Definition des predicats (fluents)
    # Note: L'API unified-planning 1.3+ utilise OrderedDict pour la signature
    at_loc = Fluent('at_loc', BoolType(), OrderedDict([('package', Package), ('location', Location)]))
    at_veh = Fluent('at_veh', BoolType(), OrderedDict([('vehicle', Vehicle), ('location', Location)]))
    in_pkg = Fluent('in', BoolType(), OrderedDict([('package', Package), ('vehicle', Vehicle)]))
    empty = Fluent('empty', BoolType(), OrderedDict([('vehicle', Vehicle)]))
    
    print("Types definis : Location, Package, Vehicle, Truck")
    print("Predicats definis : at_loc, at_veh, in, empty")
Types definis : Location, Package, Vehicle, Truck
Predicats definis : at_loc, at_veh, in, empty

Interpretation : Definition des types et predicats

Sortie obtenue : La hiérarchie de types et les predicats sont correctement définis.

Concept Python Concept PDDL Exemple
UserType('Location') Type location Lieu
UserType('Truck', father=Vehicle) Sous-type truck - vehicle
Fluent('at_loc', ...) Predicat (at-loc ?p - package ?l - location)
BoolType() Valeur booleenne Vrai/Faux

Points cles : 1. Les types definissent la structure des objets du monde 2. Les fluents (predicats) representent les etats changeants 3. L’heritage Truck -> Vehicle permet de reutiliser les actions vehicule pour les camions 4. OrderedDict assure l’ordre des paramètres (important pour unified-planning 1.3+)

Note technique : Contrairement a PDDL texte ou les paramètres sont nommes par convention (?p - package), unified-planning requiert des noms explicites dans le dictionnaire de signature.

6.2 Definition des actions

if UP_AVAILABLE:
    from collections import OrderedDict
    
    # Action : load
    load = InstantaneousAction('load', OrderedDict([('package', Package), ('vehicle', Vehicle), ('location', Location)]))
    # Recuperer les parametres de l'action
    p_load = load.parameter('package')
    v_load = load.parameter('vehicle')
    l_load = load.parameter('location')
    load.add_precondition(at_loc(p_load, l_load))
    load.add_precondition(at_veh(v_load, l_load))
    load.add_precondition(empty(v_load))
    load.add_effect(at_loc(p_load, l_load), False)  # suppression
    load.add_effect(in_pkg(p_load, v_load), True)   # ajout
    load.add_effect(empty(v_load), False)           # suppression
    
    # Action : unload
    unload = InstantaneousAction('unload', OrderedDict([('package', Package), ('vehicle', Vehicle), ('location', Location)]))
    p_unload = unload.parameter('package')
    v_unload = unload.parameter('vehicle')
    l_unload = unload.parameter('location')
    unload.add_precondition(in_pkg(p_unload, v_unload))
    unload.add_precondition(at_veh(v_unload, l_unload))
    unload.add_effect(at_loc(p_unload, l_unload), True)  # ajout
    unload.add_effect(in_pkg(p_unload, v_unload), False) # suppression
    unload.add_effect(empty(v_unload), True)             # ajout
    
    # Action : drive
    drive = InstantaneousAction('drive', OrderedDict([('vehicle', Vehicle), ('from', Location), ('to', Location)]))
    v_drive = drive.parameter('vehicle')
    from_drive = drive.parameter('from')
    to_drive = drive.parameter('to')
    drive.add_precondition(at_veh(v_drive, from_drive))
    drive.add_effect(at_veh(v_drive, from_drive), False)  # suppression
    drive.add_effect(at_veh(v_drive, to_drive), True)     # ajout
    
    print("Actions definies :")
    print(f"  - load: {load.name}({', '.join(str(p) for p in load.parameters)})")
    print(f"  - unload: {unload.name}({', '.join(str(p) for p in unload.parameters)})")
    print(f"  - drive: {drive.name}({', '.join(str(p) for p in drive.parameters)})")
Actions definies :
  - load: load(Package package, Vehicle vehicle, Location location)
  - unload: unload(Package package, Vehicle vehicle, Location location)
  - drive: drive(Vehicle vehicle, Location from, Location to)

Interpretation : Definition des actions

Sortie obtenue : Trois actions sont définies avec leurs paramètres, preconditions et effets.

Action Paramètres Preconditions Effets
load package, vehicle, location package@location, vehicle@location, vehicle vide package in vehicle, plus @location, vehicle plus vide
unload package, vehicle, location package in vehicle, vehicle@location package@location, plus in vehicle, vehicle vide
drive vehicle, from, to vehicle@from vehicle@to, plus @from

Correspondance Python/PDDL : - InstantaneousAction('load', params) → (:action load :parameters ...) - add_precondition(fluent(...)) → :precondition (and (...)) - add_effect(fluent(...), True/False) → :effect (and (...) (not (...)))

Points cles : 1. Les paramètres sont recuperes via action.parameter('name') 2. add_effect(fluent, False) ajoute une negation (not fluent) 3. Les preconditions sont implicitement en ET (conjonction)

Note technique : La méthode parameter() retourne une Expression qui peut etre utilisee directement dans les fluents. C’est plus elegant que de manipuler des chaînes de caractères.

6.3 Creation du problème complet

if UP_AVAILABLE:
    # Creation du probleme
    problem = Problem('logistics-example')
    
    # Ajout des objets
    depot = Object('depot', Location)
    warehouse = Object('warehouse', Location)
    store = Object('store', Location)
    box1 = Object('box1', Package)
    box2 = Object('box2', Package)
    truck1 = Object('truck1', Truck)
    
    problem.add_objects([depot, warehouse, store, box1, box2, truck1])
    
    # Etat initial
    problem.set_initial_value(at_loc(box1, depot), True)
    problem.set_initial_value(at_loc(box2, depot), True)
    problem.set_initial_value(at_veh(truck1, depot), True)
    problem.set_initial_value(empty(truck1), True)
    
    # But
    problem.add_goal(at_loc(box1, store))
    problem.add_goal(at_loc(box2, warehouse))
    
    # Ajout des actions au probleme
    problem.add_action(load)
    problem.add_action(unload)
    problem.add_action(drive)
    
    print("Probleme cree avec unified-planning")
    print(f"Objets: {[o.name for o in problem.all_objects]}")
    print(f"Actions: {[a.name for a in problem.actions]}")
    print(f"Buts: {[str(g) for g in problem.goals]}")
Probleme cree avec unified-planning
Objets: ['depot', 'warehouse', 'store', 'box1', 'box2', 'truck1']
Actions: ['load', 'unload', 'drive']
Buts: ['at_loc(box1, store)', 'at_loc(box2, warehouse)']

Interpretation : Problème complet

Sortie obtenue : Le problème est complet avec 6 objets, 3 actions et 2 buts.

Élément Valeur Signification
Objets depot, warehouse, store, box1, box2, truck1 Instances du monde
Actions load, unload, drive Opérations disponibles
Buts at_loc(box1, store), at_loc(box2, warehouse) Objectifs

Étapes de creation : 1. Creation du problème : Problem('logistics-example') 2. Ajout des objets : Instanciation des types 3. Etat initial : Valeurs initiales des fluents (True/False) 4. Buts : Conditions a atteindre (conjonction de fluents) 5. Actions : Ajout des schemas d’action définis plus haut

Verification : - Tous les objets sont correctement types - L’etat initial est coherent (un colis ne peut etre qu’a un seul endroit) - Les buts sont realisables (pas de contradiction)

Note technique : Le problème peut maintenant etre passe a n’importe quel planificateur supporte par unified-planning (pyperplan, Fast Downward, ENHSP, etc.).

6.4 Resolution du problème

if UP_AVAILABLE:
    from unified_planning.engines import PlanGenerationResultStatus
    from unified_planning.shortcuts import get_environment
    get_environment().credits_stream = None
    
    try:
        # Utiliser un planificateur disponible
        with OneshotPlanner(name='pyperplan') as planner:
            print(f"Planificateur: {planner.name}")
            print("Resolution en cours...\n")
            
            result = planner.solve(problem)
            
            if result.status == PlanGenerationResultStatus.SOLVED_SATISFICING:
                print("Solution trouvee !")
                print("=" * 50)
                for i, action in enumerate(result.plan.actions):
                    params = ', '.join(str(p.object()) for p in action.actual_parameters)
                    print(f"  {i+1}. {action.action.name}({params})")
                print("=" * 50)
                print(f"Longueur du plan: {len(result.plan.actions)} actions")
            else:
                print(f"Statut: {result.status}")
    except Exception as e:
        print(f"Erreur: {e}")
        print("\nSolution attendue (manuelle):")
        print("  1. load(box1, truck1, depot)")
        print("  2. drive(truck1, depot, store)")
        print("  3. unload(box1, truck1, store)")
        print("  4. drive(truck1, store, depot)")
        print("  5. load(box2, truck1, depot)")
        print("  6. drive(truck1, depot, warehouse)")
        print("  7. unload(box2, truck1, warehouse)")
Planificateur: Pyperplan
Resolution en cours...

Solution trouvee !
==================================================
  1. load(box1, truck1, depot)
  2. drive(truck1, depot, store)
  3. unload(box1, truck1, store)
  4. drive(truck1, store, depot)
  5. load(box2, truck1, depot)
  6. drive(truck1, depot, warehouse)
  7. unload(box2, truck1, warehouse)
==================================================
Longueur du plan: 7 actions

Interpretation de la solution

Le planificateur trouve une sequence d’actions qui transforme l’etat initial en etat but :

Étape Action Etat après
0 (initial) box1,box2@depot, truck1@depot
1 load(box1, truck1, depot) box1 in truck1
2 drive(truck1, depot, store) truck1@store
3 unload(box1, truck1, store) box1@store (but 1 OK)
4 drive(truck1, store, depot) truck1@depot
5 load(box2, truck1, depot) box2 in truck1
6 drive(truck1, depot, warehouse) truck1@warehouse
7 unload(box2, truck1, warehouse) box2@warehouse (but 2 OK)

Note : Ce plan de 7 actions est optimal en longueur. Le camion de capacite 1 (predicat empty) ne transporte qu’un colis a la fois : chaque colis impose donc un aller-retour (load -> drive -> unload, puis drive de retour pour aller chercher le suivant), soit 7 actions au minimum – aucun plan plus court n’existe. pyperplan renvoie l’un des plans optimaux equivalents (l’ordre de traitement de box1/box2 peut varier d’une exécution a l’autre).


7. Extensions PDDL avances

PDDL dispose de nombreuses extensions au-delà de STRIPS de base.

7.1 Preconditions negatives

Requirement :negative-preconditions

(:action enter
  :parameters (?robot - robot ?room - location)
  :precondition (and 
                  (at ?robot ?room)
                  (not (locked ?room)))  ;; negation
  :effect (inside ?robot ?room)
)

7.2 Effets conditionnels

Requirement :conditional-effects

(:action move
  :parameters (?v - vehicle ?from ?to - location)
  :precondition (at-veh ?v ?from)
  :effect (and 
            (at-veh ?v ?to) 
            (not (at-veh ?v ?from))
            ;; Effet conditionnel : si le vehicule a des passagers,
            ;; leur position change aussi
            (when (has-passengers ?v) 
                  (forall (?p - passenger) 
                    (when (in ?p ?v) 
                          (and (at ?p ?to) (not (at ?p ?from))))))
          )
)

7.3 Quantificateurs universels

Requirement :universal-preconditions et :conditional-effects

;; Tous les colis doivent etre livres
(:goal (forall (?p - package) 
          (exists (?l - location) 
            (and (destination ?p ?l) (at-loc ?p ?l)))))

7.4 Resume des requirements

Requirement Description Exemple d’usage
:strips Base STRIPS Toujours requis
:typing Types d’objets (:types truck car - vehicle)
:negative-preconditions not dans preconditions (not (locked ?r))
:disjunctive-preconditions or dans preconditions (or (at ?x A) (at ?x B))
:equality Test = (not (= ?x ?y))
:conditional-effects when dans effets (when (cond) (effect))
:adl ADL complet Combine plusieurs requirements

8. Exemple : Domaine du Gripper

Le Gripper est un problème classique : un robot avec deux pinces doit deplacer des balles entre deux pieces.

# Domaine PDDL du Gripper (classique)
GRIPPER_DOMAIN = """
(define (domain gripper-strips)
  (:requirements :strips :typing)
  (:types room ball gripper)
  
  (:predicates
    (room ?r - room)
    (ball ?b - ball)
    (gripper ?g - gripper)
    (at-robby ?r - room)        ;; position du robot
    (at ?b - ball ?r - room)    ;; position d'une balle
    (free ?g - gripper)         ;; pince libre
    (carry ?b - ball ?g - gripper)  ;; balle tenue
  )
  
  ;; Action : deplacer le robot entre deux pieces
  (:action move
    :parameters (?from ?to - room)
    :precondition (and (room ?from) (room ?to) (at-robby ?from))
    :effect (and (at-robby ?to) (not (at-robby ?from)))
  )
  
  ;; Action : prendre une balle avec une pince
  (:action pick
    :parameters (?obj - ball ?room - room ?gripper - gripper)
    :precondition (and 
                    (ball ?obj) 
                    (room ?room) 
                    (gripper ?gripper) 
                    (at ?obj ?room) 
                    (at-robby ?room) 
                    (free ?gripper))
    :effect (and 
              (carry ?obj ?gripper) 
              (not (at ?obj ?room)) 
              (not (free ?gripper)))
  )
  
  ;; Action : poser une balle
  (:action drop
    :parameters (?obj - ball ?room - room ?gripper - gripper)
    :precondition (and 
                    (ball ?obj) 
                    (room ?room) 
                    (gripper ?gripper) 
                    (at-robby ?room) 
                    (carry ?obj ?gripper))
    :effect (and 
              (at ?obj ?room) 
              (free ?gripper) 
              (not (carry ?obj ?gripper)))
  )
)
"""

print("Domaine Gripper defini")
print("Actions: move, pick, drop")
Domaine Gripper defini
Actions: move, pick, drop

Interpretation : Domaine Gripper

Sortie obtenue : Le domaine Gripper est défini avec 3 actions et 7 predicats (3 de typage + 4 relationnels).

Élément Description
Types room (lieu), ball (balle), gripper (pince)
Predicats de typage room, ball, gripper (declarations des sortes d’objets)
Predicats relationnels at-robby (robot), at (balle), free (pince libre), carry (balle tenue)
Actions move (deplacer), pick (prendre), drop (poser)

Specificites du domaine : - Le robot a deux pinces (left, right) independantes - Les balles peuvent etre transportees simultanement - Le robot peut se deplacer entre les pieces

Actions detaillees : - move(from, to) : Deplace le robot d’une piece a l’autre - pick(ball, room, gripper) : Prend une balle avec une pince (doit etre dans la même piece) - drop(ball, room, gripper) : Pose une balle dans une piece

Note technique : Ce domaine illustre l’importance de bien modeliser les ressources (les 2 pinces). Une mauvaise modelisation serait d’avoir un seul predicat free sans preciser quelle pince.

Definition du problème PDDL correspondant au domaine Gripper, instanciant les objets concrets, l’etat initial et le but a atteindre.

# Probleme PDDL du Gripper
GRIPPER_PROBLEM = """
(define (problem strips-gripper2)
  (:domain gripper-strips)
  (:objects 
    rooma roomb - room
    ball1 ball2 - ball
    left right - gripper
  )
  (:init 
    ;; Definition des objets (optionnel dans certains planificateurs)
    (room rooma) (room roomb)
    (ball ball1) (ball ball2)
    (gripper left) (gripper right)
    
    ;; Position initiale du robot
    (at-robby rooma)
    
    ;; Pinces libres
    (free left) (free right)
    
    ;; Positions initiales des balles
    (at ball1 rooma)
    (at ball2 rooma)
  )
  (:goal (and 
    (at ball1 roomb)
    (at ball2 roomb)
  ))
)
"""

print("Probleme Gripper defini")
print("\nEtat initial:")
print("  - Robot dans rooma")
print("  - ball1, ball2 dans rooma")
print("  - Pinces left, right libres")
print("\nBut:")
print("  - ball1 et ball2 dans roomb")
Probleme Gripper defini

Etat initial:
  - Robot dans rooma
  - ball1, ball2 dans rooma
  - Pinces left, right libres

But:
  - ball1 et ball2 dans roomb

Interpretation : Problème Gripper

Sortie obtenue : Le problème est instancie avec 6 objets et 11 predicats initiaux.

Élément Valeur Signification
Objets 2 rooms, 2 balls, 2 grippers Instances du monde
Etat initial Robot + balles dans rooma, pinces libres Tout au depart
But Les 2 balles dans roomb Transport complet

Analyse de l’etat initial : - at-robby rooma : Robot dans la piece rooma - at ball1 rooma, at ball2 rooma : Balles dans rooma - free left, free right : Les deux pinces sont libres

Stratégie optimale : 1. Prendre ball1 avec la pince gauche 2. Prendre ball2 avec la pince droite 3. Se deplacer vers roomb 4. Poser ball1 5. Poser ball2

Observation : Le robot peut porter les 2 balles en même temps ! Un planificateur qui n’utilise pas cette capacite ferait 7 actions au lieu de 5 (prendre, deplacer, poser, revenir, reprendre, deplacer, poser).

Analyse du problème Gripper

Élément Valeur
Objets 2 rooms, 2 balls, 2 grippers
Etat initial Robot + balles dans rooma
But Balles dans roomb
Actions possibles move, pick (x2), drop (x2)

Une solution optimale : 1. pick(ball1, rooma, left) 2. pick(ball2, rooma, right) 3. move(rooma, roomb) 4. drop(ball1, roomb, left) 5. drop(ball2, roomb, right)

Observation : Le robot peut porter deux balles simultanement car il a deux pinces !


9. Exercices

Exercice 9.1 - Etendre le problème Gripper

Le problème Gripper de la section 8 transporte 2 balles entre 2 pieces. A vous de jouer : ecrivez le problème PDDL (le domaine gripper-strips reste inchange) pour la variante suivante :

  • 3 pieces : rooma, roomb, roomc
  • 3 balles : ball1, ball2, ball3, toutes initialement dans rooma
  • Robot : demarre dans rooma, ses deux pinces left et right sont libres
  • But : ball1 et ball2 dans roomc, ball3 dans roomb

Completez le squelette de la cellule suivante en reutilisant la structure de GRIPPER_PROBLEM (sections :objects, :init, :goal).

Indice : le robot ne possede que 2 pinces mais doit livrer dans 2 pieces différentes. Une partie du transport necessitera donc un aller-retour. Reportez-vous a l’encadre methodologique ci-dessous pour la demarche.

# Exercice 9.1 : Probleme Gripper a 3 pieces / 3 balles
# Le domaine gripper-strips (defini plus haut) reste INCHANGE : seul le
# probleme change. Completez la chaine PDDL ci-dessous en vous inspirant
# de GRIPPER_PROBLEM (section 8) et de l'enonce.

GRIPPER_PROBLEM_3 = """
(define (problem strips-gripper3)
  (:domain gripper-strips)
  (:objects
    ;; TODO etudiant : declarez les pieces, les balles et les pinces
  )
  (:init
    ;; TODO etudiant : position du robot, etat des pinces, position des balles
  )
  (:goal (and
    ;; TODO etudiant : l'etat but a atteindre
  ))
)
"""

# Une fois GRIPPER_PROBLEM_3 complete, vous pourrez le resoudre avec un
# planificateur (unified-planning + OneshotPlanner) comme en section 6.4.
# Question subsidiaire : avec seulement 2 pinces, combien d'actions au
# minimum pour ce but reparti sur 2 pieces destination ?

print("Exercice a completer")
Exercice a completer

Indice : Pour aborder les exercices

Conseils pour reussir :

  1. Commencez par identifier les types : Quels sont les objets du problème ? (ex: bloc, lieu, robot)
  2. Listez les predicats : Quelles proprietes sont importantes ? (ex: position, etat)
  3. Definissez les actions : Quelles opérations sont possibles ? (preconditions + effets)
  4. Instanciez le problème : Quels objets concrets ? Quel etat initial ? Quel but ?

Methodologie : - Dessinez l’etat initial et le but sur papier - Identifiez les actions necessaires pour passer de l’un a l’autre - Verifiez que chaque action a des preconditions realisables - Testez avec un planificateur (pyperplan est rapide)

Erreurs courantes a eviter : - Oublier de declarer un type dans :types - Confondre preconditions et effets - Oublier les effets negatifs (not ...) - Créer des actions inutiles ou redondantes


Exemple guide : Domaine Robot-Entrepot

Avant de passer a l’Exercice 9.2, voici un exemple complet de definition d’un domaine PDDL simplifie. Le contexte est proche : un robot dans un entrepot peut saisir un objet et le deposer sur une etagere.

Ce que nous allons modeliser : - 2 types : objet et etagere - 3 predicats : position de l’objet, robot libre, objet sur une etagere - 2 actions : saisir et deposer

Observez comment chaque action declare ses preconditions (conditions necessaires) et ses effets (ajouts et suppressions de predicats).

# Exemple guide : Domaine PDDL Robot-Entrepot (solution complete)
ROBOT_ENTREPOT_DOMAIN = """
(define (domain robot-entrepot)
  (:requirements :strips :typing)
  (:types objet etagere - object)

  (:predicates
    ;; L'objet est dans une etagere
    (sur-etagere ?o - objet ?e - etagere)
    ;; Le robot tient un objet
    (tient ?o - objet)
    ;; Le robot a les mains libres
    (robot-libre)
  )

  ;; Action : saisir un objet depuis une etagere
  (:action saisir
    :parameters (?o - objet ?e - etagere)
    :precondition (and
                    (sur-etagere ?o ?e)
                    (robot-libre))
    :effect (and
              (tient ?o)
              (not (sur-etagere ?o ?e))
              (not (robot-libre)))
  )

  ;; Action : deposer un objet sur une etagere
  (:action deposer
    :parameters (?o - objet ?e - etagere)
    :precondition (and
                    (tient ?o))
    :effect (and
              (sur-etagere ?o ?e)
              (robot-libre)
              (not (tient ?o)))
  )
)
"""

# Verification structurelle : compter les composantes PDDL
actions = ROBOT_ENTREPOT_DOMAIN.count("(:action")
predicates = ROBOT_ENTREPOT_DOMAIN.count("(") - ROBOT_ENTREPOT_DOMAIN.count("(not")  # approximation
print("Domaine Robot-Entrepot defini : robot-entrepot")
print(f"Actions definies : {actions} (saisir, deposer)")
print()
print("Action 'saisir(objet, etagere)' :")
print("  Preconditions : objet sur etagere + robot libre")
print("  Effets        : robot tient objet + objet retire de l'etagere + robot plus libre")
print()
print("Action 'deposer(objet, etagere)' :")
print("  Preconditions : robot tient objet")
print("  Effets        : objet sur etagere + robot libre + robot ne tient plus l'objet")
print()
print("> Chaque effet negatif (not ...) est indispensable pour que le planificateur")
print("  comprenne que l'etat du monde change apres l'action.")
Domaine Robot-Entrepot defini : robot-entrepot
Actions definies : 2 (saisir, deposer)

Action 'saisir(objet, etagere)' :
  Preconditions : objet sur etagere + robot libre
  Effets        : robot tient objet + objet retire de l'etagere + robot plus libre

Action 'deposer(objet, etagere)' :
  Preconditions : robot tient objet
  Effets        : objet sur etagere + robot libre + robot ne tient plus l'objet

> Chaque effet negatif (not ...) est indispensable pour que le planificateur
  comprenne que l'etat du monde change apres l'action.

Exercice 9.2 - Domaine PDDL Robot-Cuisine

Ecrivez un domaine PDDL pour un robot dans une cuisine.

Contexte : Un robot peut prendre un ingredient et le poser dans un plat.

Objectif : Completer la chaîne PDDL ci-dessous avec 2 actions et leurs preconditions/effets.

Indices : - # Étape 1 : Définir les types ingredient, location, plat - # Étape 2 : Action prendre : preconditions = ingredient a la location, robot libre - # Étape 3 : Action poser : preconditions = robot tient ingredient ; effets = ingredient dans le plat - # Étape 4 : Verifier la syntaxe PDDL (parentheses, types, predicats)

# Exercice 9.2 : Domaine PDDL Robot-Cuisine
ROBOT_CUISINE_DOMAIN = """(define (domain robot-cuisine)
  (:requirements :strips :typing)
  (:types ingredient location plat - object)
  (:predicates
    (at ?i - ingredient ?l - location)
    (in-fridge ?i - ingredient)
    (holding ?i - ingredient)
    (in-plate ?i - ingredient ?p - plat)
    (robot-free)
  )
  ;; TODO etudiant: ajoutez les 2 actions (prendre et poser)
)
"""

print(f"Domaine Robot-Cuisine:\n{ROBOT_CUISINE_DOMAIN}")
print("Exercice a completer")
Domaine Robot-Cuisine:
(define (domain robot-cuisine)
  (:requirements :strips :typing)
  (:types ingredient location plat - object)
  (:predicates
    (at ?i - ingredient ?l - location)
    (in-fridge ?i - ingredient)
    (holding ?i - ingredient)
    (in-plate ?i - ingredient ?p - plat)
    (robot-free)
  )
  ;; TODO etudiant: ajoutez les 2 actions (prendre et poser)
)

Exercice a completer

Exercice 9.3 - Reconstruire le domaine Gripper avec unified_planning

Au lieu d’ecrire du PDDL textuel, reconstruisez le domaine Gripper avec l’API Python unified_planning.

Objectif : Définir les types, predicats, actions et un problème, puis resoudre.

Indices : - # Étape 1 : Types = room, ball, gripper - # Étape 2 : Predicats = at_room(ball, room), free(gripper), carry(ball, gripper) - # Étape 3 : Actions = pick, drop, move - # Étape 4 : Problème = 2 balles en roomA, but = 2 balles en roomB

if UP_AVAILABLE:
    from unified_planning.shortcuts import *
    from unified_planning.engines import PlanGenerationResultStatus

    # TODO etudiant: definissez le domaine Gripper avec l'API unified_planning
    gripper_domain = None  # TODO etudiant

    print(f"Domaine Gripper UP: {gripper_domain}")
    print("Exercice a completer")
Domaine Gripper UP: None
Exercice a completer

10. Resume

Points cles

Concept Description
PDDL Langage standard pour la planification automatique
Domaine Définit types, predicats et actions (reutilisable)
Problème Définit objets, etat initial et but (spécifique)
Predicats Proprietes/relations binaires (vrai/faux)
Actions Transitions avec preconditions et effets
STRIPS Modèle de base (add/delete lists)

Structure PDDL

domain.pddl          problem.pddl
    |                     |
    v                     v
(:types ...)        (:objects ...)
(:predicates ...)   (:init ...)
(:action ...)       (:goal ...)

Prochaines étapes

Dans le notebook suivant Planners-3-State-Space, nous explorerons : - La representation des espaces d’etats - La recherche dans un graphe d’etats - Les algorithmes de recherche (BFS, DFS, A*)


11. Conclusion

Ce notebook a couvre les fondamentaux de PDDL, le langage standard de la planification automatique.

Resume des acquis

Competence Statut Pratique
Lire un domaine PDDL OK Exemples Logistics, Gripper
Ecrire un domaine PDDL OK Exercices
Lire un problème PDDL OK Exemples Logistics, Gripper
Ecrire un problème PDDL OK Exercices
Modeliser avec unified-planning OK Exemple Logistics complet

Concepts fondamentaux

Concept Description Exemple
Domaine Types, predicats, actions (reutilisable) logistics-simple
Problème Objets, etat initial, but (spécifique) logistics-simple-1
Predicat Propriete binaire (vrai/faux) (at ?obj ?loc)
Action Transition avec preconditions et effets load, unload, drive
STRIPS Modèle de base (add/delete lists) Preconditions + effets

Prochaines étapes

  1. Planners-3-State-Space : Comprendre comment les planificateurs explorent l’espace d’etats
  2. Planners-4-Fast-Downward : Utiliser un planificateur industriel
  3. Planners-5-Heuristics : Decouvrir les heuristiques pour accelerer la recherche

Point cle : PDDL separe le quoi (domaine, actions générales) du comment (problème, instance spécifique). Cette separation permet de reutiliser un domaine pour plusieurs problemes.



Ressources


Notebook suivant : Planners-3-State-Space

Retour au sommet