Add acceleration cost

This commit is contained in:
TJU-Lu 2025-11-07 14:55:30 +08:00
parent 3826bf7d34
commit 854c932b97
4 changed files with 46 additions and 32 deletions

View File

@ -9,6 +9,7 @@ acc_max_train: 6.0
# IMPORTANT: weight of costs for unit speed (can be visualized in tensorboard) # IMPORTANT: weight of costs for unit speed (can be visualized in tensorboard)
wg: 0.12 # guidance wg: 0.12 # guidance
ws: 10.0 # smoothness ws: 10.0 # smoothness
wa: 1.0 # acceleration
wc: 0.1 # collision wc: 0.1 # collision
# dataset: set image_size = primitive_num × downsampling_factor (×32 for ResNet-18) in each axis # dataset: set image_size = primitive_num × downsampling_factor (×32 for ResNet-18) in each axis

View File

@ -18,15 +18,12 @@ class YOPOLoss(nn.Module):
super(YOPOLoss, self).__init__() super(YOPOLoss, self).__init__()
self.sgm_time = cfg["sgm_time"] self.sgm_time = cfg["sgm_time"]
self.device = th.device("cuda" if th.cuda.is_available() else "cpu") self.device = th.device("cuda" if th.cuda.is_available() else "cpu")
self._C, self._B, self._L, self._R = self.qp_generation() self._C, self._B, self._L, self._RJ, self._RA = self.qp_generation()
self._R = self._R.to(self.device) self._RJ = self._RJ.to(self.device)
self._RA = self._RA.to(self.device)
self._L = self._L.to(self.device) self._L = self._L.to(self.device)
vel_scale = cfg["vel_max_train"] / 1.0 self.denormalize_weight()
self.smoothness_weight = cfg["ws"] self.smoothness_loss = SmoothnessLoss(self._RJ, self._RA)
self.safety_weight = cfg["wc"]
self.goal_weight = cfg["wg"]
self.denormalize_weight(vel_scale)
self.smoothness_loss = SmoothnessLoss(self._R)
self.safety_loss = SafetyLoss(self._L) self.safety_loss = SafetyLoss(self._L)
self.goal_loss = GuidanceLoss() self.goal_loss = GuidanceLoss()
print("------ Actual Loss ------") print("------ Actual Loss ------")
@ -43,15 +40,21 @@ class YOPOLoss(nn.Module):
for j in range(i, 6): for j in range(i, 6):
A[2 * i + 1, j] = math.factorial(j) / math.factorial(j - i) * (self.sgm_time ** (j - i)) A[2 * i + 1, j] = math.factorial(j) / math.factorial(j - i) * (self.sgm_time ** (j - i))
# H海森矩阵论文中的矩阵Q # H海森矩阵对应Jerk
H = th.zeros((6, 6)) H = th.zeros((6, 6))
for i in range(3, 6): for i in range(3, 6):
for j in range(3, 6): for j in range(3, 6):
H[i, j] = i * (i - 1) * (i - 2) * j * (j - 1) * (j - 2) / (i + j - 5) * (self.sgm_time ** (i + j - 5)) H[i, j] = i * (i - 1) * (i - 2) * j * (j - 1) * (j - 2) / (i + j - 5) * (self.sgm_time ** (i + j - 5))
return self.stack_opt_dep(A, H) # Q海森矩阵对应Accel
Q = th.zeros((6, 6))
for i in range(2, 6):
for j in range(2, 6):
Q[i, j] = (i * (i - 1)) * (j * (j - 1)) / (i + j - 3) * (self.sgm_time ** (i + j - 3))
def stack_opt_dep(self, A, Q): return self.stack_opt_dep(A, H, Q)
def stack_opt_dep(self, A, H, Q):
Ct = th.zeros((6, 6)) Ct = th.zeros((6, 6))
Ct[[0, 2, 4, 1, 3, 5], [0, 1, 2, 3, 4, 5]] = 1 Ct[[0, 2, 4, 1, 3, 5], [0, 1, 2, 3, 4, 5]] = 1
@ -63,11 +66,13 @@ class YOPOLoss(nn.Module):
_L = B @ Ct _L = B @ Ct
_R = _C @ (B_T) @ Q @ B @ Ct _R_Jerk = _C @ (B_T) @ H @ B @ Ct
return _C, B, _L, _R _R_Acc = _C @ (B_T) @ Q @ B @ Ct
def denormalize_weight(self, vel_scale): return _C, B, _L, _R_Jerk, _R_Acc
def denormalize_weight(self):
""" """
Denormalize the cost weight to ensure consistency across different speeds to simplify parameter tuning. Denormalize the cost weight to ensure consistency across different speeds to simplify parameter tuning.
smoothness cost: time integral of jerk² is used as a smoothness cost. smoothness cost: time integral of jerk² is used as a smoothness cost.
@ -77,9 +82,11 @@ class YOPOLoss(nn.Module):
goal cost: projection of the trajectory onto goal direction. goal cost: projection of the trajectory onto goal direction.
Independent of speed. Independent of speed.
""" """
self.smoothness_weight = self.smoothness_weight / vel_scale ** 5 vel_scale = cfg["vel_max_train"] / 1.0
self.safety_weight = self.safety_weight * vel_scale self.smoothness_weight = cfg["ws"] / vel_scale ** 5
self.goal_weight = self.goal_weight self.safety_weight = cfg["wc"] * vel_scale
self.goal_weight = cfg["wg"]
self.accele_weight = cfg["wa"] / vel_scale ** 3
def forward(self, state, prediction, goal, map_id): def forward(self, state, prediction, goal, map_id):
""" """
@ -102,10 +109,10 @@ class YOPOLoss(nn.Module):
goal_cost = th.tensor(0.0, device=self.device, requires_grad=True) goal_cost = th.tensor(0.0, device=self.device, requires_grad=True)
if self.smoothness_weight > 0: if self.smoothness_weight > 0:
smoothness_cost = self.smoothness_loss(Df, Dp) smoothness_cost, acceleration_cost = self.smoothness_loss(Df, Dp)
if self.safety_weight > 0: if self.safety_weight > 0:
safety_cost = self.safety_loss(Df, Dp, map_id) safety_cost = self.safety_loss(Df, Dp, map_id)
if self.goal_weight > 0: if self.goal_weight > 0:
goal_cost = self.goal_loss(Df, Dp, goal) goal_cost = self.goal_loss(Df, Dp, goal)
return self.smoothness_weight * smoothness_cost, self.safety_weight * safety_cost, self.goal_weight * goal_cost return self.smoothness_weight * smoothness_cost, self.safety_weight * safety_cost, self.goal_weight * goal_cost, self.accele_weight * acceleration_cost

View File

@ -3,9 +3,10 @@ import torch as th
class SmoothnessLoss(nn.Module): class SmoothnessLoss(nn.Module):
def __init__(self, R): def __init__(self, RJ, RA):
super(SmoothnessLoss, self).__init__() super(SmoothnessLoss, self).__init__()
self._R = R self._RJ = RJ
self._RA = RA
def forward(self, Df, Dp): def forward(self, Df, Dp):
""" """
@ -15,13 +16,16 @@ class SmoothnessLoss(nn.Module):
Returns: Returns:
cost_smooth: (batch_size) smoothness loss cost_smooth: (batch_size) smoothness loss
""" """
R = self._R.unsqueeze(0).expand(Dp.shape[0], -1, -1) RJ = self._RJ.unsqueeze(0).expand(Dp.shape[0], -1, -1)
RA = self._RA.unsqueeze(0).expand(Dp.shape[0], -1, -1)
D_all = th.cat([Df, Dp], dim=2) # dx, dy, dz will be rows D_all = th.cat([Df, Dp], dim=2) # dx, dy, dz will be rows
# Reshape for matmul: (batch, 6, 1) # Reshape for matmul: (batch, 6, 1)
dx, dy, dz = D_all[:, 0].unsqueeze(2), D_all[:, 1].unsqueeze(2), D_all[:, 2].unsqueeze(2) dx, dy, dz = D_all[:, 0].unsqueeze(2), D_all[:, 1].unsqueeze(2), D_all[:, 2].unsqueeze(2)
# Compute smoothness loss: dxᵀ R dx + ... # Compute smoothness loss: dxᵀ R dx + ...
cost_smooth = dx.transpose(1, 2) @ R @ dx + dy.transpose(1, 2) @ R @ dy + dz.transpose(1, 2) @ R @ dz jerk_smooth = dx.transpose(1, 2) @ RJ @ dx + dy.transpose(1, 2) @ RJ @ dy + dz.transpose(1, 2) @ RJ @ dz
return cost_smooth.squeeze() accel_smooth = dx.transpose(1, 2) @ RA @ dx + dy.transpose(1, 2) @ RA @ dy + dz.transpose(1, 2) @ RA @ dz
return jerk_smooth.squeeze(), accel_smooth.squeeze()

