diff --git a/CMakeLists.txt b/CMakeLists.txt
index b23425c17395281887538fc309aebe885367bd71..e4a67b1bb2ab83919808446a47eab3863bf7fe50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,6 +93,8 @@ SET (${PROJECT_NAME}_HEADERS
   include/hpp/manipulation/path-optimization/random-shortcut.hh
   include/hpp/manipulation/path-optimization/spline-gradient-based.hh
 
+  include/hpp/manipulation/path-planner/end-effector-trajectory.hh
+
   include/hpp/manipulation/problem-target/state.hh
 
   include/hpp/manipulation/steering-method/cross-state-optimization.hh
diff --git a/include/hpp/manipulation/path-planner/end-effector-trajectory.hh b/include/hpp/manipulation/path-planner/end-effector-trajectory.hh
new file mode 100644
index 0000000000000000000000000000000000000000..e39dd0f5cbf1a851a24d0891a12f28877c8d9646
--- /dev/null
+++ b/include/hpp/manipulation/path-planner/end-effector-trajectory.hh
@@ -0,0 +1,137 @@
+// Copyright (c) 2019 CNRS
+// Authors: Joseph Mirabel
+//
+// This file is part of hpp-manipulation
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation  If not, see
+// <http://www.gnu.org/licenses/>.
+
+
+#ifndef HPP_MANIPULATION_PATH_PLANNER_END_EFFECTOR_TRAJECTORY_HH
+# define HPP_MANIPULATION_PATH_PLANNER_END_EFFECTOR_TRAJECTORY_HH
+
+# include <hpp/manipulation/config.hh>
+# include <hpp/manipulation/fwd.hh>
+
+# include <pinocchio/spatial/se3.hpp>
+
+# include <hpp/pinocchio/frame.hh>
+# include <hpp/core/path-planner.hh>
+
+namespace hpp {
+  namespace manipulation {
+    namespace pathPlanner {
+      class HPP_MANIPULATION_DLLAPI IkSolverInitialization
+      {
+        public:
+          typedef std::vector<Configuration_t> Configurations_t;
+
+          Configurations_t solve (vectorIn_t target)
+          {
+            return impl_solve (target);
+          }
+
+        protected:
+          virtual Configurations_t impl_solve (vectorIn_t target) = 0;
+      };
+      typedef boost::shared_ptr<IkSolverInitialization> IkSolverInitializationPtr_t;
+
+      HPP_PREDEF_CLASS (EndEffectorTrajectory);
+      typedef boost::shared_ptr<EndEffectorTrajectory> EndEffectorTrajectoryPtr_t;
+
+      class HPP_MANIPULATION_DLLAPI EndEffectorTrajectory : public core::PathPlanner
+      {
+      public:
+        /// Return shared pointer to new instance
+        /// \param problem the path planning problem
+        static EndEffectorTrajectoryPtr_t create (const core::Problem& problem);
+        /// Return shared pointer to new instance
+        /// \param problem the path planning problem
+        /// \param roadmap previously built roadmap
+        static EndEffectorTrajectoryPtr_t createWithRoadmap
+          (const core::Problem& problem, const core::RoadmapPtr_t& roadmap);
+
+        /// Initialize the problem resolution
+        ///  \li call parent implementation
+        ///  \li get number nodes in problem parameter map
+        virtual void startSolve ();
+
+        /// One step of the algorithm
+        virtual void oneStep ();
+
+        /// Get the number of random configurations shoot (after using init
+        /// config) in order to generate the initial config of the final path.
+        int nRandomConfig () const
+        { return nRandomConfig_; }
+
+        void nRandomConfig (int n)
+        {
+          assert (n >= 0);
+          nRandomConfig_ = n;
+        }
+
+        /// Number of steps to generate goal config (successive projections).
+        int nDiscreteSteps () const
+        { return nDiscreteSteps_; }
+
+        void nDiscreteSteps (int n)
+        {
+          assert (n > 0);
+          nDiscreteSteps_ = n;
+        }
+
+        /// If enabled, only add one solution to the roadmap.
+        /// Otherwise add all solution.
+        void checkFeasibilityOnly (bool enable);
+
+        bool checkFeasibilityOnly () const
+        {
+          return feasibilityOnly_;
+        }
+
+        void ikSolverInitialization (IkSolverInitializationPtr_t solver)
+        {
+          ikSolverInit_ = solver;
+        }
+
+        void tryConnectInitAndGoals ();
+
+      protected:
+        /// Protected constructor
+        /// \param problem the path planning problem
+        EndEffectorTrajectory (const core::Problem& problem);
+        /// Protected constructor
+        /// \param problem the path planning problem
+        /// \param roadmap previously built roadmap
+        EndEffectorTrajectory (const core::Problem& problem, const core::RoadmapPtr_t& roadmap);
+        /// Store weak pointer to itself
+        void init (const EndEffectorTrajectoryWkPtr_t& weak);
+
+      private:
+        std::vector<core::Configuration_t> configurations(const core::Configuration_t& q_init);
+
+        /// Weak pointer to itself
+        EndEffectorTrajectoryWkPtr_t weak_;
+        /// Number of random config.
+        int nRandomConfig_;
+        /// Number of steps to generate goal config.
+        int nDiscreteSteps_;
+        /// Ik solver initialization. An external Ik solver can be plugged here.
+        IkSolverInitializationPtr_t ikSolverInit_;
+        /// Feasibility
+        bool feasibilityOnly_;
+      }; // class EndEffectorTrajectory
+    } // namespace pathPlanner
+  } // namespace manipulation
+} // namespace hpp
+
+#endif // HPP_MANIPULATION_PATH_PLANNER_END_EFFECTOR_TRAJECTORY_HH
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ae2c0ecb6b5ff76d5a8902ebd461fa6a955c59d..19c8702c7a5ffe6f9b887ceb0f7d50b8ce02a0ab 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -49,6 +49,8 @@ SET(SOURCES
   path-optimization/random-shortcut.cc
   path-optimization/enforce-transition-semantic.cc
 
+  path-planner/end-effector-trajectory.cc
+
   problem-target/state.cc
 
   steering-method/end-effector-trajectory.cc
diff --git a/src/path-planner/end-effector-trajectory.cc b/src/path-planner/end-effector-trajectory.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a2a5a8f122443bc8269ed2c0d2e310d2be3d3b22
--- /dev/null
+++ b/src/path-planner/end-effector-trajectory.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2019 CNRS
+// Authors: Joseph Mirabel
+//
+// This file is part of hpp-manipulation
+// hpp-manipulation is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation, either version
+// 3 of the License, or (at your option) any later version.
+//
+// hpp-manipulation is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Lesser Public License for more details.  You should have
+// received a copy of the GNU Lesser General Public License along with
+// hpp-manipulation  If not, see
+// <http://www.gnu.org/licenses/>.
+
+# include <hpp/manipulation/path-planner/end-effector-trajectory.hh>
+
+# include <pinocchio/multibody/data.hpp>
+
+# include <hpp/util/exception-factory.hh>
+# include <hpp/pinocchio/util.hh>
+# include <hpp/pinocchio/device-sync.hh>
+# include <hpp/pinocchio/liegroup-element.hh>
+
+# include <hpp/constraints/differentiable-function.hh>
+# include <hpp/constraints/implicit.hh>
+
+# include <hpp/core/config-projector.hh>
+# include <hpp/core/config-validations.hh>
+# include <hpp/core/configuration-shooter.hh>
+# include <hpp/core/path-validation.hh>
+# include <hpp/core/problem.hh>
+# include <hpp/core/roadmap.hh>
+# include <hpp/manipulation/steering-method/end-effector-trajectory.hh>
+
+namespace hpp {
+  namespace manipulation {
+    namespace pathPlanner {
+      typedef manipulation::steeringMethod::EndEffectorTrajectory      SM_t;
+      typedef manipulation::steeringMethod::EndEffectorTrajectoryPtr_t SMPtr_t;
+
+      EndEffectorTrajectoryPtr_t EndEffectorTrajectory::create (const core::Problem& problem)
+      {
+        EndEffectorTrajectoryPtr_t ptr (new EndEffectorTrajectory(problem));
+        ptr->init(ptr);
+        return ptr;
+      }
+
+      EndEffectorTrajectoryPtr_t EndEffectorTrajectory::createWithRoadmap (
+          const core::Problem& problem, const core::RoadmapPtr_t& roadmap)
+      {
+        EndEffectorTrajectoryPtr_t ptr (new EndEffectorTrajectory(problem, roadmap));
+        ptr->init(ptr);
+        return ptr;
+      }
+
+      void EndEffectorTrajectory::tryConnectInitAndGoals ()
+      {}
+
+      void EndEffectorTrajectory::startSolve ()
+      {
+        //core::PathPlanner::startSolve();
+        //problem().checkProblem ();
+        if (!problem().robot ()) {
+          std::string msg ("No device in problem.");
+          hppDout (error, msg);
+          throw std::runtime_error (msg);
+        }
+
+        if (!problem().initConfig ()) {
+          std::string msg ("No init config in problem.");
+          hppDout (error, msg);
+          throw std::runtime_error (msg);
+        }
+
+        // Tag init and goal configurations in the roadmap
+        roadmap()->resetGoalNodes ();
+
+        SMPtr_t sm (HPP_DYNAMIC_PTR_CAST (SM_t, problem().steeringMethod()));
+        if (!sm)
+          throw std::invalid_argument ("Steering method must be of type hpp::manipulation::steeringMethod::EndEffectorTrajectory");
+
+        if (!sm->constraints() || !sm->constraints()->configProjector())
+          throw std::invalid_argument ("Steering method constraint has no ConfigProjector.");
+        core::ConfigProjectorPtr_t constraints (sm->constraints()->configProjector());
+
+        const constraints::ImplicitPtr_t& trajConstraint = sm->trajectoryConstraint ();
+        if (!trajConstraint)
+          throw std::invalid_argument ("EndEffectorTrajectory has no trajectory constraint.");
+        if (!sm->trajectory())
+          throw std::invalid_argument ("EndEffectorTrajectory has no trajectory.");
+
+        const core::NumericalConstraints_t& ncs = constraints->numericalConstraints();
+        bool ok = false;
+        for (std::size_t i = 0; i < ncs.size(); ++i) {
+          if (ncs[i] == trajConstraint) {
+            ok = true;
+            break; // Same pointer
+          }
+          // Here, we do not check the right hand side on purpose.
+          // if (*ncs[i] == *trajConstraint) {
+          if (ncs[i]->functionPtr() == trajConstraint->functionPtr()
+              && ncs[i]->comparisonType() == trajConstraint->comparisonType()) {
+            ok = true;
+            // TODO We should only modify the path constraint.
+            // However, only the pointers to implicit constraints are copied
+            // while we would like the implicit constraints to be copied as well.
+            ncs[i]->rightHandSideFunction (sm->trajectory());
+            break; // logically identical
+          }
+        }
+        if (!ok) {
+          HPP_THROW (std::logic_error, "EndEffectorTrajectory could not find "
+              "constraint " << trajConstraint->function());
+        }
+      }
+
+      void EndEffectorTrajectory::oneStep ()
+      {
+        SMPtr_t sm (HPP_DYNAMIC_PTR_CAST (SM_t, problem().steeringMethod()));
+        if (!sm)
+          throw std::invalid_argument ("Steering method must be of type hpp::manipulation::steeringMethod::EndEffectorTrajectory");
+        if (!sm->trajectoryConstraint ())
+          throw std::invalid_argument ("EndEffectorTrajectory has no trajectory constraint.");
+        if (!sm->trajectory())
+          throw std::invalid_argument ("EndEffectorTrajectory has no trajectory.");
+
+        if (!sm->constraints() || !sm->constraints()->configProjector())
+          throw std::invalid_argument ("Steering method constraint has no ConfigProjector.");
+        core::ConfigProjectorPtr_t constraints (sm->constraints()->configProjector());
+
+        core::ConfigValidationPtr_t  cfgValidation (problem().configValidations());
+        core::  PathValidationPtr_t pathValidation (problem().pathValidation());
+        core::    ValidationReportPtr_t cfgReport;
+        core::PathValidationReportPtr_t pathReport;
+
+        core::interval_t timeRange (sm->timeRange());
+
+        std::vector<core::Configuration_t> qs (configurations(*problem().initConfig()));
+        if (qs.empty()) {
+          hppDout (info, "Failed to generate initial configs.");
+          return;
+        }
+
+        // Generate a valid initial configuration.
+        bool success = false;
+        bool resetRightHandSide = true;
+        std::size_t i;
+        Configuration_t q1;
+        for (i = 0; i < qs.size(); ++i)
+        {
+          if (resetRightHandSide) {
+            constraints->rightHandSideAt (timeRange.first);
+            resetRightHandSide = false;
+          }
+          Configuration_t& q (qs[i]);
+          if (!constraints->apply (q)) continue;
+          if (!cfgValidation->validate (q, cfgReport)) continue;
+          resetRightHandSide = true;
+
+          q1 = q;
+          success = true;
+          for (int j = 1; j <= nDiscreteSteps_; ++j) {
+            value_type t = timeRange.first + j * (timeRange.second - timeRange.first) / nDiscreteSteps_;
+            constraints->rightHandSideAt (t);
+            hppDout (info, "RHS: " << setpyformat << constraints->rightHandSide().transpose());
+            if (!constraints->apply (q)) {
+              hppDout (info, "Failed to generate destination config.\n" << setpyformat
+                  << *constraints
+                  << "\nq=" << one_line (q));
+              success = false;
+              break;
+            }
+          }
+          if (!success) continue;
+          success = false;
+
+          if (!cfgValidation->validate (q, cfgReport)) {
+            hppDout (info, "Destination config is in collision.");
+            continue;
+          }
+
+          core::PathPtr_t path = (*sm) (q1, q);
+          if (!path) {
+            hppDout (info, "Steering method failed.\n" << setpyformat
+                << one_line(q1) << '\n'
+                << one_line(q ) << '\n'
+                );
+            continue;
+          }
+
+          core::PathPtr_t validPart;
+          if (!pathValidation->validate (path, false, validPart, pathReport)) {
+            hppDout (info, "Path is in collision.");
+            continue;
+          }
+
+          roadmap()->initNode (boost::make_shared<Configuration_t>(q1));
+          core::NodePtr_t init = roadmap()->   initNode ();
+          core::NodePtr_t goal = roadmap()->addGoalNode (boost::make_shared<Configuration_t>(q ));
+          roadmap()->addEdge (init, goal, path);
+          success = true;
+          if (feasibilityOnly_) break;
+        }
+      }
+
+      std::vector<core::Configuration_t> EndEffectorTrajectory::configurations(const core::Configuration_t& q_init)
+      {
+        if (!ikSolverInit_) {
+          std::vector<core::Configuration_t> configs(nRandomConfig_ + 1);
+          configs[0] = q_init;
+          for (int i = 1; i < nRandomConfig_ + 1; ++i)
+            problem().configurationShooter()->shoot(configs[i]);
+          return configs;
+        }
+
+        // TODO Compute the target and call ikSolverInit_
+        // See https://gepgitlab.laas.fr/airbus-xtct/hpp_airbus_xtct for an
+        // example using IKFast.
+        throw std::runtime_error ("Using an IkSolverInitialization is not implemented yet");
+      }
+
+      EndEffectorTrajectory::EndEffectorTrajectory (const core::Problem& problem)
+        : core::PathPlanner (problem)
+      {}
+
+      EndEffectorTrajectory::EndEffectorTrajectory (const core::Problem& problem, const core::RoadmapPtr_t& roadmap)
+        : core::PathPlanner (problem, roadmap)
+      {}
+
+      void EndEffectorTrajectory::checkFeasibilityOnly (bool enable)
+      {
+        feasibilityOnly_ = enable;
+      }
+
+      void EndEffectorTrajectory::init (const EndEffectorTrajectoryWkPtr_t& weak)
+      {
+        core::PathPlanner::init (weak);
+        weak_ = weak;
+        nRandomConfig_ = 10;
+        nDiscreteSteps_ = 1;
+        feasibilityOnly_ = true;
+      }
+    } // namespace pathPlanner
+  } // namespace manipulation
+} // namespace hpp
diff --git a/src/problem-solver.cc b/src/problem-solver.cc
index a75cd757a8eb622b65eb626ab7fcdf6c5ef6e948..92eede11847f4842373c0a733e8c1eac6fa38b7f 100644
--- a/src/problem-solver.cc
+++ b/src/problem-solver.cc
@@ -57,6 +57,7 @@
 #include "hpp/manipulation/graph-node-optimizer.hh"
 #include "hpp/manipulation/path-optimization/random-shortcut.hh"
 #include "hpp/manipulation/path-optimization/enforce-transition-semantic.hh"
+#include "hpp/manipulation/path-planner/end-effector-trajectory.hh"
 #include "hpp/manipulation/problem-target/state.hh"
 #include "hpp/manipulation/steering-method/cross-state-optimization.hh"
 #include "hpp/manipulation/steering-method/graph.hh"
@@ -121,6 +122,7 @@ namespace hpp {
       robotType ("hpp::manipulation::Device");
 
       pathPlanners.add ("M-RRT", ManipulationPlanner::create);
+      pathPlanners.add ("EndEffectorTrajectory", pathPlanner::EndEffectorTrajectory::createWithRoadmap);
 
       pathValidations.add ("Graph-Discretized"                      , createDiscretizedCollisionGraphPathValidation);
       pathValidations.add ("Graph-DiscretizedCollision"             , createDiscretizedCollisionGraphPathValidation);