Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCP Croco Class #112

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
2b74b10
creating the ocp param base class & the ocp base croco
ArthurH91 Dec 11, 2024
60648fc
populate ocp param & croco
ArthurH91 Dec 11, 2024
3d36d16
setting up the params base class
ArthurH91 Dec 11, 2024
0820ea7
add test for TestOcpBase
ArthurH91 Dec 11, 2024
16fbafe
change all relative paths to absolute ones
ArthurH91 Dec 11, 2024
6ff4507
unit tests of the data class ocp param croco base
ArthurH91 Dec 11, 2024
1da7779
first try on testing the ocp croco base class
ArthurH91 Dec 11, 2024
0105d0c
ocp croco set up
ArthurH91 Dec 11, 2024
4ddeea4
getting rid of target in the dataclass
ArthurH91 Dec 11, 2024
b2eac48
getting rid of target in the dataclass 2
ArthurH91 Dec 11, 2024
4f07f49
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 11, 2024
61beafa
fixing imports
ArthurH91 Dec 12, 2024
5d0ced3
fixing imports 2
ArthurH91 Dec 12, 2024
6be9c9f
removing x0 property ocp base
ArthurH91 Dec 12, 2024
bf1dde7
Update agimus_controller/agimus_controller/trajectory.py
ArthurH91 Dec 12, 2024
4c42f41
Update agimus_controller/agimus_controller/ocp_base.py
ArthurH91 Dec 12, 2024
eab8f12
docstring + bringing back x0 in the solve + changing the name x_init …
ArthurH91 Dec 13, 2024
894fce4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 13, 2024
09c47cc
adding armature in the robot model factory
ArthurH91 Dec 13, 2024
cf7991f
Merge branch 'ahaffemayer/feature/create-ocp-param-class' of github.c…
ArthurH91 Dec 13, 2024
a55807d
getting rid of the x0 property
ArthurH91 Dec 13, 2024
f7db9b6
getting rid of x init and u init method
ArthurH91 Dec 13, 2024
92212d2
changing the robot model input for the init
ArthurH91 Dec 13, 2024
0e8d145
getting rid of the trajectory points
ArthurH91 Dec 13, 2024
95ab7c7
getting rid of useless imports
ArthurH91 Dec 13, 2024
ef8bd36
changing the solve function to only solving and creating properties f…
ArthurH91 Dec 13, 2024
5cda790
changing the result of solve
ArthurH91 Dec 13, 2024
6e54b6e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 13, 2024
58b8dc7
changing OCPParam to ocp_param
ArthurH91 Jan 2, 2025
3faa492
adding few comments + changing the docstring for the init
ArthurH91 Jan 2, 2025
4e2373c
adding the filter linesearch data input in the dataclass ocp param croco
ArthurH91 Jan 2, 2025
9047baa
unit testing the ocp param base dataclass
ArthurH91 Jan 2, 2025
ebb28b7
deleting the ocp base unit test because that's only an abstract class
ArthurH91 Jan 2, 2025
90afd0f
changing ocpparamcrocobase to ocpparambasecroco
ArthurH91 Jan 2, 2025
1fb5f38
wrong placement of problem solved
ArthurH91 Jan 2, 2025
c544c12
changing ocpparamcrocobase to ocpparambasecroco
ArthurH91 Jan 2, 2025
e422536
switching from ... to pass for abstract class because it explodes oth…
ArthurH91 Jan 2, 2025
fa3da6e
fixing the union problem
ArthurH91 Jan 2, 2025
5c8a53e
fixing the property
ArthurH91 Jan 2, 2025
038cd0f
first unit test passing, now working on the simple example as unit test
ArthurH91 Jan 2, 2025
8ce22ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 2, 2025
156afd8
addind setters for ocp base croco & robot model
ArthurH91 Jan 6, 2025
f6c8efb
adding the unit test for the simple ocp
ArthurH91 Jan 6, 2025
982f976
adding the npy results for the simple ocp used for the unit testing
ArthurH91 Jan 6, 2025
31c06ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 6, 2025
e011080
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
bc1f31d
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
255c1f0
Update agimus_controller/agimus_controller/ocp_base.py
ArthurH91 Jan 6, 2025
417d9bb
Update agimus_controller/agimus_controller/ocp_base.py
ArthurH91 Jan 6, 2025
bafd67c
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
54ddd9b
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
adb3ced
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
861f7c2
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 6, 2025
db440c7
Apply suggestions from code review
ArthurH91 Jan 6, 2025
b2f99bb
Update agimus_controller/agimus_controller/ocp_param_base.py
ArthurH91 Jan 6, 2025
ee4181c
taking reviews in to account
ArthurH91 Jan 6, 2025
841493e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 6, 2025
402968b
switching to snake case
ArthurH91 Jan 6, 2025
d6ca98b
fixing typo
ArthurH91 Jan 6, 2025
55c3095
change t to horizon size
ArthurH91 Jan 6, 2025
dcf4812
Merge branch 'ahaffemayer/feature/create-ocp-param-class' of github.c…
ArthurH91 Jan 6, 2025
b8eb8ab
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 6, 2025
72e2185
methods instead of properties for the croco models
ArthurH91 Jan 6, 2025
b6266d0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 6, 2025
303dd30
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 7, 2025
0dfe450
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
6b731ca
Update agimus_controller/tests/test_ocp_param_base.py
ArthurH91 Jan 7, 2025
7c703d6
Update agimus_controller/tests/test_ocp_param_base.py
ArthurH91 Jan 7, 2025
25d8344
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
d2b9107
Update agimus_controller/tests/test_ocp_croco_base.py
ArthurH91 Jan 7, 2025
ad39d40
Update agimus_controller/agimus_controller/ocp_base.py
ArthurH91 Jan 7, 2025
2885b0a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
8083c11
getting rid of useless docstrings
ArthurH91 Jan 7, 2025
c1f6126
displacing the creation of running model & terminal model in the if s…
ArthurH91 Jan 7, 2025
0cc5936
proper type hinting
ArthurH91 Jan 7, 2025
9e5c374
fixing unit tests
ArthurH91 Jan 7, 2025
d9e49cc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
88d2449
Update agimus_controller/agimus_controller/ocp_base.py
ArthurH91 Jan 7, 2025
2aa81eb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
88f572d
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 7, 2025
c7cb95c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2025
91bf33c
adressing the change in update crocoddyl problem
ArthurH91 Jan 7, 2025
b52e01d
merge conflict
ArthurH91 Jan 7, 2025
e29276f
changing the input of the update croccodyl problem method
ArthurH91 Jan 7, 2025
4a7cedd
merging robot refactor
ArthurH91 Jan 10, 2025
d805ea4
fixing unit test ocp croco base param
ArthurH91 Jan 10, 2025
3ddf226
Merge branch 'ahaffemayer/refactor-robot-factory' into ahaffemayer/fe…
ArthurH91 Jan 10, 2025
d6caca3
changing factory to models
ArthurH91 Jan 10, 2025
faf187e
first two unit tests working
ArthurH91 Jan 10, 2025
80c1e59
Merge branch 'topic/humble-devel/refactor' into ahaffemayer/feature/c…
ArthurH91 Jan 13, 2025
a000c3c
unit tests passing for ocp class
ArthurH91 Jan 13, 2025
cede4b6
fixing unit test of robot models
ArthurH91 Jan 13, 2025
a60d680
Update agimus_controller/tests/test_ocp_croco_base.py
ArthurH91 Jan 13, 2025
3227e9e
order of import / duplicate import / typo
ArthurH91 Jan 13, 2025
3436ee9
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 13, 2025
4da3b41
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 13, 2025
da7724f
new pickle way of loading the benchmark
ArthurH91 Jan 13, 2025
46594a3
Merge branch 'ahaffemayer/feature/create-ocp-param-class' of github.c…
ArthurH91 Jan 13, 2025
ca034e1
getting rid of shortcut robot model
ArthurH91 Jan 13, 2025
e8ada60
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 13, 2025
103f20a
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 13, 2025
b6a9d0e
Update agimus_controller/agimus_controller/ocp_base_croco.py
ArthurH91 Jan 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions agimus_controller/agimus_controller/factory/robot_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

@dataclass
class RobotModelParameters:
full_q0: npt.NDArray[np.float64] = np.array(
[], dtype=np.float64
) # Initial full configuration of the robot
q0: npt.NDArray[np.float64] = np.array(
[], dtype=np.float64
) # Initial configuration of the robot
) # Initial reduced configuration of the robot
free_flyer: bool = False # True if the robot has a free flyer
locked_joint_names: list[str] = field(default_factory=list)
urdf_path: Path = Path() # Path to the URDF file
Expand All @@ -33,10 +36,15 @@ class RobotModelParameters:
) # Red color for the collision model

def __post_init__(self):
# Check q0 is not empty
if len(self.q0) == 0:
raise ValueError("q0 cannot be empty.")

# Check full_q0 is not empty
if len(self.full_q0) == 0:
raise ValueError("full_q0 cannot be empty.")

# Check q0 is not empty or if there is no reduced model, q0 is the full configuration
if len(self.q0) == 0 and not len(self.locked_joint_names) == 0:
raise ValueError("q0 cannot be empty while reducing the model.")
elif len(self.q0) == 0:
self.q0 = self.full_q0
# Handle armature:
if self.armature.size == 0:
# Use a default armature filled with 0s, based on the size of q0
Expand Down Expand Up @@ -73,6 +81,7 @@ def __init__(self, param: RobotModelParameters):
self._collision_model = None
self._visual_model = None
self._q0 = deepcopy(self._params.q0)
self._full_q0 = deepcopy(self._params.full_q0)
self.load_models() # Populate models

@property
Expand Down Expand Up @@ -108,6 +117,11 @@ def q0(self) -> np.array:
"""Initial configuration of the robot."""
return self._q0

@property
def full_q0(self) -> np.array:
"""Initial full configuration of the robot."""
return self._full_q0

def load_models(self) -> None:
"""Load and prepare robot models based on parameters."""
self._load_full_pinocchio_models()
Expand Down Expand Up @@ -145,7 +159,7 @@ def _apply_locked_joints(self) -> None:
raise ValueError(f"Joint {jn} not found in the robot model.")

self._robot_model = pin.buildReducedModel(
self._full_robot_model, joints_to_lock, self._q0
self._full_robot_model, joints_to_lock, self._full_q0
)

def _update_collision_model_to_capsules(self) -> None:
Expand Down
5 changes: 2 additions & 3 deletions agimus_controller/agimus_controller/mpc_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ class OCPResults:

@dataclass
class OCPDebugData:
# Solver infos
problem_solved: bool = False

# Debug data
result: list[TrajectoryPoint]
references: list[TrajectoryPoint]
kkt_norms: list[np.float64]
collision_distance_residuals: list[dict[np.float64]]
# Solver infos
problem_solved: bool = False


@dataclass
Expand Down
72 changes: 60 additions & 12 deletions agimus_controller/agimus_controller/ocp_base.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,90 @@
from abc import ABC, abstractmethod

import numpy as np
import numpy.typing as npt

from agimus_controller.mpc_data import OCPResults, OCPDebugData
from agimus_controller.trajectory import WeightedTrajectoryPoint


class OCPBase(ABC):
"""Base class for the Optimal Control Problem (OCP) solver.

This class defines the interface for the OCP solver."""

def __init__(self) -> None:
pass

@abstractmethod
def set_reference_horizon(
self, reference_trajectory: list[WeightedTrajectoryPoint]
) -> None:
...
"""Set the reference trajectory and the weights of the costs for the OCP solver. This method should be implemented by the derived class."""
pass

@abstractmethod
@property
@abstractmethod
def horizon_size() -> int:
...
"""Returns the horizon size of the OCP.

Returns:
int: size of the horizon.
"""
pass

@abstractmethod
@property
def dt() -> int:
...
@abstractmethod
def dt() -> float:
"""Returns the time step of the OCP in seconds.

Returns:
int: time step of the OCP.
"""
pass

@abstractmethod
def solve(
self, x0: np.ndarray, x_init: list[np.ndarray], u_init: list[np.ndarray]
self,
x0: npt.NDArray[np.float64],
x_warmstart: list[npt.NDArray[np.float64]],
u_warmstart: list[npt.NDArray[np.float64]],
) -> None:
...
"""Solver for the OCP. This method should be implemented by the derived class.
The method should solve the OCP for the given initial state and warmstart values.

Args:
x0 (npt.NDArray[np.float64]): current state of the robot.
x_warmstart (list[npt.NDArray[np.float64]]): Warmstart values for the state. This doesn't include the current state.
u_warmstart (list[npt.NDArray[np.float64]]): Warmstart values for the control inputs.
"""
pass

@abstractmethod
@property
@abstractmethod
def ocp_results(self) -> OCPResults:
...
"""Returns the results of the OCP solver.
The solve method should be called before calling this method.

Returns:
OCPResults: Class containing the results of the OCP solver.
"""
pass

@ocp_results.setter
def ocp_results(self, value: OCPResults) -> None:
"""Set the output data structure of the OCP.

Args:
value (OCPResults): New output data structure of the OCP.
"""
pass

@abstractmethod
@property
@abstractmethod
def debug_data(self) -> OCPDebugData:
...
"""Returns the debug data of the OCP solver.

Returns:
OCPDebugData: Class containing the debug data of the OCP solver.
"""
pass
146 changes: 146 additions & 0 deletions agimus_controller/agimus_controller/ocp_base_croco.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
from abc import abstractmethod

import crocoddyl
import mim_solvers
import numpy as np
import numpy.typing as npt

from agimus_controller.factory.robot_model import RobotModels
from agimus_controller.mpc_data import OCPResults, OCPDebugData
from agimus_controller.ocp_base import OCPBase
from agimus_controller.ocp_param_base import OCPParamsBaseCroco
from agimus_controller.trajectory import TrajectoryPointWeights


class OCPBaseCroco(OCPBase):
def __init__(
self,
robot_models: RobotModels,
ocp_params: OCPParamsBaseCroco,
) -> None:
"""Defines common behavior for all OCP using croccodyl. This is an abstract class with some helpers to design OCPs in a more friendly way.

Args:
robot_models (RobotModels): All models of the robot.
ocp_params (OCPParamsBaseCroco): Input data structure of the OCP.
"""
# Setting the robot model
self._robot_models = robot_models
self._collision_model = self._robot_models.collision_model
self._armature = self._robot_models._params.armature

# Stat and actuation model
self._state = crocoddyl.StateMultibody(self._robot_models.robot_model)
self._actuation = crocoddyl.ActuationModelFull(self._state)

# Setting the OCP parameters
self._ocp_params = ocp_params
self._ocp = None
self._ocp_results = None
self._running_model_list = []
self._terminal_model = []
self._problem = None

@property
def horizon_size(self) -> int:
"""Number of time steps in the horizon."""
return self._ocp_params.horizon_size

@property
def dt(self) -> float:
"""Integration step of the OCP."""
return self._ocp_params.dt

@abstractmethod
def create_running_model_list(self) -> list[crocoddyl.ActionModelAbstract]:
"""Create the list of running models."""
pass

@abstractmethod
def create_terminal_model(self) -> crocoddyl.ActionModelAbstract:
"""Create the terminal model."""
pass

@abstractmethod
def update_crocoddyl_problem(
self,
x0: npt.NDArray[np.float64],
weighted_trajectory_points: list[TrajectoryPointWeights],
) -> None:
"""Update the Crocoddyl problem's references, weights and x0."""
pass

def solve(
self,
x0: npt.NDArray[np.float64],
x_warmstart: list[npt.NDArray[np.float64]],
u_warmstart: list[npt.NDArray[np.float64]],
) -> None:
"""Solves the OCP.
The results can be accessed through the ocp_results property.

Args:
x0 (npt.NDArray[np.float64]): Current state of the robot.
x_warmstart (list[npt.NDArray[np.float64]]): Predicted states for the OCP.
u_warmstart (list[npt.NDArray[np.float64]]): Predicted control inputs for the OCP.
"""
### Creation of the state and actuation models

if self._ocp is None:
# Create the running models
self._running_model_list = self.create_running_model_list()
# Create the terminal model
self._terminal_model = self.create_terminal_model()
# Create the shooting problem
self._problem = crocoddyl.ShootingProblem(
x0, self._running_model_list, self._terminal_model
)
# Create solver + callbacks
self._ocp = mim_solvers.SolverCSQP(self._problem)

# Merit function
self._ocp.use_filter_line_search = self._ocp_params.use_filter_line_search

# Parameters of the solver
self._ocp.termination_tolerance = self._ocp_params.termination_tolerance
self._ocp.max_qp_iters = self._ocp_params.qp_iters
self._ocp.eps_abs = self._ocp_params.eps_abs
self._ocp.eps_rel = self._ocp_params.eps_rel
self._ocp.with_callbacks = self._ocp_params.callbacks

# Creating the warmstart lists for the solver
# Solve the OCP
self._ocp.solve([x0] + x_warmstart, u_warmstart, self._ocp_params.solver_iters)

# Store the results
self._ocp_results = OCPResults(
states=self._ocp.xs,
ricatti_gains=self._ocp.K,
feed_forward_terms=self._ocp.us,
)

@property
def ocp_results(self) -> OCPResults:
"""Output data structure of the OCP.

Returns:
OCPResults: Output data structure of the OCP. It contains the states, Ricatti gains, and feed-forward terms.
"""
return self._ocp_results

@ocp_results.setter
def ocp_results(self, value: OCPResults) -> None:
"""Set the output data structure of the OCP.

Args:
value (OCPResults): New output data structure of the OCP.
"""
self._ocp_results = value

@property
def debug_data(self) -> OCPDebugData:
pass

@debug_data.setter
def debug_data(self, value: OCPDebugData) -> None:
pass
21 changes: 21 additions & 0 deletions agimus_controller/agimus_controller/ocp_param_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from dataclasses import dataclass

import numpy as np


@dataclass
class OCPParamsBaseCroco:
"""Input data structure of the OCP."""

# Data relevant to solve the OCP
dt: float # Integration step of the OCP
horizon_size: int # Number of time steps in the horizon
solver_iters: int # Number of solver iterations
qp_iters: int = 200 # Number of QP iterations (must be a multiple of 25).
termination_tolerance: float = (
1e-3 # Termination tolerance (norm of the KKT conditions)
)
eps_abs: float = 1e-6 # Absolute tolerance of the solver
eps_rel: float = 0.0 # Relative tolerance of the solver
callbacks: bool = False # Flag to enable/disable callbacks
use_filter_line_search = False # Flag to enable/disable the filter line searchs
Binary file not shown.
Loading
Loading