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

Ros2 node demo3 #152

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2ea7940
cherry-picked kotochleb cganges
kzorina Jan 20, 2025
975972f
draft for ros node
kzorina Jan 20, 2025
a61c4f1
Fixed some params
kzorina Jan 20, 2025
9c61329
Clean up
kzorina Jan 20, 2025
010999f
Hardcoded for hardware experiment
kzorina Jan 21, 2025
c8072f5
Hardcoded for simulation experiment
kzorina Jan 21, 2025
751f07c
Fix TODO items
kzorina Jan 23, 2025
aa0afa5
Merge remote-tracking branch 'origin/topic/humble-devel/refactor' int…
kzorina Jan 23, 2025
1d2150c
fix some tests, change to OCPPose reaching
kzorina Jan 23, 2025
eff53ec
give gravity compensation as u_ref in simple_traj
kzorina Jan 24, 2025
c7dc204
small cleanup
kzorina Jan 24, 2025
a956a32
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 24, 2025
c1f916e
played with q0 setting from hardware
kzorina Jan 24, 2025
47b5478
fixed todo in simple traj publisher
kzorina Jan 24, 2025
96e9440
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 24, 2025
634fadd
delete params file
kzorina Jan 24, 2025
80ddace
revert changes in tests
kzorina Jan 24, 2025
4e57e2e
changed to filed for q0
kzorina Jan 24, 2025
9275330
trying to make CI happy
kzorina Jan 24, 2025
c8db50e
trying to make CI happy
kzorina Jan 24, 2025
0e46670
trying to make CI happy
kzorina Jan 24, 2025
291405d
Update agimus_controller/agimus_controller/warm_start_reference.py
kzorina Jan 27, 2025
4b60281
Update agimus_controller_ros/agimus_controller_ros/agimus_controller_…
kzorina Jan 27, 2025
662d542
Update agimus_controller/tests/test_ocp_croco_base.py
kzorina Jan 27, 2025
e941ab4
Update agimus_controller_ros/agimus_controller_ros/ros_utils.py
kzorina Jan 27, 2025
56072f1
Update agimus_controller_ros/agimus_controller_ros/ros_utils.py
kzorina Jan 27, 2025
61c39ea
Update agimus_controller/tests/test_ocp_croco_base.py
kzorina Jan 27, 2025
a729d57
Update agimus_controller/tests/test_ocp_croco_goal_reaching.py
kzorina Jan 27, 2025
703b799
Update agimus_controller_ros/agimus_controller_ros/agimus_controller.py
kzorina Jan 27, 2025
d690077
Update agimus_controller_ros/agimus_controller_ros/agimus_controller.py
kzorina Jan 27, 2025
7ce55c5
Update agimus_controller_ros/agimus_controller_ros/agimus_controller.py
kzorina Jan 27, 2025
04022d1
Update agimus_controller_ros/agimus_controller_ros/agimus_controller.py
kzorina Jan 27, 2025
ef0e160
add conversion to np array in buffer filling
kzorina Jan 28, 2025
f75142d
change the shape of u_init in warmstart_ref
kzorina Jan 28, 2025
9ba2aeb
bring back dependencies
kzorina Jan 28, 2025
62de55c
add QoS to all subscr/pub
kzorina Jan 28, 2025
1f5fae1
rename traj publisher
kzorina Jan 28, 2025
92553ee
store numpy state msg
kzorina Jan 28, 2025
dc495eb
Merging refactor branch
kzorina Jan 28, 2025
0875595
WIP - why effort weight makes it unstable?..
kzorina Jan 28, 2025
f5f9f84
add files for OCP debugging
kzorina Jan 31, 2025
0be6f64
Fix bugs in demo3, now runs okay
kzorina Jan 31, 2025
1f6a7db
Merge remote-tracking branch 'origin/topic/humble-devel/refactor' int…
kzorina Feb 3, 2025
5ebda1d
Fix trajectory buffer definition
kzorina Feb 3, 2025
52d50c5
Fix broken tests
kzorina Feb 3, 2025
6ec6326
Fix warmstart size
kzorina Feb 3, 2025
0ca7d40
Change warmstart size
kzorina Feb 3, 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
54 changes: 16 additions & 38 deletions agimus_controller/agimus_controller/factory/robot_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@

@dataclass
class RobotModelParameters:
full_q0: npt.NDArray[np.float64] = field(
default_factory=lambda: np.array([], dtype=np.float64)
) # Initial full configuration of the robot
q0: npt.NDArray[np.float64] = field(
default_factory=lambda: np.array([], dtype=np.float64)
) # Initial reduced configuration of the robot
) # Initial full 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)
moving_joint_names: list[str] = field(default_factory=list)
urdf: Union[
Path, str
] = "" # Path to the URDF file or string containing URDF as an XML
Expand All @@ -39,26 +36,17 @@ class RobotModelParameters:
) # Red color for the collision model

def __post_init__(self):
# 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
self.armature = np.zeros(len(self.q0), dtype=np.float64)
# Use a default armature filled with 0s, based on the size of moving_joint_names
self.armature = np.zeros(len(self.moving_joint_names), dtype=np.float64)

# Ensure armature has the same shape as q0
# Ensure armature has the same shape as moving_joint_names
if (
self.armature.shape != self.q0.shape and not self.free_flyer
len(self.armature) != len(self.moving_joint_names) and not self.free_flyer
): #! TODO: Do the same for free flyer
raise ValueError(
f"Armature must have the same shape as q0. Got {self.armature.shape} and {self.q0.shape}."
f"Armature must have the same shape as moving_joint_names. Got {self.armature.shape} and {len(self.moving_joint_names)}."
)

# Ensure URDF and SRDF are valid
Expand Down Expand Up @@ -96,7 +84,6 @@ 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 @@ -127,20 +114,10 @@ def collision_model(self) -> pin.GeometryModel:
raise AttributeError("Colision model has not been computed yet.")
return self._collision_model

@property
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()
self._apply_locked_joints()
self._lock_joints()
if self._params.collision_as_capsule:
self._update_collision_model_to_capsules()
if self._params.self_collision:
Expand Down Expand Up @@ -187,23 +164,24 @@ def _load_full_pinocchio_models(self) -> None:
f"Failed to load URDF models from {self._params.urdf}: {e}"
)

def _apply_locked_joints(self) -> None:
def _lock_joints(self) -> None:
"""Apply locked joints."""
joints_to_lock = []
for jn in self._params.locked_joint_names:
if self._full_robot_model.existJointName(jn):
for jn in self._full_robot_model.names:
if jn == "universe":
continue
if not jn in self._params.moving_joint_names:
joints_to_lock.append(self._full_robot_model.getJointId(jn))
else:
raise ValueError(f"Joint {jn} not found in the robot model.")

if len(self._q0) == 0:
self._q0 = np.zeros(self._full_robot_model.nq)
self._robot_model, [
self._collision_model,
self._visual_model,
] = pin.buildReducedModel(
self._full_robot_model,
[self._collision_model, self._visual_model],
joints_to_lock,
self._full_q0,
self._q0,
)

def _update_collision_model_to_capsules(self) -> None:
Expand Down
12 changes: 8 additions & 4 deletions agimus_controller/agimus_controller/mpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,29 @@ def setup(
self._ocp = ocp
self._warm_start = warm_start
self._buffer = buffer
self._mpc_debug_data = MPCDebugData(ocp=self._ocp.debug_data)

def run(self, initial_state: TrajectoryPoint, current_time_ns: int) -> OCPResults:
assert self._ocp is not None
assert self._warm_start is not None
timer1 = time.perf_counter_ns()
self._buffer.clear_past(current_time_ns)
if len(self._buffer) < self._ocp.horizon_size:
return None
reference_trajectory = self._extract_horizon_from_buffer()
self._ocp.set_reference_trajectory(reference_trajectory)
self._ocp.set_reference_weighted_trajectory(reference_trajectory)
timer2 = time.perf_counter_ns()
reference_trajectory_points = [el.point for el in reference_trajectory]
x0, x_init, u_init = self._warm_start.generate(
initial_state, reference_trajectory, self._ocp.debug_data.result
initial_state, reference_trajectory_points
)
timer3 = time.perf_counter_ns()
self._ocp.solve(x0, x_init, u_init)
self._warm_start.update_previous_solution(self._ocp.debug_data.result)
self._warm_start.update_previous_solution(self._ocp._ocp_results.states)
timer4 = time.perf_counter_ns()

# Extract the solution.
self._mpc_debug_data = self._ocp.debug_data
self._mpc_debug_data.ocp = self._ocp.debug_data
self._mpc_debug_data.duration_iteration_ns = timer4 - timer1
self._mpc_debug_data.duration_horizon_update_ns = timer2 - timer1
self._mpc_debug_data.duration_generate_warm_start_ns = timer3 - timer2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ def set_reference_weighted_trajectory(
# Modify running cost weight
state_reg.cost.activation.weights = np.concatenate(
(
reference_weighted_trajectory[i].weight.w_robot_configuration,
reference_weighted_trajectory[i].weight.w_robot_velocity,
reference_weighted_trajectory[i].weights.w_robot_configuration,
reference_weighted_trajectory[i].weights.w_robot_velocity,
)
)
# Modify control regularization cost
Expand All @@ -155,13 +155,13 @@ def set_reference_weighted_trajectory(
# Modify running cost weight
ctrl_reg.cost.activation.weights = reference_weighted_trajectory[
i
].weight.w_robot_effort
].weights.w_robot_effort
# Modify end effector frame cost

# setting running model goal tracking reference, weight and frame id
# assuming exactly one end-effector tracking reference was passed to the trajectory
ee_names = list(
iter(reference_weighted_trajectory[i].weight.w_end_effector_poses)
iter(reference_weighted_trajectory[i].weights.w_end_effector_poses)
)
if len(ee_names) > 1:
raise ValueError("Only one end-effector tracking reference is allowed.")
Expand All @@ -173,7 +173,7 @@ def set_reference_weighted_trajectory(
ee_cost.cost.residual.id = ee_id
ee_cost.cost.activation.weights = reference_weighted_trajectory[
i
].weight.w_end_effector_poses[ee_name]
].weights.w_end_effector_poses[ee_name]
ee_cost.cost.residual.reference = reference_weighted_trajectory[
i
].point.end_effector_poses[ee_name]
Expand All @@ -191,14 +191,14 @@ def set_reference_weighted_trajectory(

state_reg.cost.activation.weights = np.concatenate(
(
reference_weighted_trajectory[i].weight.w_robot_configuration,
reference_weighted_trajectory[i].weight.w_robot_velocity,
reference_weighted_trajectory[-1].weights.w_robot_configuration,
MaximilienNaveau marked this conversation as resolved.
Show resolved Hide resolved
reference_weighted_trajectory[-1].weights.w_robot_velocity,
)
)
# Modify end effector frame cost

ee_names = list(
iter(reference_weighted_trajectory[i].weight.w_end_effector_poses)
iter(reference_weighted_trajectory[-1].weights.w_end_effector_poses)
)
ee_name = ee_names[0]
ee_cost = self._solver.problem.runningModels[-1].differential.costs.costs[
Expand All @@ -207,7 +207,7 @@ def set_reference_weighted_trajectory(
ee_cost.cost.residual.id = ee_id
ee_cost.cost.activation.weights = reference_weighted_trajectory[
-1
].weight.w_end_effector_poses[ee_name]
].weights.w_end_effector_poses[ee_name]
ee_cost.cost.residual.reference = reference_weighted_trajectory[
-1
].point.end_effector_poses[ee_name]
2 changes: 1 addition & 1 deletion agimus_controller/agimus_controller/ocp_base_croco.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def solve(
self._problem.x0 = x0
# Solve the OCP
self._solver.solve(
[x0] + x_warmstart, u_warmstart, self._ocp_params.solver_iters
list([x0] + x_warmstart), u_warmstart, self._ocp_params.solver_iters
)

# Store the results
Expand Down
13 changes: 7 additions & 6 deletions agimus_controller/agimus_controller/trajectory.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,17 @@ class WeightedTrajectoryPoint:
"""Trajectory point and it's corresponding weights."""

point: TrajectoryPoint
weight: TrajectoryPointWeights
weights: TrajectoryPointWeights


class TrajectoryBuffer(deque):
"""List of variable size in which the HPP trajectory nodes will be."""

def clear_past(self, current_time_ns):
while self and self[0].point.time_ns < current_time_ns:
self.popleft()
def clear_past(self, current_time_ns, delta_ns=1e9):
self.popleft()
# while self and self[0].point.time_ns < current_time_ns - delta_ns:
# self.popleft()

def horizon(self, horizon_size, dt_ocp):
def horizon(self, horizon_size, dt_ocp=None):
# TBD improve this implementation in case the dt_mpc != dt_ocp
return self._buffer[: self._ocp.horizon_size]
return list(self)[:horizon_size]
14 changes: 8 additions & 6 deletions agimus_controller/agimus_controller/warm_start_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,22 @@ def generate(
f"Expected x_init shape {(len(reference_trajectory), self._nx)}, "
f"from provided reference got {x_init.shape}"
)

# print(np.array(reference_trajectory[0].robot_configuration).shape)
# print(np.array(reference_trajectory[0].robot_velocity).shape)
# print(np.array(reference_trajectory[0].robot_acceleration).shape)
kzorina marked this conversation as resolved.
Show resolved Hide resolved
u_init = [
pin.rnea(
self._rmodel,
self._rdata,
point.robot_configuration,
point.robot_velocity,
point.robot_acceleration,
np.array(point.robot_configuration),
kzorina marked this conversation as resolved.
Show resolved Hide resolved
np.array(point.robot_velocity),
np.array(point.robot_acceleration),
)
for point in reference_trajectory
]
assert np.array(u_init).shape == (len(reference_trajectory), self._rmodel.nv), (
f"Expected u_init shape {(len(reference_trajectory), self._rmodel.nv)}, "
f"from provided reference got {u_init.shape}"
)

return x0, x_init, u_init
# reduce the size of control ref by one to fit Croco way of doing things
return x0, x_init, u_init[:-1]
kzorina marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 0 additions & 8 deletions agimus_controller/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,7 @@

<!-- Run-time dependencies -->
<exec_depend>python3-numpy</exec_depend>
<exec_depend>colmpc</exec_depend>
<exec_depend>crocoddyl</exec_depend>
<exec_depend>coal</exec_depend>
<exec_depend>example-robot-data</exec_depend>
<exec_depend>mim_solvers</exec_depend>
<exec_depend>numpy</exec_depend>
<exec_depend>pinocchio</exec_depend>
kzorina marked this conversation as resolved.
Show resolved Hide resolved
<exec_depend>xacro</exec_depend>
<exec_depend>rospkg</exec_depend>
kzorina marked this conversation as resolved.
Show resolved Hide resolved

<!-- Test dependencies -->
<test_depend>ament_copyright</test_depend>
Expand Down
22 changes: 12 additions & 10 deletions agimus_controller/tests/test_ocp_croco_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,28 @@ def set_reference_weighted_trajectory(self, reference_weighted_trajectory):
def setUpClass(self):
# Mock the RobotModelFactory and OCPParamsCrocoBase
robot = robex.load("panda")
urdf_path = Path(robot.urdf)
srdf_path = Path(robot.urdf.replace("urdf", "srdf"))
urdf_meshes_dir = urdf_path.parent.parent.parent.parent.parent
urdf = Path(robot.urdf)
srdf = Path(robot.urdf.replace("urdf", "srdf"))
urdf_meshes_dir = urdf.parent.parent.parent.parent.parent
kzorina marked this conversation as resolved.
Show resolved Hide resolved
free_flyer = False
locked_joint_names = ["panda_finger_joint1", "panda_finger_joint2"]
reduced_nq = robot.model.nq - len(locked_joint_names)
full_q0 = np.zeros(robot.model.nq)
q0 = np.zeros(reduced_nq)
moving_joint_names = set(robot.model.names) - set(
locked_joint_names + ["universe"]
)
q0 = np.zeros(robot.model.nq)
armature = np.full(reduced_nq, 0.1)

# Store shared initial parameters
self.params = RobotModelParameters(
q0=q0,
full_q0=full_q0,
free_flyer=free_flyer,
locked_joint_names=locked_joint_names,
urdf=urdf_path,
srdf=srdf_path,
moving_joint_names=moving_joint_names,
urdf=urdf,
srdf=srdf,
kzorina marked this conversation as resolved.
Show resolved Hide resolved
urdf_meshes_dir=urdf_meshes_dir,
collision_as_capsule=True,
self_collision=True,
self_collision=False,
armature=armature,
)

Expand Down
9 changes: 5 additions & 4 deletions agimus_controller/tests/test_ocp_croco_goal_reaching.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ def setUp(self):
free_flyer = False
locked_joint_names = ["panda_finger_joint1", "panda_finger_joint2"]
reduced_nq = robot.model.nq - len(locked_joint_names)
full_q0 = pin.randomConfiguration(robot.model)
q0 = full_q0[:reduced_nq]
moving_joint_names = set(robot.model.names) - set(
locked_joint_names + ["universe"]
)
q0 = np.zeros(robot.model.nq)
armature = np.full(reduced_nq, 0.1)

# Store shared initial parameters
self.params = RobotModelParameters(
q0=q0,
full_q0=full_q0,
free_flyer=free_flyer,
locked_joint_names=locked_joint_names,
moving_joint_names=moving_joint_names,
urdf=urdf,
srdf=srdf,
kzorina marked this conversation as resolved.
Show resolved Hide resolved
urdf_meshes_dir=urdf_meshes_dir,
Expand Down
Loading
Loading