View File

@ -82,13 +82,13 @@ class YopoTrainer:
def train_one_epoch(self, epoch: int, total_progress): def train_one_epoch(self, epoch: int, total_progress):
one_epoch_progress = self.progress_log.add_task(f"Epoch: {epoch}", total=len(self.train_dataloader)) one_epoch_progress = self.progress_log.add_task(f"Epoch: {epoch}", total=len(self.train_dataloader))
inspect_interval = max(1, len(self.train_dataloader) // 16) inspect_interval = max(1, len(self.train_dataloader) // 16)
traj_losses, score_losses, smooth_losses, safety_losses, goal_losses, start_time = [], [], [], [], [], time.time() traj_losses, score_losses, smooth_losses, safety_losses, goal_losses, acc_losses, start_time = [], [], [], [], [], [], time.time()
for step, (depth, pos, rot, obs_b, map_id) in enumerate(self.train_dataloader): # obs: body frame for step, (depth, pos, rot, obs_b, map_id) in enumerate(self.train_dataloader): # obs: body frame
if depth.shape[0] != self.batch_size: continue # batch size == number of env if depth.shape[0] != self.batch_size: continue # batch size == number of env
self.optimizer.zero_grad() self.optimizer.zero_grad()
trajectory_loss, score_loss, smooth_cost, safety_cost, goal_cost = self.forward_and_compute_loss(depth, pos, rot, obs_b, map_id) trajectory_loss, score_loss, smooth_cost, safety_cost, goal_cost, acc_cost = self.forward_and_compute_loss(depth, pos, rot, obs_b, map_id)
loss = self.loss_weight[0] * trajectory_loss + self.loss_weight[1] * score_loss loss = self.loss_weight[0] * trajectory_loss + self.loss_weight[1] * score_loss
@ -101,6 +101,7 @@ class YopoTrainer:
smooth_losses.append(self.loss_weight[0] * smooth_cost.item()) smooth_losses.append(self.loss_weight[0] * smooth_cost.item())
safety_losses.append(self.loss_weight[0] * safety_cost.item()) safety_losses.append(self.loss_weight[0] * safety_cost.item())
goal_losses.append(self.loss_weight[0] * goal_cost.item()) goal_losses.append(self.loss_weight[0] * goal_cost.item())
acc_losses.append(self.loss_weight[0] * acc_cost.item())
if step % inspect_interval == inspect_interval - 1: if step % inspect_interval == inspect_interval - 1:
batch_fps = inspect_interval / (time.time() - start_time) batch_fps = inspect_interval / (time.time() - start_time)
@ -112,7 +113,8 @@ class YopoTrainer:
self.tensorboard_log.add_scalar("Detail/SmoothLoss", np.mean(smooth_losses), epoch * len(self.train_dataloader) + step) self.tensorboard_log.add_scalar("Detail/SmoothLoss", np.mean(smooth_losses), epoch * len(self.train_dataloader) + step)
self.tensorboard_log.add_scalar("Detail/SafetyLoss", np.mean(safety_losses), epoch * len(self.train_dataloader) + step) self.tensorboard_log.add_scalar("Detail/SafetyLoss", np.mean(safety_losses), epoch * len(self.train_dataloader) + step)
self.tensorboard_log.add_scalar("Detail/GoalLoss", np.mean(goal_losses), epoch * len(self.train_dataloader) + step) self.tensorboard_log.add_scalar("Detail/GoalLoss", np.mean(goal_losses), epoch * len(self.train_dataloader) + step)
traj_losses, score_losses, smooth_losses, safety_losses, goal_losses, start_time = [], [], [], [], [], time.time() self.tensorboard_log.add_scalar("Detail/AccelLoss", np.mean(acc_losses), epoch * len(self.train_dataloader) + step)
traj_losses, score_losses, smooth_losses, safety_losses, goal_losses, acc_losses, start_time = [], [], [], [], [], [], time.time()
self.progress_log.update(one_epoch_progress, advance=1) self.progress_log.update(one_epoch_progress, advance=1)
self.progress_log.update(total_progress, advance=1 / len(self.train_dataloader)) self.progress_log.update(total_progress, advance=1 / len(self.train_dataloader))
@ -126,7 +128,7 @@ class YopoTrainer:
for step, (depth, pos, rot, obs_b, map_id) in enumerate(self.val_dataloader): # obs: body frame for step, (depth, pos, rot, obs_b, map_id) in enumerate(self.val_dataloader): # obs: body frame
if depth.shape[0] != self.batch_size: continue # batch size == num of env if depth.shape[0] != self.batch_size: continue # batch size == num of env
trajectory_loss, score_loss, _, _, _ = self.forward_and_compute_loss(depth, pos, rot, obs_b, map_id) trajectory_loss, score_loss, _, _, _, _ = self.forward_and_compute_loss(depth, pos, rot, obs_b, map_id)
traj_losses.append(self.loss_weight[0] * trajectory_loss.item()) traj_losses.append(self.loss_weight[0] * trajectory_loss.item())
score_losses.append(self.loss_weight[1] * score_loss.item()) score_losses.append(self.loss_weight[1] * score_loss.item())
@ -166,12 +168,12 @@ class YopoTrainer:
# [B*V*H, 3, 3]: [px, py, pz; vx, vy, vz; ax, ay, az] # [B*V*H, 3, 3]: [px, py, pz; vx, vy, vz; ax, ay, az]
end_state_w = torch.stack([end_pos_w, end_vel_w, end_acc_w], dim=1) end_state_w = torch.stack([end_pos_w, end_vel_w, end_acc_w], dim=1)
smooth_cost, safety_cost, goal_cost = self.yopo_loss(start_state_w, end_state_w, goal_w, map_id) smooth_cost, safety_cost, goal_cost, acc_cost = self.yopo_loss(start_state_w, end_state_w, goal_w, map_id)
trajectory_loss = (smooth_cost + safety_cost + goal_cost).mean() trajectory_loss = (smooth_cost + safety_cost + goal_cost + acc_cost).mean()
score_label = (smooth_cost + safety_cost + goal_cost).clone().detach() score_label = (smooth_cost + safety_cost + goal_cost + acc_cost).clone().detach()
score_loss = F.smooth_l1_loss(score_flat, score_label) score_loss = F.smooth_l1_loss(score_flat, score_label)
return trajectory_loss, score_loss, smooth_cost.mean(), safety_cost.mean(), goal_cost.mean() return trajectory_loss, score_loss, smooth_cost.mean(), safety_cost.mean(), goal_cost.mean(), acc_cost.mean()
def save_model(self): def save_model(self):
if hasattr(self, "epoch_i"): if hasattr(self, "epoch_i"):