Skip to content

mgonzs13/omni_plan

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

208 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OmniPlan


OmniPlan is a ROS 2 framework for automated task planning and execution. It integrates multiple classical planners with flexible execution mechanisms including direct action implementation, state machines and behavior trees. The framework supports both knowledge base and knowledge graph approaches for state management, enabling planning solutions for robotic applications. Finally, OmniPlan can be extended through the creation of new plugins to integrate new planners and new knowledge sources.


License: GPL-v3.0 GitHub release Code Size Last Commit

GitHub issues GitHub pull requests Contributors

Python Formatter Check C++ Formatter Check

ROS 2 Distro Build and Test
Foxy Foxy Build
Galatic Galactic Build
Humble Humble Build and Test
Iron Iron Build
Jazzy Jazzy Build
Kilted Kilted Build
Rolling Rolling Build

Table of Contents

Key Features

  • Plugin Architecture: Extensible design using ROS 2 pluginlib for omni customization.
  • Multiple PDDL Planners: Support for POPF, SMTP and VHPOP planners and VAL plan validator.
  • Flexible Execution: Execute plans using direct actions, YASMIN state machines or Behavior Trees.
  • Knowledge Management: Choose between knowledge base or knowledge graph approaches or integrate your own implementation.
  • ROS 2 Native: Built on ROS 2 with proper message interfaces.

Installation

# Clone this repo
cd ~/ros2_ws/src
git clone https://github.com/mgonzs13/omni_plan
# Install dependecies
cd ~/ros2_ws
vcs import src < src/omni_plan/dependencies.repos
rosdep install --from-paths src --ignore-src -r -y
# SMTPlan+ dependency
sudo apt install libz3-dev -y
# Colin and OPTIC dependecy
sudo apt install coinor-libcbc3 -y
# Optional: You may need to create symbolic link
sudo ln -s /usr/lib/x86_64-linux-gnu/libCbc.so.3.10.11 /usr/lib/x86_64-linux-gnu/libCbc.so.3
# Build the workspace
colcon build --symlink-install

To run the tests:

colcon test --executor sequential --packages-select omni_plan omni_plan_knowledge_base omni_plan_knowledge_graph omni_plan_popf omni_plan_vhpop omni_plan_smtp omni_plan_optic omni_plan_lpg omni_plan_colin omni_plan_mrta omni_plan_val omni_plan_dispatcher omni_plan_yasmin omni_plan_bt omni_plan_tests
colcon test-result --verbose

Demos

easy_plan_2_1.mp4

The framework includes several demo packages showcasing different planning and execution approaches:

Knowledge Graph with POPF Planner

ros2 launch omni_plan_demos popf_kg_demo.launch.py

Knowledge Base with POPF Planner

ros2 launch omni_plan_demos popf_kb_demo.launch.py

Other Demos

  • smtp_kb_demo.launch.py / smtp_kg_demo.launch.py: SMTP planner demos
  • vhpop_kb_demo.launch.py / vhpop_kg_demo.launch.py: VHPOP planner demos

Adding Knowledge

ros2 run omni_plan_demos knowledge_graph_demo

Parallel Assembly Demo

Demonstrates concurrent action execution using the knowledge graph. Three robots pick up parts in parallel, then a convergence step assembles them once all predecessors have completed.

ros2 launch omni_plan_demos popf_assembly_demo.launch.py

The PDDL domain models pick-up (per-robot) and assemble (requires all parts) as durative actions with OVER_ALL battery conditions so the planner schedules them as a true parallel graph.

ros2 run omni_plan_demos assembly_demo

OmniPlan TUI Monitor

The omni_plan_tui package provides a terminal-based monitoring interface for active plan execution. It subscribes to three topics and renders a live, colour-coded view in the terminal using ncurses.

Launch

ros2 run omni_plan_tui omni_plan_tui_node

Tabs

Tab Key Description
Plan Execution 1 Level-grouped graph view of all actions with real-time status icons and elapsed times
FSM State 2 Current YASMIN state machine state and full hierarchy
Action Catalog 3 Complete list of loaded plugin actions with parameters, conditions and effects

Key Bindings

