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

Inconsistent behavior with VFS and mjSpec #2484

Open
2 tasks done
vikashplus opened this issue Mar 8, 2025 · 5 comments
Open
2 tasks done

Inconsistent behavior with VFS and mjSpec #2484

vikashplus opened this issue Mar 8, 2025 · 5 comments
Labels
bug Something isn't working

Comments

@vikashplus
Copy link

vikashplus commented Mar 8, 2025

Intro

Hi!

One of the early MuJoCo developer

My setup

Mac, python, mujoco==3.2.4

What's happening? What did you expect?

When using a virtual file system with mjSpec, there are inconsistencies in the behavior (especially when names with Caps are involved)

Case: when the asset/mesh/file is only the filename

name on disc name in XML loads w/ mjSpec? loads natively?
cube.stl Cube.stl True True
cube.stl cube.stl True True
Cube.stl Cube.stl True True
Cube.stl cube.stl False True

Case: when the asset/mesh/file is more complex

name on disc name in XML loads w/ mjSpec? loads natively?
cube.stl path/Cube.stl True True
cube.stl path/cube.stl True True
Cube.stl path/Cube.stl False True
Cube.stl path/cube.stl False True

In summary,

  1. capitalization is not respected natively. Everything works
  2. capitalization is partially respected in mjSpec
  3. capitalization behavior depends on how the name is specified

Steps for reproduction

  1. Unzip the folder
  2. Run python test.py
  3. Change filenames on disc and on XML as specified above.

NOTE:

  1. When trying out cases with VFS, ensure that the fallback of loading assets from the disc doesn't work otherwise the bug won't highlight.
  2. This is extremely important as this bug is about discrepancies between VFS and load from the disc.

Recommendation:
The docs around how caps, relative paths, partial paths, etc are handled in VFS are non-existent. Consider updating the docs.

Minimal model for reproduction

See attached

vfs_caps_bug.zip

Code required for reproduction

python test.py

Confirmations

@vikashplus vikashplus added the bug Something isn't working label Mar 8, 2025
@vikashplus
Copy link
Author

Here is a simpler example.

@quagla can you try this will MuJoCo==3.2.4

import mujoco

mesh_name = "Cube.stl"
assets = {
    mesh_name: b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00"
}

model_str = """
<mujoco model="test model">
    <asset>
      <mesh name="cube" file="cube.stl" scale=".05 .05 .05"/>
    </asset>
    <worldbody>
      <geom type="mesh" mesh="cube" pos="0 0 0" contype="0" conaffinity="0"/>
    </worldbody>
</mujoco>
"""


spec = mujoco.MjSpec.from_string(model_str)
model = spec.compile(assets)

@quagla
Copy link
Contributor

quagla commented Mar 10, 2025

This looks to be a VFS issue, not an mjSpec one. It's reproducible also by loading from XML directly if assets are given with VFS.

@quagla quagla removed their assignment Mar 10, 2025
@vikashplus
Copy link
Author

vikashplus commented Mar 10, 2025

Im observing something different

import mujoco

mesh_name = "Cube.stl"
assets = {
    mesh_name: b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x00\x00\x80\xbf\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x80\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80\xbf\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x80\xbf\x00\x00"
}

model_str = """
<mujoco model="test model">
    <asset>
      <mesh name="cube" file="cube.stl" scale=".05 .05 .05"/>
    </asset>
    <worldbody>
      <geom type="mesh" mesh="cube" pos="0 0 0" contype="0" conaffinity="0"/>
    </worldbody>
</mujoco>
"""

print("Loading model directly", end=": ")
mj_model = mujoco.MjModel.from_xml_string(model_str, assets)
print("success")

print("Loading model via spec", end=": ")
spec = mujoco.MjSpec.from_string(model_str)
model = spec.compile(assets)
print("success")

output

Loading model directly: success
Loading model via spec: Traceback (most recent call last):
  File "/Users/vikashplus/Repos/myolab/myo_api/myo_api/sandbox/vfs_caps_bug/test2.py", line 25, in <module>
    model = spec.compile(assets)
ValueError: Error: Error opening file 'cube.stl': No such file or directory

summary
direct loading of model via XML and VFS works
loading model using spec and VFS fails

@quagla
Copy link
Contributor

quagla commented Mar 10, 2025

This is my MRE directly in C which indicates a VFS issue (note that every time you do from_xml_string an mjSpec is created under the hood in the same way as my MRE below). I don't know how your example succeeds unless Python does some magic or you have a file with that name in the folder where you run the test.

TEST_F(MjCMeshTest, LoadWithVFS) {
  static constexpr char cube[] = R"(
  v -1 -1  1
  v  1 -1  1
  v -1  1  1
  v  1  1  1
  v -1  1 -1
  v  1  1 -1
  v -1 -1 -1
  v  1 -1 -1)";
  auto vfs = std::make_unique<mjVFS>();
  mj_defaultVFS(vfs.get());
  mj_addBufferVFS(vfs.get(), "Cube.obj", cube, sizeof(cube));
  static constexpr char xml[] = R"(
  <mujoco>
    <asset>
      <mesh name="cube" file="cube.obj"/>
    </asset>
    <worldbody>
      <geom type="mesh" mesh="cube"/>
    </worldbody>
  </mujoco>
  )";
  std::array<char, 1024> error;
  mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
  ASSERT_THAT(spec, NotNull()) << error.data();
  mjModel* model = mj_compile(spec, vfs.get());
  EXPECT_THAT(model, IsNull()) << mjs_getError(spec);
  mj_deleteSpec(spec);
  mj_deleteVFS(vfs.get());
}

Note that also here if I use Cube in the XML and cube in VFS, then the model is not NULL.

@quagla
Copy link
Contributor

quagla commented Mar 11, 2025

Maybe @kbayes who knows VFS well has got any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants