Avalon-Benchmark / avalon

A 3D video game environment and benchmark designed from scratch for reinforcement learning research
https://generallyintelligent.com/avalon/
GNU General Public License v3.0
175 stars 16 forks source link

Following instructions in tutorial_custom_env fails on first step #30

Open dfm794 opened 1 year ago

dfm794 commented 1 year ago

I'm trying to walk through the instruction in the README.md file to create a custom environment. Summarizing, the steps I took were to copy avalon/datagen/godot to another location, per instructions rsync -r ./avalon/datagen/godot/ /sphere_eater --info=progress2 copy main.tscn and sphere.gd to /sphere_eater then launch the godot_editor, import project.godot

When this is done, a top level scene is already open - scene_root

Ignoring that, I open main.tscn and see the expected cube and sphere.

Next step is to run the environment. This however launches an avalon environment rather than the custom one, as project settings -> Application -> run -> Main Scene points to the scene_root.tscn file

Setting project settings -> Application -> run -> Main Scene to the main.tscn file however results in a fault at res://game/game_manager/game_manager.gd:46 - at function _init which is the line scene_root.add_child(node) line in this loop

for node in controlled_nodes: scene_root.add_child(node)

The error message is Attempt to call function 'add-child' in base 'null instance' on a null instance

It seems that scene_root is hardwired? Rather than using the node passed in to the init function as _root?

bai-generally-intelligent commented 1 year ago

Interesting, I haven't experienced this error befeore. @mx781 any idea?

mx781 commented 1 year ago

Hi @dfm794 - thanks for flagging, will look into it this week - sorry for the delay!

mx781 commented 1 year ago

@dfm794 super sorry for the long delay - I finally got a chance to look into the issue. Here's what missing in the tutorial:

1) You need to set is_teleporter_enabled to false in the config.json. The default must've changed since we made this tutorial 2) We updated the engine to require an explicit SpawnPoint, which is also missing from the example. You will need to add this to the main.tscn:

[node name="SpawnPoint" type="Spatial" parent="."]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )

At that point, hitting play should work. FYI the scene_root hard-coding is expected - main.tscn (or any level for that matter) is loaded in code, as opposed to by changing the project settings.

I will update the tutorial to fix this - thanks for reporting!

dfm794 commented 1 year ago

@mx781 Thanks for this information... I wanted to feedback back the results of making these mods and ask a followup question...

So, I went to my working directory which was in the same state as when the bug was filed. I made the two changes and had mixed results. That is, now when I ran the scene main.tscn. I was no longer placed in the 'avalon' environment..the window showed custom env as described in main. However, the crash happened as before.

I diffed the directory against avalon/datagen/godot and saw no unexpected diffs.

So..I started over from scratch just to be sure and encountered something strange. That is, if I started godot_editor in the copied avalon/datagen/godot directory, I got an error 'No config file given'. Clearly there is a config.json in the working directory. Specifying that config file with the --config-file flag resulted in a whole series of message of the form:

SCRIPT ERROR: Invalid get index '1' (on base: 'Array'). at: SimLoop.load_avalon_spec_from_args (res://game/scene_root/sim_loop.gd:111) SCRIPT ERROR: Invalid get index '0' (on base: 'Nil'). at: SimLoop._initialize (res://game/scene_root/sim_loop.gd:23) SCRIPT ERROR: Invalid call. Nonexistent function 'do_physics' in base 'Nil'

I could not find anyway out of this..eventually, I just launched godot_editor outside this directory and then I was able to import the project.godot file, open main.tscn and run the game.

However, when the game launches (and this was true previously as well), the scene is not still. The scene is rotating and the keyboard navigation controls, while they have an effect, it doesn't seem to me that it responds as expected.

I understand there are a bunch of items here perhaps not directly related to the bug, I apologize of that, but as far as I know there is no discord or other communication channel to get help here and I would like to get this to work to the point I can evaluate if Avalon is useful for doing my experiments. I'm getting the impression that there are a bunch of assumptions not in the docs/readmes that i'm stumbling over.

mx781 commented 1 year ago

Hm, I suspect the likely assumption that's not spelled out in our docs is that you want to be using the custom editor and binary, available here. python -m avalon.install_godot_binary will have downloaded them for you and they'll be correctly used for any training, but it won't have them as defaults system-wide, so that's likely the cause of the issues you're seeing. Can you confirm that the editor and binary you're launching are the ones from our custom build?

dfm794 commented 1 year ago

That requirement is spelled out. but of course it's worth validating. When i restarted this experiment, I ensured the latest by installing with the overwrite flag...

python -m avalon.install_godot_binary --overwrite 2023-06-04 19:36:42.400 | INFO | main:install_available_binaries_for_current_platform:130 - Installing editor and headless runner builds from https://github.com/Avalon-Benchmark/godot/releases/download 2023-06-04 19:36:42.406 | INFO | main:fetch_binary:92 - Downloading 3.4.4.avalon.0.9.3/linux-x11-editor.zip into avalon/bin/godot_editor 2023-06-04 19:36:44.471 | INFO | main:fetch_binary:92 - Downloading 3.4.4.avalon.0.9.3/linux-egl-editor.zip into avalon/bin/godot

Then validating with check install ython -m avalon.common.check_install Worker (PID=61068): process group: /home/dfm/dev/avalon-benchmark/avalon/avalon/datagen/godot/datagen.sh --thread_count=4 -U --input_pipe_path=/tmp/science/tmp/8a2398bc-f681-4f63-9e3c-14c147cc4247/godot.log.actions --output_pipe_path=/tmp/science/tmp/8a2398bc-f681-4f63-9e3c-14c147cc4247/godot.log.observations --resolution=96x96 --cuda-gpu-id=0 /tmp/science/data/d6cbc661-9f1f-418b-9e2a-cc0571a48460/be1c54b0-0fd0-4698-ad08-2f866b505133/config.json' &>> /tmp/science/tmp/8a2398bc-f681-4f63-9e3c-14c147cc4247/godot.log Worker (PID=61068): TO DEBUG RUN: /home/dfm/dev/avalon-benchmark/avalon/avalon/datagen/godot/datagen.sh --thread_count=4 -U --input_pipe_path=/tmp/science/data/d6cbc661-9f1f-418b-9e2a-cc0571a48460/be1c54b0-0fd0-4698-ad08-2f866b505133/actions.out --output_pipe_path=/tmp/debug_output --resolution=96x96 --cuda-gpu-id=0 /tmp/science/data/d6cbc661-9f1f-418b-9e2a-cc0571a48460/be1c54b0-0fd0-4698-ad08-2f866b505133/config.json avalon seems to be working properly!

I placed the godot binary at the head of my path as well as setting the GODOT_BINARY_PATH env var. echo $GODOT_BINARY_PATH /home/dfm/dev/avalon-benchmark/avalon/avalon/bin and 'which' validates that... which godot_editor /home/dfm/dev/avalon-benchmark/avalon/avalon/bin/godot_editor

finally, the output from the launch of the editor shows the correct version.. godot_editor Godot Engine v3.4.4.stable.avalon.e462a8744 - https://godotengine.org OpenGL ES 3.0 Renderer: llvmpipe (LLVM 12.0.0, 256 bits) OpenGL ES Batching: ON

I'm sure I'm missing something as I beleive you must be using it every day in your research. But I can't see what it is.

dfm794 commented 1 year ago

Some additional issues from this tutorial. I have moved on from Step1, to the remaining steps. Step2 works fine, no problem Step3 also works, with the caveat that these instructions, as others are missing the required calls to

from avalon.common.log_utils import configure_local_logger
configure_local_logger()

Step 4 does not work... In the tail end of the section "The Python Side" there are two issues 1) The instruction don't direct you to change the call to SphereEater env to update the observation_type and goal_evaluator unless of course I'm wrong and you should not update those?

env = SphereEaterEnv(
    config=create_base_benchmark_config(),
    observation_type=SphereEaterObservation,
    action_type=VRAction,
    goal_evaluator=SphereEaterGoalEvaluator()
)

2) This call to create the SphereEaterEnv object will fail with a message:

InvalidObservationType: Could not find requested feature 'is_sphere_eaten'. Available features are: ['target_head_position', 'target_left_hand_position',...

looking at the trace, this is due to calling a function in AvalonObservation, get_exposed_features() that does not include the feature added in the code in the tutorial instruction, 'is_sphere_eaten'.
File ~/anaconda3/envs/avalon-rst/lib/python3.9/site-packages/avalon/datagen/godot_env/observations.py:88, in GodotObservationContext.__init__(self, observation_type, is_space_flattened, available_features)
     86 self.is_space_flattened = is_space_flattened
     87 self.available_features = available_features
---> 88 self.selected_features = self._select_features()
     89 self.observation_space: Union[spaces.Dict, spaces.Space] = self._create_observation_space()
     90 self.flattened_observation_keys: List[str] = []

File ~/anaconda3/envs/avalon-rst/lib/python3.9/site-packages/avalon/datagen/godot_env/observations.py:106, in GodotObservationContext._select_features(self)
    104     continue
    105 elif type_and_dims is None and field not in OPTIONAL_FEATURES:
--> 106     raise InvalidObservationType(
    107         f"Could not find requested feature '{field}'. Available features are: {list(self.available_features)}"
    108     )

I tried the naive thing thing of adding a new definition of get_exposed_features() to the SphereEaterObservation class, but this complained about missing arguments to init().

mx781 commented 1 year ago

Okay, seems like something more intricate is going on. I will try to work my way through the tutorial from scratch to make sure any recent changes haven't broken any of the steps here. I'll try to get back to you by tomorrow.

mx781 commented 1 year ago

Apologies for more delays. Let's take it one issue at a time:

The scene is rotating and the keyboard navigation controls, while they have an effect, it doesn't seem to me that it responds as expected.

  • Can you:
  • a) share the output of git rev-parse HEAD and git diff?
  • b) record a short video (e.g. Loom) of what's going on? this sounds very bizarre given you have the right versions and are running on Linux, which seems to be the case.

The instruction don't direct you to change the call to SphereEater env to update the observation_type and goal_evaluator

This is correct - you shouldn't need to change this to follow this tutorial (and I was able to verify it works - does it not for you?). Your broader intuition is correct and skipping this detail was done for the simplicity of the tutorial, but I can see how it is misleading. The additional change you need to introduce is in avalon/datagen/godot/game/utils/observation_handler.gd#173, where you want to add this custom feature to the available features:

# ...
interactive_observation["is_sphere_eaten"] = [null, TYPE_INT, [1]]
return interactive_observation

Adding that should let it work with SphereEaterObservation.

Let me know if you hit any more snags - I will update the tut to include this as well.

dfm794 commented 1 year ago

@mx781 Sorry for the delay, I was traveling. I've rerun from scratch to validate all the steps. Let me address you questions and then enumerate changes that need to be made for the tutorial to work and finally open questions.

Apologies for more delays. Let's take it one issue at a time:

  • Launching and interacting with the built-in env in Godot: I was able to do this as expected after the tweaks above. When you say "the crash still happens", what crash are you referring to; what's the error message? Is this even still a problem?

    • If it is, could you start from a fresh clone / untainted main branch, rsync it to wherever you want to run it from, update config.json and main.tscn as described above and try again, using the avalon/bin/godot_editor editor? I realise this is re-iterating what you've probably been doing, but I want to clarify whether it's the absolute stock case that doesn't work for you.

I see I made my reply too complicated. Let me rephrase this. Step 1 works as expected with your edits. It seems that I needed to start from scratch, but also delete godot's files in ~/.cache for it to work. I was noting that I ran into a separate strange behavior, for which I'll open a separate bug. That is, if I start the godot_editor in the directory with the config.json file, it will exit with the error 'No config file given'. By this I mean running the directory we are instructed to copy as follows: Godot can be a bit fiddly when you edit files underneath it while the editor is running, so we find it best to copy the avalon/godot subdirectory to a new location: rsync -r ./avalon/datagen/godot/ /tmp/sphere_eater --info=progress2

Note that this seems to happen repeatedly and in other example, like the add task tutorial

The scene is rotating and the keyboard navigation controls, while they have an effect, it doesn't seem to me that it responds as expected.

  • Can you:

    • a) share the output of git rev-parse HEAD and git diff?
    • b) record a short video (e.g. Loom) of what's going on? this sounds very bizarre given you have the right versions and are running on Linux, which seems to be the case.

I have done further investigation on this behavior and it seems to be related to remote X display. If I launch godot_editor and run the game on my ubuntu 20.04 workstation, displaying with a locally attached display, everything works as expected. In my case I was using a windows 10 machine with the Xming X-client for convenience. X packets were tunneled over ssh with Putty. While godot is running on the same machine/same environment, when displayed remotely, the camera view slowly spins and angles down. You can see a video of that here

output of the commands you asked for:

git rev-parse HEAD 1c8f9fecb36e7169bf7c1cffc289f9fa37a5280e

git diff Note, this is empty, because the changes on only made to the copied 'godot' directory per earlier instructions So, I add here a diff of that copied directory with the origin directory in the repo:

diff avalon/datagen/godot ../../custom/sphere_eater
Common subdirectories: avalon/datagen/godot/android and ../../custom/sphere_eater/android
Common subdirectories: avalon/datagen/godot/benchmark and ../../custom/sphere_eater/benchmark
diff avalon/datagen/godot/config.json ../../custom/sphere_eater/config.json
13c13
<     "is_teleporter_enabled": true,
---
>     "is_teleporter_enabled": false,
Common subdirectories: avalon/datagen/godot/entities and ../../custom/sphere_eater/entities
Only in ../../custom/sphere_eater: exp1.ipynb
Only in ../../custom/sphere_eater: exp2.ipynb
Common subdirectories: avalon/datagen/godot/game and ../../custom/sphere_eater/game
Only in ../../custom/sphere_eater: .import
Only in ../../custom/sphere_eater: .ipynb_checkpoints
Common subdirectories: avalon/datagen/godot/items and ../../custom/sphere_eater/items
Only in ../../custom/sphere_eater: main.tscn
Common subdirectories: avalon/datagen/godot/materials and ../../custom/sphere_eater/materials
Common subdirectories: avalon/datagen/godot/navigation and ../../custom/sphere_eater/navigation
diff avalon/datagen/godot/project.godot ../../custom/sphere_eater/project.godot
1211,1214d1210
< 3d/bullet/ccd_allowed_penetration=0.04
< 3d/bullet/ccd_radius_factor=0.2
< 3d/bullet/ccd_only_on_demand=false
< 3d/bullet/substeps=0
1221a1218
> 3d/bullet/ccd_radius_factor=0.2
Common subdirectories: avalon/datagen/godot/recorder and ../../custom/sphere_eater/recorder
Common subdirectories: avalon/datagen/godot/scenery and ../../custom/sphere_eater/scenery
Common subdirectories: avalon/datagen/godot/scenes and ../../custom/sphere_eater/scenes
Only in ../../custom/sphere_eater: setpaths.sh
Common subdirectories: avalon/datagen/godot/shaders and ../../custom/sphere_eater/shaders
Common subdirectories: avalon/datagen/godot/terrain and ../../custom/sphere_eater/terrain
Common subdirectories: avalon/datagen/godot/testing and ../../custom/sphere_eater/testing
Common subdirectories: avalon/datagen/godot/utils and ../../custom/sphere_eater/utils

The instruction don't direct you to change the call to SphereEater env to update the observation_type and goal_evaluator

This is correct - you shouldn't need to change this to follow this tutorial (and I was able to verify it works - does it not for you?). Your broader intuition is correct and skipping this detail was done for the simplicity of the tutorial, but I can see how it is misleading. The additional change you need to introduce is in avalon/datagen/godot/game/utils/observation_handler.gd#173, where you want to add this custom feature to the available features:

# ...
interactive_observation["is_sphere_eaten"] = [null, TYPE_INT, [1]]
return interactive_observation

Adding that should let it work with SphereEaterObservation.

The above here is all dealing with Step 4 adding a goal evaluator. I'm a little confused here and also found another issue with the tutorial. First let's deal with the issue... A new function is added on the godot side, in observation_handler.gd func is_sphere_eaten(player: Player) -> bool: Then a print statement is added to game_manager.gd print("is sphere reached? %s" % observation_handler.is_sphere_reached(player)) However, this is calling is_sphere_reached, which is not what we defined. If I change this to is_sphere_eaten, the godot side works.

Now for the confusing part.. If you add the python code in 'the python part' of step 4, but never use it in the when the env is created, then you are never using that logic, you are just repeating step 3, with no new computations for goal progress, right?

Finally, if I do add it, and add the lines you suggest to get_available_features, it does not change the behavior. Here's my function and the stack trace

func get_available_features(player: Player) -> Dictionary:
    # TODO ideally we could return `.keys` but env bridge does something fancy with this dictionary
    var interactive_observation = get_interactive_observation(player, 0, 0, {}, false, false)

    if camera_controller.are_debug_views_enabled:
        interactive_observation[CONST.TOP_DOWN_RGBD_FEATURE] = [
            null,
            CONST.FAKE_TYPE_IMAGE,
            [camera_controller.resolution.y, camera_controller.resolution.x, 4]
        ]
        interactive_observation[CONST.ISOMETRIC_RGBD_FEATURE] = [
            null,
            CONST.FAKE_TYPE_IMAGE,
            [camera_controller.resolution.y, camera_controller.resolution.x, 4]
        ]
    interactive_observation["is_sphere_eaten"] = [null, TYPE_INT, [1]]
    return interactive_observation

InvalidObservationType Traceback (most recent call last) Cell In[6], line 1 ----> 1 env = SphereEaterEnv( 2 config=create_base_benchmark_config(), 3 observation_type=SphereEaterObservation, 4 action_type=VRAction, 5 goal_evaluator=SphereEaterGoalEvaluator() 6 ) 7 env.reset() 10 def random_env_step():

File ~/anaconda3/envs/avalon-pip-custom-sphere/lib/python3.9/site-packages/avalon/datagen/godot_env/godot_env.py:129, in GodotEnv.init(self, config, observation_type, action_type, goal_evaluator, gpu_id, is_logging_artifacts_on_error_to_s3, s3_bucket_name, is_error_log_checked_after_each_step, is_observation_space_flattened, is_godot_restarted_on_error, is_dev_flag_added, run_uuid) 121 self.process = InteractiveGodotProcess( 122 self.config, is_dev_flag_added=is_dev_flag_added, gpu_id=self.gpu_id, run_uuid=run_uuid 123 ) 124 self._bridge: GodotEnvBridge[ActionType] = GodotEnvBridge.build_by_starting_process( 125 self.process, 126 screen_resolution=(self.config.recording_options.resolution_x, self.config.recording_options.resolution_y), 127 ) --> 129 self.observation_context = GodotObservationContext( 130 observation_type=observation_type, 131 is_space_flattened=is_observation_space_flattened, 132 available_features=self._bridge.query_available_features(), 133 ) 134 self._bridge.select_and_cache_features(self.observation_context.selected_features) 135 self.seed_nicely(0)

File ~/anaconda3/envs/avalon-pip-custom-sphere/lib/python3.9/site-packages/avalon/datagen/godot_env/observations.py:88, in GodotObservationContext.init(self, observation_type, is_space_flattened, available_features) 86 self.is_space_flattened = is_space_flattened 87 self.available_features = available_features ---> 88 self.selected_features = self._select_features() 89 self.observation_space: Union[spaces.Dict, spaces.Space] = self._create_observation_space() 90 self.flattened_observation_keys: List[str] = []

File ~/anaconda3/envs/avalon-pip-custom-sphere/lib/python3.9/site-packages/avalon/datagen/godot_env/observations.py:106, in GodotObservationContext._select_features(self) 104 continue 105 elif type_and_dims is None and field not in OPTIONAL_FEATURES: --> 106 raise InvalidObservationType( 107 f"Could not find requested feature '{field}'. Available features are: {list(self.available_features)}" 108 ) 109 # TODO: check that the types line up 110 assert type_and_dims is not None

InvalidObservationType: Could not find requested feature 'is_sphere_eaten'. Available features are: ['target_head_position', 'target_left_hand_position', 'target_right_hand_position', 'target_head_rotation', 'target_left_hand_rotation', 'target_right_hand_rotation', 'physical_body_position', 'physical_head_position', 'physical_left_hand_position', 'physical_right_hand_position', 'physical_body_rotation', 'physical_head_rotation', 'physical_left_hand_rotation', 'physical_right_hand_rotation', 'physical_body_linear_velocity', 'physical_head_linear_velocity', 'physical_left_hand_linear_velocity', 'physical_right_hand_linear_velocity', 'physical_head_angular_velocity', 'physical_left_hand_angular_velocity', 'physical_right_hand_angular_velocity', 'physical_body_delta_position', 'physical_head_delta_position', 'physical_left_hand_delta_position', 'physical_right_hand_delta_position', 'physical_head_relative_position', 'physical_left_hand_relative_position', 'physical_right_hand_relative_position', 'physical_head_relative_rotation', 'physical_left_hand_relative_rotation', 'physical_right_hand_relative_rotation', 'physical_body_delta_rotation', 'physical_head_delta_rotation', 'physical_left_hand_delta_rotation', 'physical_right_hand_delta_rotation', 'physical_body_delta_linear_velocity', 'physical_head_delta_linear_velocity', 'physical_left_hand_delta_linear_velocity', 'physical_right_hand_delta_linear_velocity', 'physical_head_delta_angular_velocity', 'physical_left_hand_delta_angular_velocity', 'physical_right_hand_delta_angular_velocity', 'left_hand_thing_colliding_with_hand', 'left_hand_held_thing', 'right_hand_thing_colliding_with_hand', 'right_hand_held_thing', 'physical_body_kinetic_energy_expenditure', 'physical_body_potential_energy_expenditure', 'physical_head_potential_energy_expenditure', 'physical_left_hand_kinetic_energy_expenditure', 'physical_left_hand_potential_energy_expenditure', 'physical_right_hand_kinetic_energy_expenditure', 'physical_right_hand_potential_energy_expenditure', 'fall_damage', 'hit_points_lost_from_enemies', 'hit_points_gained_from_eating', 'reward', 'hit_points', 'is_dead', 'nearest_food_position', 'nearest_food_id', 'is_food_present_in_world', 'is_done', 'episode_id', 'frame_id', 'rgbd']

Let me know if you hit any more snags - I will update the tut to include this as well.

One last issue with the tutorial in Step 5. Step 5 assumes that you have altered the is_done logic, causing the episode to end when the sphere is encountered. But earlier in the tutorial, this was stated as optional. "You can also adjust the is_done logic if you like, but you can also simply override it on the Python side later." So for Step 5, it needs to be made explicit you need to go back and do that steps, I beleive.

So, to summarize what I see as changes to run the tutorial so far. Prior to Step 1 Install PyTorch after installing avalon via pip, as avalon.install_godot_binary requires it. Step 1 set is_teleporter_enabled to false in the config.json and add this to the main.tscn that's copied from the tutorial's repo [node name="SpawnPoint" type="Spatial" parent="."] transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )

after opening the editor, open main.tscn, as scene defaults to something else.

Step 3 add the following after first imports from avalon.common.log_utils import configure_local_logger configure_local_logger()

Step 4 Change print statement from print("is sphere reached? %s" % observation_handler.is_sphere_reached(player)) to print("is sphere reached? %s" % observation_handler.is_sphere_eaten(player))

Thanks for you help. I do think avalon could be useful for my work and I'm quite a ways into the add task tutorial.

mx781 commented 1 year ago

Thanks for summarizing all the changes to get things working!

Can you confirm you were able to complete the tutorial with those changes applied? Or is there still something outstanding; it was not quite clear from your last message.

dfm794 commented 1 year ago

Thanks for summarizing all the changes to get things working!

Can you confirm you were able to complete the tutorial with those changes applied? Or is there still something outstanding; it was not quite clear from your last message.

Sure...yes, there are still three issues...one major and two minor ones. Let's walk through them.

Major: Adding feature 'is_sphere_eaten' fails you had pointed out that in order to use the SphereEaterObservation class and the SphereEaterGoalEvaluator class in the SphereEaterEnv constructor, one had to add two lines to avalon/datagen/godot/game/utils/observation_handler.gd#173

    interactive_observation["is_sphere_eaten"] = [null, TYPE_INT, [1]]
    return interactive_observation

When I did this, it did not work, as I wrote above. After debugging I discovered the issue. That is, the game code that is running when using godot_editor is not the same as is used when running godot via the gym interface. Why?

When you follow the tutorial instructions, you copy the game code via rsync

rsync -r ./avalon/datagen/godot/ /tmp/sphere_eater --info=progress2

Then the tutorial instructs you to modify gdscript code here. When you run the editor, you open a specific project and when you run from within the editor the gdscript game code executed is the code from this project directory.

However, when you create an environment and run the code from python via the gym interface, the game code that is running is whatever was installed (in my case via pip install avalon). This is why I got the error message about 'is_sphere_eaten' not being in the available features, even though I made the edits per your instructions. When I modified the installed game code, then it worked and I could see these features in the debugger.

This seems like a big deal Following the tutorial, one would believe one is always using the code modified in the copied directory, when in fact different game code is running depending on whether one is running interactive in godot_editor or via gym apis in python.

So,(if there is a way to do so) the tutorial code needs to have godot run the game code that one is instructed to copy and modify in the instructions -and if that is not possible, the instructions have to change to modify the code in both places.

Ok, that's the major issue. Now the two minor ones.

Minor: is_done code needs to change for step 5 This I already mentioned - the instructions in step4 tell you modifying is_done() is optional, but step5 assumes you have done so.

Minor: agent is not facing sphere in step 5 The instructions state "The agent spawns facing the sphere, so all it takes to look smart here is to march straight forward:" However, in my case - and I've not modified any code besides what one is instructed to do in the tutorial - the agent is not directly facing the sphere when it spawns - the sphere is slightly off to the left. So when 'forward' actions are given, the agent passes by the left side of the sphere.

That I think is the last three pieces to have the tutorial work 'out of the box' from the instructions. In full disclosure, I did not try Step 6

mx781 commented 1 year ago

Thanks again for the thoroughness on these comments!

The divergence of the two directories is indeed really confusing, thanks for highlighting it, I now realize this is a very sneaky distinction for the user. In essence, when the tutorial says "Godot can be a bit fiddly when you edit files underneath it while the editor is running", this should've really said something like "if you want to play with the environment in the editor itself, you should run rsync ... && godot /tmp/path instead of running it directly, because it's specifically opening files in the editor that creates cache files etc that can cause unexpected behavior when running it using the GodotEnv.

It sounds like the only bit you weren't able to debug through is the last piece where you say "the sphere is slightly off to the left". Is this also happening when you're not using the remote X display?

dfm794 commented 1 year ago

Thanks again for the thoroughness on these comments!

The divergence of the two directories is indeed really confusing, thanks for highlighting it, I now realize this is a very sneaky >distinction for the user. In essence, when the tutorial says "Godot can be a bit fiddly when you edit files underneath it while >the editor is running", this should've really said something like "if you want to play with the environment in the editor itself, >you should run rsync ... && godot /tmp/path instead of running it directly, because it's specifically opening files in the editor that creates cache files etc that can cause unexpected behavior when running it using the GodotEnv.

It sounds like the only bit you weren't able to debug through is the last piece where you say "the sphere is slightly off to the left". Is this also happening when you're not using the remote X display?

Thanks for the kind words... Regarding the last comment on the sphere. I was mistaken as to what is going on here...the real issues is that the environment was not reset between episodes via env.reset()

This came about because as I go through the tutorial, I'm copy-pasting from the tutorial dot md file to a jupyter notebook. The previous execution at the end of Step 4 is repeating what's in Step 2, which a change to the environment creation to use the SphereEater classes.


env = SphereEaterEnv(
    config=create_base_benchmark_config(),
    observation_type=SphereEaterObservation,
    action_type=VRAction,
    goal_evaluator=SphereEaterGoalEvaluator()
)
env.reset()

def random_env_step():
    action = env.action_space.sample()
    obs, reward, done, info = env.step(action)
    if done:
        env.reset()
    return obs

observations = [random_env_step() for _ in range(50)]
display_video(observations)

Note that this code does not do an env.reset() after the episode.

Then Step 5's code in the tutorial does not reset before execution...

import attr
from avalon.datagen.env_helper import get_null_vr_action

base_action = get_null_vr_action()
move_forward = attr.evolve(base_action, head_z=-1)

steps = 25
actions = [move_forward] * steps
observations = []
for step in range(steps):
    obs, goal_progress = env.act(actions[step])
    if goal_progress.is_done:
        print("Done!")
        env.reset()
        break
    observations.append(obs)
display_video(observations)

So net net - the Sphere is just wherever it was at the end of the 'random_env_step()' executed in the previous cell.

If I just insert env.reset() early, all works as expected.

base_action = get_null_vr_action()
move_forward = attr.evolve(base_action, head_z=-1)
env.reset()

steps = 25

So, I think it would be useful to add this reset call to the tutorial code.

mx781 commented 1 year ago

Alright, great! Will collate all these changes into a PR and close when merged.

dfm794 commented 1 year ago

Alright, great! Will collate all these changes into a PR and close when merged.

Great. If you'd like me to run though it again post the PR to validate, I can do that. Just let me know