Genesis icon indicating copy to clipboard operation
Genesis copied to clipboard

[Bug]: "TypeError: not a sequence" Error with Multi-DoF Joints in Custom MJCF Files

Open Hippozhibos opened this issue 1 year ago • 2 comments

Bug Description

Description The official reinforcement learning (RL) code is designed for the Go2 robot, which assumes a one-to-one relationship between joints and degrees of freedom (1 joint = 1 DoF). When using a custom MJCF file where a single joint has multiple degrees of freedom (1 joint > 1 DoF), the code raises the following error: "TypeError: not a sequence".

This issue arises because the code does not handle scenarios where different links contains different numbers of Dofs (e.g., entity has different type of joints which contains different number of dofs).

Steps to Reproduce

To ReproduceSteps to reproduce the issue:

Within go2_env.py,replace the default Go2 robot's file with a custom MJCF file where at least one joint defines multiple degrees of freedom (e.g., 3 rotational DoFs). Probably ./genesis/asset/xml/ant.xml is enough, because it contains a free joint.

Run go2_train.py. Revise the parameters in get_cfgs() according to the custom MJCF, especially: "num_actions", "default_joint_angles", "dof_names"(which I thinks should be named as "joint_names") in env_cfg, and "num_obs" in obs_cfg. By the way, it would be better if the code can change this value automaticlly.

Observe the following error:

Traceback (most recent call last):
  File "/Genesis/examples/locomotion/go2_train.py", line 315, in <module>
    main()
  File "/Genesis/examples/locomotion/go2_train.py", line 300, in main
    env = Go2Env(
  File "/Genesis/examples/locomotion/go2_env.py", line 26, in __init__
    self._initialize_environment(num_envs, show_viewer)
  File "/Genesis/examples/locomotion/go2_env.py", line 63, in _initialize_environment
    self.robot.set_dofs_kp([self.env_cfg["kp"]] * self.env_cfg["num_actions"], self.motor_dofs)
  File "/anaconda3/envs/genesis/lib/python3.10/site-packages/genesis/utils/misc.py", line 48, in wrapper
    return method(self, *args, **kwargs)
  File "/anaconda3/envs/genesis/lib/python3.10/site-packages/genesis/engine/entities/rigid_entity/rigid_entity.py", line 1807, in set_dofs_kp
    self._solver.set_dofs_kp(kp, self._get_dofs_idx(dofs_idx_local), envs_idx)
  File "/anaconda3/envs/genesis/lib/python3.10/site-packages/genesis/engine/entities/rigid_entity/rigid_entity.py", line 1757, in _get_dofs_idx
    return self._get_dofs_idx_local(dofs_idx_local) + self._dof_start
  File "/anaconda3/envs/genesis/lib/python3.10/site-packages/genesis/engine/entities/rigid_entity/rigid_entity.py", line 1751, in _get_dofs_idx_local
    dofs_idx_local = torch.as_tensor(dofs_idx_local, dtype=gs.tc_int, device=gs.device)
TypeError: not a sequence

Expected Behavior

Maybe a more general code for RL training would suite more users. The code should correctly handle MJCF/URDF files with joints that have multiple DoFs, either by:

Automatically flattening or mapping joint configurations to a consistent sequence.

Raising a clear error message with suggestions to fix the input data.

Screenshots/Videos

No response

Relevant log output


Environment

  • OS: Ubuntu 22.04
  • GPU/CPU:NVIDIA GeForce RTX 4090
  • GPU-driver version: 550.90.07
  • CUDA / CUDA-toolkit version: CUDA Version: 12.4

Release version or Commit ID

commit hash: 36b6670a4a0783d4ed58d6cd8edbd5662727677f

Additional Context

I've tried to fix this error by adding the following changes:

  1. change go2_env.py line71 from: self.motor_dofs = [self.robot.get_joint(name).dof_idx_local for name in self.env_cfg["dof_names"]] to: self.motor_dofs = list(itertools.chain.from_iterable( [self.robot.get_joint(name).dof_idx_local] if isinstance(self.robot.get_joint(name).dof_idx_local, int) else self.robot.get_joint(name).dof_idx_local for name in self.env_cfg["dof_names"] ))

  2. change go2_env.py line108 - line 113 from:

self.default_dof_pos = torch.tensor( [self.env_cfg["default_joint_angles"][name] for name in self.env_cfg["dof_names"]], device=self.device, dtype=gs.tc_float, ) to: flat_dof_pos = list(itertools.chain.from_iterable( [value] if isinstance(value, (int, float)) else value for value in [self.env_cfg["default_joint_angles"][name] for name in self.env_cfg["dof_names"]] )) self.default_dof_pos = torch.tensor(flat_dof_pos, device=self.device, dtype=gs.tc_float)

Hippozhibos avatar Jan 23 '25 09:01 Hippozhibos

My understanding is that Genesis is a physics engine and not an RL environment. The RL example is just a simple showcase of how you could use Genesis for RL training.

Kashu7100 avatar Jan 24 '25 13:01 Kashu7100

@Kashu7100 Thank you for your clarification! I completely understand that Genesis is primarily a physics engine, and the existing RL example is more of a functional demonstration. My question stems from some compatibility issues I encountered while trying to adapt the code to a custom model, and I agree that it’s unreasonable to expect the demo code to be universally applicable.

Regarding the manual configuration of num_actions and num_obs, I’d like to ask: Does Genesis provide any API interfaces that can automatically retrieve the following information to avoid hardcoding? For example:

num_actions:

Is it possible to directly obtain the total degrees of freedom (DoFs) of the entity through something like self.robot.num_dofs, instead of manually counting joint configurations?

(For instance, if the MJCF contains multiple joints with multiple DoFs, num_actions should be the sum of all DoFs across joints.)

num_obs:

Can the observation space dimension be dynamically obtained through something like self.obs_buf.shape[-1]?

Or is there a predefined observation calculation tool (e.g., the dimension of the return value from get_observations())?

Desired automatic configuration (pseudo-code) env_cfg = { "num_actions": self.robot.total_dofs, # Assuming a total_dofs attribute exists "dof_names": self.robot.active_joint_names, # Assuming a list of joint names exists "default_joint_angles": self.robot.neutral_positions # Assuming neutral positions exist }

If such interfaces exist, could you provide an example of how to use them? This would be very helpful for adapting different models.

Additionally, I attempted to fix the code by flattening the motor_dofs and default_dof_pos (see the modifications in the original Issue). Do you think this approach aligns with Genesis’s design logic? Is there a more recommended way to implement this?

Hippozhibos avatar Feb 05 '25 03:02 Hippozhibos