-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Proposal: Refactor DexforceW1Cfg solver initialization (PytorchSolver vs SRSSolver overwrite)
File: embodichain/lab/sim/robots/dexforce_w1/cfg.py
Class: DexforceW1Cfg
Current behavior
@configclass
class DexforceW1Cfg(RobotCfg):
...
@classmethod
def from_dict(
cls, init_dict: Dict[str, str | float | tuple | dict]
) -> DexforceW1Cfg:
...
init_dict_m = init_dict.copy()
version = init_dict_m.get("version", "v021")
arm_kind = init_dict_m.get("arm_kind", "anthropomorphic")
with_default_eef = init_dict_m.get("with_default_eef", True)
init_dict_m.pop("version", None)
init_dict_m.pop("arm_kind", None)
init_dict_m.pop("with_default_eef", None)
# 1) Build base cfg via utils (this already initializes PytorchSolver solver_cfg based on URDF + control_parts)
cfg: DexforceW1Cfg = cls()._build_default_cfg(
version=version, arm_kind=arm_kind, with_default_eef=with_default_eef
)
# 2) Merge default physics configs
default_physics_cfgs = cls()._build_default_physics_cfgs(
arm_kind=arm_kind, with_default_eef=with_default_eef
)
for key, value in default_physics_cfgs.items():
setattr(cfg, key, value)
# 3) Build SRSSolver configs for both arms
default_solver_cfg = cls()._build_default_solver_cfg(
is_industrial=(arm_kind == "industrial")
)
# 4) ⚠️ This line overwrites the PytorchSolver-based solver_cfg created in _build_default_cfg
cfg.solver_cfg = default_solver_cfg
# 5) Finally merge external overrides
cfg = merge_robot_cfg(cfg, init_dict_m)
return cfgNotes:
-
_build_default_cfg()internally callsbuild_dexforce_w1_cfg()(indexforce_w1/utils.py), which already builds a completesolver_cfgbased on the assembled URDF andcontrol_parts, including:full_bodyleft_arm_to_torsoright_arm_to_torso- other whole-body / torso-related control parts as
PytorchSolverCfg.
-
_build_default_solver_cfg()only returns SRSSolver configs for the two 7-DoF arms:
@staticmethod
def _build_default_solver_cfg(is_industrial: bool) -> SolverCfg:
from embodichain.lab.sim.solvers import SRSSolverCfg
from embodichain.lab.sim.robots.dexforce_w1.params import W1ArmKineParams
if is_industrial:
...
else:
...
return {
"right_arm": SRSSolverCfg(...),
"left_arm": SRSSolverCfg(...),
}As a result:
build_dexforce_w1_cfg()first initializes a rich set of PytorchSolver entries (full_body / *_arm_to_torso / ...).- Then
cfg.solver_cfg = default_solver_cfgreplaces the entire dict with a much smaller one that only contains"left_arm"and"right_arm"SRSSolver entries. - All PytorchSolver-based configs built in
utils.build_dexforce_w1_solver_cfgare effectively discarded; their initialization work becomes meaningless and the final behavior is non‑obvious to users.
Why this is problematic
DexforceW1 is designed to support two complementary IK solver families:
-
URDF-based PytorchSolver:
- Used for whole-body / torso-related control parts such as
full_body,left_arm_to_torso,right_arm_to_torso, etc.
- Used for whole-body / torso-related control parts such as
-
DH-based SRSSolver:
- Used for the 7-DoF arms:
left_arm,right_arm(high-quality, analytic-like IK).
- Used for the 7-DoF arms:
The current from_dict() implementation:
- discards the PytorchSolver-based entries created in
_build_default_cfg/build_dexforce_w1_cfg, - keeps only the SRSSolver entries for
left_arm/right_arm, - breaks the intended layering: “whole-body PytorchSolver” + “per-arm SRSSolver”.
It also makes the code path harder to reason about:
logging solver_cfg right after build_dexforce_w1_cfg() shows rich PytorchSolver configs, but the final DexforceW1Cfg.solver_cfg that Robot.init_solver sees is a different, reduced set.
Proposed improvement
Goals:
-
Preserve the PytorchSolver-based solver configs produced by
build_dexforce_w1_cfg()for:full_bodyleft_arm_to_torsoright_arm_to_torso- etc.
-
Still provide SRSSolver-based defaults for:
"left_arm""right_arm"
-
Allow external
robot.solver_cfgin user configs to override individual fields (e.g.,tcp,ik_nearest_weight) instead of replacing the entire solver_cfg dict.
Suggested change (only in the solver initialization part of from_dict):
@classmethod
def from_dict(
cls, init_dict: Dict[str, str | float | tuple | dict]
) -> DexforceW1Cfg:
...
cfg: DexforceW1Cfg = cls()._build_default_cfg(
version=version, arm_kind=arm_kind, with_default_eef=with_default_eef
)
default_physics_cfgs = cls()._build_default_physics_cfgs(
arm_kind=arm_kind, with_default_eef=with_default_eef
)
for key, value in default_physics_cfgs.items():
setattr(cfg, key, value)
# Build SRSSolver defaults for left/right arms
default_solver_cfg = cls()._build_default_solver_cfg(
is_industrial=(arm_kind == "industrial")
)
# ✅ Proposed: merge instead of overwrite.
# If build_dexforce_w1_cfg did not set solver_cfg, just use SRSSolver defaults.
# Otherwise, only update "left_arm"/"right_arm" with SRSSolver, keeping
# full_body / left_arm_to_torso / right_arm_to_torso PytorchSolver entries.
if cfg.solver_cfg is None:
cfg.solver_cfg = default_solver_cfg
else:
cfg.solver_cfg.update(default_solver_cfg)
cfg = merge_robot_cfg(cfg, init_dict_m)
return cfgWith this:
-
build_dexforce_w1_cfg()+build_dexforce_w1_solver_cfg()are responsible for:- default
control_partslayout (torso,head,left_arm,right_arm,full_body,...); - URDF-based
PytorchSolverCfgforfull_body,left_arm_to_torso,right_arm_to_torso, etc.
- default
-
_build_default_solver_cfg()is responsible only for:- providing SRSSolverCfg for
"left_arm"and"right_arm"(used to overwrite or supplement those keys).
- providing SRSSolverCfg for
-
External
init_dict["solver_cfg"]:- can safely override fields (e.g.,
tcp,ik_nearest_weight) viamerge_robot_cfgwithout unintentionally wiping out all other solver entries.
- can safely override fields (e.g.,
Summary
Currently, the PytorchSolver-based solver_cfg built in _build_default_cfg (via build_dexforce_w1_cfg) is entirely overwritten in from_dict by _build_default_solver_cfg, making the earlier initialization effectively useless.
By changing the logic to merge (cfg.solver_cfg.update(default_solver_cfg)) instead of blindly overwriting, we can:
- keep whole-body / torso PytorchSolver IK intact,
- still use SRSSolver for arm-specific IK,
- and make the configuration behavior much easier to understand and extend.