Key Action
q / Q Quit
1 / 2 / 3 Jump to Plan / FSM / Actions tab
Tab / ] Next tab
[ Previous tab
/ Scroll one row
PgUp / PgDn Scroll ten rows
Home / End Jump to top / bottom
Mouse click (tab bar) Switch to clicked tab

Topics Subscribed

Topic Message type QoS
/omni_plan/actions_info omni_plan_msgs/ActionInfoArray Transient-local (latched)
/omni_plan/plan_execution omni_plan_msgs/PlanExecutionStatus Default
/fsm_viewer yasmin_msgs/StateMachine Default

API Development

OmniPlan uses a plugin-based architecture that allows developers to extend the framework by creating new planners, plan validators, and actions. All plugins are loaded using ROS2's pluginlib system.

Creating New PDDL Managers

PDDL managers handle domain and problem generation from action definitions and manage the current world state. They support different state representation approaches (e.g., knowledge base vs. knowledge graph). Inherit from omni_plan::PddlManager:

#include "omni_plan/pddl_manager.hpp"

class MyPddlManager : public omni_plan::PddlManager {
public:
  MyPddlManager() : PddlManager() {}

protected:
  // Generate PDDL domain and problem from current state
  std::pair<omni_plan::pddl::Domain, omni_plan::pddl::Problem>
  get_pddl() const override {
    // Implement your state representation logic here
    // Return a pair of Domain and Problem objects
  }

  // Check if there are any goals to achieve
  bool has_goals() const override {
    // Query your state representation for pending goals
  }

  // Clear all current goals
  bool clear_goals() const override {
    // Clear goals from your state representation
  }

  // Check if a predicate exists in the current state
  bool predicate_exists(const omni_plan::pddl::Predicate &predicate) const override {
    // Query your state representation for the predicate
  }

  // Check if a predicate is part of the goal conditions
  bool predicate_is_goal(const omni_plan::pddl::Predicate &predicate) const override {
    // Check if the predicate is in the goals
  }

  // Apply a single effect to the current state
  void apply_effect(const omni_plan::pddl::Effect &exp) override {
    // Update your state representation with the effect
    // May add or delete predicates depending on the effect type
  }
};

Creating New Planners

To create a new planner, inherit from the omni_plan::Planner base class and implement the required virtual methods:

#include "omni_plan/planner.hpp"

class MyPlanner : public omni_plan::Planner {
public:
  MyPlanner() : Planner() {}

protected:
  // Generate plan from PDDL files
  std::string generate_plan(const std::string domain_path,
                           const std::string problem_path) const override {
    // Implement your planning algorithm here
    // Return the plan as a string in PDDL format
  }

  // Check if the plan output indicates a valid solution
  bool has_solution(const std::string &plan_str) const override {
    // Analyze the planner output to determine if a solution was found
  }

  // Optional: Parse action lines from the plan output
  std::pair<std::string, std::vector<std::string>>
  parse_action_line(std::string line) const override {
    // Extract action name and parameters from a line of planner output
  }

  // Optional: Extract lines containing actions from the complete plan output
  std::vector<std::string>
  get_lines_with_actions(const std::string &plan_str) const override {
    // Filter and return only the lines that represent actions
  }
};

Register your planner in a plugins.xml file and export it using PLUGINLIB_EXPORT_CLASS.

Creating New Plan Validators

Plan validators verify that generated plans are correct. Inherit from omni_plan::PlanValidator:

#include "omni_plan/plan_validator.hpp"

class MyValidator : public omni_plan::PlanValidator {
public:
  MyValidator() : PlanValidator() {}

protected:
  // Validate plan against domain and problem
  bool validate_plan(const std::string &domain_path,
                    const std::string &problem_path,
                    const std::string &plan_path) const override {
    // Implement validation logic using your preferred validator
  }

  // Convert Plan object to PDDL string format
  std::string parse_pddl(const omni_plan::pddl::Plan &plan) const override {
    // Convert the internal Plan representation to PDDL format
  }
};

Creating New Plan Dispatchers

A PlanDispatcher plugin controls how the actions of a plan are executed once the planning graph has been built. Two built-in strategies are provided (SequentialPlanDispatcher and ParallelPlanDispatcher). You can create your own by:

  1. Inheriting from omni_plan::PlanDispatcher and implementing dispatch_actions().
  2. Registering it as a pluginlib plugin with base class omni_plan::PlanDispatcher.
// my_dispatcher/include/my_dispatcher/my_plan_dispatcher.hpp
#include "omni_plan/plan_dispatcher.hpp"

namespace my_dispatcher {

class MyPlanDispatcher : public omni_plan::PlanDispatcher {
public:
  MyPlanDispatcher() : omni_plan::PlanDispatcher() {}

protected:
  omni_plan::pddl::ActionStatus dispatch_actions(
      const std::vector<omni_plan::pddl::GraphNode::Ptr> &all_nodes) override {

    for (const auto &node : all_nodes) {
      if (this->is_canceled()) {
        return omni_plan::pddl::ActionStatus::CANCELED;
      }

      this->set_node_status(node->node_num,
                            omni_plan_msgs::msg::PlanActionStatus::RUNNING);
      this->publish_exec_status(
          omni_plan_msgs::msg::PlanExecutionStatus::RUNNING);

      auto action = node->action.action;
      this->push_current_action(action);
      omni_plan::pddl::ActionStatus result =
          this->run_node_action(node, action);
      this->remove_current_action(action);

      if (result != omni_plan::pddl::ActionStatus::SUCCEEDED) {
        if (this->cancel_on_abort_) {
          this->cancel_plan();
        }
        return result;
      }
    }

    this->clear_current_actions();
    return omni_plan::pddl::ActionStatus::SUCCEEDED;
  }
};

} // namespace my_dispatcher

Protected helpers available in dispatch_actions()

Helper Description
is_canceled() Returns true if cancel_plan() has been called.
cancel_plan() Cancels all running actions and sets the cancellation flag.
run_node_action(node, action) Applies PDDL effects, runs the action, and rolls back on failure. Returns ActionStatus.
push_current_action(action, use_cache) Registers an action as currently running (enables cancellation). Returns the action instance to use — if use_cache=true a cached copy is returned when the action is already in use (for parallel branches).
remove_current_action(action) Un-registers a completed action.
clear_current_actions() Clears all tracked actions (call at the end of dispatch).
set_node_status(node_num, status) Updates the per-node execution status for publishing.
publish_exec_status(overall) Publishes the current status snapshot on /omni_plan/plan_execution.
acquire_cached_action(action) Returns an idle clone from the action pool (or creates one).
release_cached_action(action) Returns a clone to the pool for future reuse.

Protected configuration flags

Flag Default Description
cancel_on_abort_ false When true, call cancel_plan() automatically whenever an action aborts.
cancel_on_new_goals_ false When true, monitor the PDDL manager for new goals and cancel execution if any appear.

Both flags are exposed as ROS parameters: plan_dispatcher.cancel_on_abort and plan_dispatcher.cancel_on_new_goals.

Creating New Actions

Actions define the executable behaviors in your planning domain. All actions inherit from omni_plan::pddl::Action and must implement the run and cancel methods. All action types must be registered in a plugins.xml file and exported using the appropriate PLUGINLIB_EXPORT_CLASS macro.

Regular Omni Plan Actions

For simple actions implemented directly in C++:

#include "omni_plan/pddl/action.hpp"

class MyAction : public omni_plan::pddl::Action {
public:
  MyAction()
      : Action("my_action", {
          {"param1", "type1"},
          {"param2", "type2"}
      }) {
    // Add preconditions
    this->add_condition(omni_plan::pddl::START, "predicate_name",
                       {"param1", "param2"});

    // Add effects
    this->add_effect(omni_plan::pddl::END, "predicate_name",
                    {"param1"}, true);  // true for negated effect
  }

  omni_plan::pddl::ActionStatus run(const std::vector<std::string> &params) override {
    // Implement your action execution logic
    // Return SUCCEEDED, CANCELED, or ABORTED
    return omni_plan::pddl::ActionStatus::SUCCEEDED;
  }

  void cancel() override {
    // Handle action cancellation
  }
};

YASMIN Actions

For actions that use YASMIN state machines defined programmatically:

#include "omni_plan_yasmin/yasmin_action.hpp"

class MyYasminAction : public omni_plan_yasmin::YasminAction {
public:
  MyYasminAction()
      : YasminAction("my_action", {
          {"param1", "type1"},
          {"param2", "type2"}
      }) {
    // Define PDDL conditions and effects as in regular actions

    // Build your YASMIN state machine
    this->add_state("STATE1", std::make_shared<MyState1>());
    this->add_state("STATE2", std::make_shared<MyState2>());
    this->add_transition("STATE1", "STATE2", "outcome1");
    // ... configure state machine
  }
};

YASMIN Factory Actions

For actions using YASMIN state machines defined in XML files:

#include "omni_plan_yasmin/yasmin_factory_action.hpp"

class MyYasminFactoryAction : public omni_plan_yasmin::YasminFactoryAction {
public:
  MyYasminFactoryAction()
      : YasminFactoryAction("my_action",
                           {{"param1", "type1"}, {"param2", "type2"}},
                           "/path/to/state_machine.xml") {
    // Define PDDL conditions and effects
    // The state machine is loaded from the XML file
  }
};

Behavior Tree Actions

For actions implemented as Behavior Trees:

#include "omni_plan_bt/bt_action.hpp"

class MyBtAction : public omni_plan_bt::BtAction {
public:
  MyBtAction()
      : BtAction("my_action",
                {{"param1", "type1"}, {"param2", "type2"}},
                "/path/to/behavior_tree.xml") {
    // Define PDDL conditions and effects
    // The behavior tree is loaded from the XML file
  }
};

About

OmniPlan is a ROS 2 framework for automated task planning and execution. Can be extended through the creation of new plugins to integrate new planners and new knowledge sources.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors