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

Open
wants to merge 116 commits into
base: topic/humble-devel/refactor
Choose a base branch
from

Conversation

ArthurH91
Copy link
Collaborator

@ArthurH91 ArthurH91 commented Dec 11, 2024

Hello,

Intro

Here's a draft PR on the OCP base class.

After a discussion with @MaximilienNaveau , we agreed that having only crocoddyl might restrict some users. So we decided to have the OCPBase class staying as it is right now, and having a daughter class OCPCrocoBase, which is a simple task to reach a target with the end effector, as a baseline.

In this draft, I'd like to discuss this with y'all. Do you agree with this reasoning?

What's in the PR

  • changes on absolute paths for files relative to mpc
  • modification of the input of solve of the ocpbase (because it takes x_init and x_0, which is already given in x_init)
  • the OCPCrocoBaseClass, as mentionned earlier
  • an OCP Param Base class, that can "generalize" the input of the OCP (ie number of iterations of the solver, qp iters if SQP, error, termination condition ...) after the parsing.
  • a draft on the testing, because i have several issues, especially with the data classes not taking none as input

What I'd like to discuss

  • OCP Param Base class, I believe that's a valid option as output to the parser (be it from ros, yaml or whatever)

agimus_controller/agimus_controller/mpc_data.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_base.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_base_croco.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_param_base.py Outdated Show resolved Hide resolved
agimus_controller/tests/test_ocp_base.py Outdated Show resolved Hide resolved
WeightedTrajectoryPoints: List[
WeightedTrajectoryPoint
] | None = None # List of weighted trajectory points
armature: npt.NDArray[np.float64] | None = None # Armature of the robot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should go with a RobotModel object that goes into the robot_model class.
We should use the srdf of the robot for this maybe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

srdf would be a hassle, but we could discuss it in a separate PR. For now, i simply put the armature as a property.

agimus_controller/agimus_controller/ocp_param_base.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_param_base.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/trajectory.py Outdated Show resolved Hide resolved

def test_horizon_size(self):
"""Test the horizon_size property of the OCPCrocoBase class."""
ocp = OCPCrocoBase(self.rmodel, self.cmodel, self.OCPParams)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you expect a type error to be flag in the test above why this would work?

Copy link
Contributor

@Kotochleb Kotochleb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, you should avoid introduction of new public function to the abstract class. It was meant to be somewhat fixed so that no one is confused, and we do not end up with compatibility issues like those between for example, default Crocoddyl objects and MiM Solvers.

Additionally your implementation of OCPCrocoBase is not generic at all. It is very specific and you should introduce another class like OCPCrocoReaching where you can impose use of CSQP and your costs. OCPCrocoBase should only implement basic interaction with Crocoddyl and expose maybe new private abstract functions that allow user to choose which solver they want to use and how they want to set up shooting problem object.

Additionally please add docstrigns to OCPBase as you are the first person to extend it, so we will not have to document it later on.

agimus_controller/agimus_controller/ocp_base.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_base_croco.py Outdated Show resolved Hide resolved
self,
rmodel: pin.Model,
cmodel: pin.GeometryModel,
OCPParams: OCPParamsCrocoBase,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO OCPParamsCrocoBase should be simply arguments of the __init__ function. In C++ it's required due to registers and stuff, but python doesn't care

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i believe that having a data class is better for readability / organisation purpose, and so that you don't have gazillion of stuff in the init of the class

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But most of them can be passed as defaults. And this is a common pattern in python libraries. Just look at how many parameters matplotlib function have

agimus_controller/agimus_controller/trajectory.py Outdated Show resolved Hide resolved
Comment on lines 4 to 15
<<<<<<< Updated upstream
from agimus_controller.agimus_controller.ocp_param_base import OCPParamsCrocoBase
from agimus_controller.agimus_controller.trajectory import (
WeightedTrajectoryPoint,
TrajectoryPoint,
TrajectoryPointWeights,
)

=======
from agimus_controller.ocp_param_base import OCPParamsCrocoBase
from agimus_controller.trajectory import WeightedTrajectoryPoint, TrajectoryPoint, TrajectoryPointWeights
>>>>>>> Stashed changes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ArthurH91... Why did you commit merge conflict? Can you do pre-commit install?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well you know sometimes it happens

Copy link
Contributor

@Kotochleb Kotochleb Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not accept in this case answer that you are just a chill guy

Comment on lines 24 to 26
dt = np.float64(0.01)
T = 100
solver_iters = 50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to cast those and you can pass them in the OCPParamsCrocoBase directly

agimus_controller/agimus_controller/ocp_base.py Outdated Show resolved Hide resolved
agimus_controller/agimus_controller/ocp_base_croco.py Outdated Show resolved Hide resolved
def x_init(self) -> np.ndarray:
"""Initial guess of the states."""
x_init = []
for TrajectoryPoint in self._OCPParams.WeightedTrajectoryPoints:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snake_case: "TrajectoryPoint" -> "trajectory_point" or "traj_point" or "t_point" etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little preference for "trajectory_point"

terminalCostModel.addCost(
"stateReg",
xRegCost,
np.concatenate(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that the CostModelSum::addCost API take a vector of weights as its third input:
https://github.com/loco-3d/crocoddyl/blob/da92f67394c07c987458a8cb24bc0e33edd64227/include/crocoddyl/core/costs/cost-sum.hxx#L29

For weights regularization, you can define a CostModelResidual with an ActivationModelWeightedQuad, see here or here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! I was doing it blindly here tbh

Copy link

@MedericFourmy MedericFourmy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is the function solve: maybe creating croco objects everytime would be too costly.

### Creation of the state and actuation models

# Stat and actuation model
self._state = crocoddyl.StateMultibody(self._rmodel)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I wonder if this is reasonable (in terms of time) to create croco objects (running/terminal costs, Action models etc.) from scratch every time we solve? It's simpler than going through through the croco ocp structure to only but might be a bit slow.
I just checked on a personal codebase a similar function and got:
T = 20 -> 0.7 ms
T = 40 -> 1.5 ms
T = 60 -> 2.7 ms
T = 80 -> 3.8 ms
T = 100 -> 4.5 ms

So it seems that the _state/_actuation creation is negligeable (I'd still put it in __init__ or a setup function) compared to the running cost creation.

@ArthurH91
Copy link
Collaborator Author

My main concern is the function solve: maybe creating croco objects everytime would be too costly.

i totally agree with this, thank you all for the reviews, i'm gonna start working on it tmrw !

ArthurH91 and others added 30 commits January 8, 2025 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants