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

Update pytorch interface #65

Merged
merged 7 commits into from
Jan 26, 2024
Merged

Update pytorch interface #65

merged 7 commits into from
Jan 26, 2024

Conversation

Giulero
Copy link
Collaborator

@Giulero Giulero commented Jan 25, 2024

The aim of this PR is to update the Pytorch interface and habilitate its use in deep learning context (i.e. use differentiability). Uniforming to float64.

@Giulero
Copy link
Collaborator Author

Giulero commented Jan 26, 2024

I did a 10-minute test to verify that the pytorch interface can be used in a neural network.

I created a small NN that takes in input a constant value, it has some intermediate layers, with the last one a forward kinematics function. The network outputs 2 homogenous transforms (l_sole and l_lower_leg).
The objective is to minimize the loss computed as the difference (for a certain definition of difference, in the example nn.MSELoss()) between the predicted transforms and the target ones.

This is the script:

# test if the forward kinematics is differentiable and can be used in a neural network

import logging
import icub_models
import torch.nn as nn
import torch
from adam import Representations
from adam.pytorch import KinDynComputations
import matplotlib.pyplot as plt
from rich.progress import track

# torch.random.seed(42)
# torch.set_default_dtype(torch.float64)

model_path = str(icub_models.get_model_file("iCubGazeboV2_5"))

joints_name_list = [
    "torso_pitch",
    "torso_roll",
    "torso_yaw",
    "l_shoulder_pitch",
    "l_shoulder_roll",
    "l_shoulder_yaw",
    "l_elbow",
    "r_shoulder_pitch",
    "r_shoulder_roll",
    "r_shoulder_yaw",
    "r_elbow",
    "l_hip_pitch",
    "l_hip_roll",
    "l_hip_yaw",
    "l_knee",
    "l_ankle_pitch",
    "l_ankle_roll",
    "r_hip_pitch",
    "r_hip_roll",
    "r_hip_yaw",
    "r_knee",
    "r_ankle_pitch",
    "r_ankle_roll",
]


logging.basicConfig(level=logging.DEBUG)
logging.debug("Showing the robot tree.")

root_link = "root_link"
comp = KinDynComputations(model_path, joints_name_list, root_link)
comp.set_frame_velocity_representation(Representations.MIXED_REPRESENTATION)

n_dofs = len(joints_name_list)

# base pose quantities
# pytorch random quantities
xyz = (torch.rand(3) - 0.5) * 5
rpy = (torch.rand(3) - 0.5) * 5
base_vel = (torch.rand(6) - 0.5) * 5
# joints quantitites
joints_val = (torch.rand(n_dofs) - 0.5) * 5
joints_dot_val = (torch.rand(n_dofs) - 0.5) * 5

from adam.pytorch.torch_like import SpatialMath

math = SpatialMath()

g = torch.tensor([0, 0, -9.80665])
H_b = math.H_from_Pos_RPY(xyz, rpy).array

random_joints = torch.randn(n_dofs, requires_grad=True)
# forward kinematics, to compute targets
output_lsole = comp.forward_kinematics("l_sole", H_b, random_joints)
output_llower_leg = comp.forward_kinematics("l_lower_leg", H_b, random_joints)


class fkNN(nn.Module):
    def __init__(self):
        super(fkNN, self).__init__()
        self.comp = KinDynComputations(model_path, joints_name_list, root_link)
        self.comp.set_frame_velocity_representation(
            Representations.MIXED_REPRESENTATION
        )
        self.fc1 = nn.Linear(n_dofs, 6 + n_dofs)
        self.act1 = nn.ReLU()
        self.fc2 = nn.Linear(6 + n_dofs, 6 + n_dofs)
        self.act2 = nn.ReLU()
        self.out = nn.Linear(6 + n_dofs, n_dofs)

    def forward(self, joints_val):
        x = self.act1(self.fc1(joints_val))
        x = self.act2(self.fc2(x))
        x = self.out(x)
        # pass to the forward kinematics
        return self.comp.forward_kinematics(
            "l_sole", H_b, x
        ), self.comp.forward_kinematics("l_lower_leg", H_b, x)

    def get_joints(self, value):
        x = self.act1(self.fc1(value))
        x = self.act2(self.fc2(x))
        return self.out(x)


fkNN = fkNN()

criterion = nn.MSELoss()
optimizer = torch.optim.SGD(fkNN.parameters(), lr=0.01)

joints_val_torch = torch.ones(n_dofs, requires_grad=True)
target_lsole = output_lsole.clone().detach()
target_llower_leg = output_llower_leg.clone().detach()
loss_array = []
for _ in track(range(30000), description="Training"):
    optimizer.zero_grad()
    output_lsole, output_llower_leg = fkNN(joints_val_torch)
    loss = criterion(target_lsole, output_lsole) + criterion(
        target_llower_leg, output_llower_leg
    )
    loss.backward()
    optimizer.step()
    # print(f"loss: {loss}")
    loss_array.append(loss.detach().numpy())
print(f"target lsole: {target_lsole}")
print(f"output lsole: {output_lsole}")
print(f"target llower leg: {target_llower_leg}")
print(f"output llower leg: {output_llower_leg}")
print(f"original joints: {random_joints}")
print(f"nn joints: {fkNN.get_joints(joints_val_torch)}")

steps = range(len(loss_array))
plt.plot(steps, loss_array)
plt.xlabel("steps")
plt.ylabel("loss")
plt.show()

The output of the network, i.e. the predicted homogeneous transforms of l_sole and l_lower_leg converge to the target values.

target lsole: tensor([[-0.4314,  0.7503, -0.5010,  1.1846],
        [ 0.7966,  0.5775,  0.1789,  1.1001],
        [ 0.4235, -0.3219, -0.8468, -0.6390],
        [ 0.0000,  0.0000,  0.0000,  1.0000]], dtype=torch.float64)
output lsole: tensor([[-0.4314,  0.7503, -0.5010,  1.1846],
        [ 0.7966,  0.5775,  0.1789,  1.1001],
        [ 0.4235, -0.3219, -0.8468, -0.6390],
        [ 0.0000,  0.0000,  0.0000,  1.0000]], dtype=torch.float64,
       grad_fn=<MmBackward0>)
target llower leg: tensor([[-0.4487,  0.8847,  0.1266,  1.1776],
        [ 0.7305,  0.2815,  0.6222,  1.2364],
        [ 0.5148,  0.3716, -0.7726, -0.8485],
        [ 0.0000,  0.0000,  0.0000,  1.0000]], dtype=torch.float64)
output llower leg: tensor([[-0.4487,  0.8847,  0.1266,  1.1776],
        [ 0.7305,  0.2815,  0.6222,  1.2364],
        [ 0.5148,  0.3716, -0.7726, -0.8485],
        [ 0.0000,  0.0000,  0.0000,  1.0000]], dtype=torch.float64,
       grad_fn=<MmBackward0>)

The output of the intermediate layers, i.e, the joints that we insert in the forward kinematics function converge to the joints values used to compute the target values (for the left leg joints, the 6 values before the 6 last values)

original joints: tensor([ 1.8165, -0.4723,  0.5580, -1.3924,  0.2683, -0.2554,  1.2683, -0.8576,
        -0.0050, -1.4101, -0.6394,  0.5347, -0.5572,  0.8706,  2.6310, -0.1141,
         0.7860,  0.1291, -0.4271,  0.0490, -1.9311, -0.9298,  0.2214],
       requires_grad=True)
nn joints: tensor([-0.0812, -0.1694,  0.3457, -0.3293, -0.7453,  0.0631,  0.4840, -0.2661,
        -0.3233,  0.0550,  0.3637,  0.5347, -0.5572,  0.8706,  2.6310, -0.1141,
         0.7860,  0.1906,  0.6204,  0.4075,  0.0221,  0.3031, -0.3142],

cc. @Zweisteine96

Copy link
Contributor

@traversaro traversaro left a comment

Choose a reason for hiding this comment

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

Some typos

src/adam/parametric/pytorch/computations_parametric.py Outdated Show resolved Hide resolved
src/adam/parametric/pytorch/computations_parametric.py Outdated Show resolved Hide resolved
Giulero and others added 3 commits January 26, 2024 14:02
Co-authored-by: Silvio Traversaro <silvio@traversaro.it>
Co-authored-by: Silvio Traversaro <silvio@traversaro.it>
@Giulero
Copy link
Collaborator Author

Giulero commented Jan 26, 2024

Thanks @traversaro! Fixed

Copy link

@Zweisteine96 Zweisteine96 left a comment

Choose a reason for hiding this comment

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

Thanks Giuse! I'll try in my network soon!

@Giulero
Copy link
Collaborator Author

Giulero commented Jan 26, 2024

Thanks! :)

@Giulero Giulero merged commit 919576d into main Jan 26, 2024
15 checks passed
@Giulero Giulero deleted the pytorch-interface branch January 26, 2024 16:30
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.

3 participants