Creando un Entorno Personalizado en Gym: Problema y Solución

Desarrollado por Yel Martínez · Digital Strategist & Technologist

Entorno Gym personalizado en Python: MarsExplorerEnv y aprendizaje por refuerzo

Cómo definir un entorno personalizado en OpenAI Gym, solucionar los errores AttributeError: metadata y NotImplementedError: render(), y registrarlo correctamente con gym.envs.registration.register() para usarlo con gym.make().

Python · OpenAI Gym Reinforcement Learning metadata · render() · register() Entorno personalizado
Python · OpenAI Gym · RL class MarsExplorerEnv(gym.Env): metadata = {'render.modes': ['human']} def step(self, action): reward = 10 if done else -5 def render(self, mode='human'): print(f"Estado: {self.state}") register( id='MarsExplorer-v0', entry_point='__main__:...' )
01 Problema metadata · render
02 metadata Atributo de clase
03 render() Validación de modo
04 register() gym.make() listo
Qué hace este código

MarsExplorerEnv: entorno de aprendizaje por refuerzo en OpenAI Gym

El código define un entorno personalizado en Gym donde un agente debe moverse entre posiciones (0–9) para llegar a una zona de interés evitando zonas peligrosas. Es un ejemplo directo de aprendizaje por refuerzo (RL) aplicado a exploración autónoma — el mismo tipo de lógica que usan los rovers en entornos desconocidos.

Este tipo de entornos personalizados se usa para entrenar agentes en toma de decisiones secuenciales: el agente recibe una recompensa positiva al llegar a la zona de interés, negativa si entra en una zona peligrosa y neutra en el resto de estados. La política se aprende iterando sobre episodios.

10 posiciones en el espacio de observación — el agente se mueve entre estados 0 y 9
2 acciones posibles — izquierda (0) o derecha (1) en cada paso del episodio
3 zonas peligrosas (2, 4, 7) con recompensa negativa de −5 al pisarlas
+10 recompensa al alcanzar la zona de interés en posición 9 — condición de fin de episodio
Los errores

Dos errores concretos: metadata y registro del entorno

Al ejecutar el entorno sin las correcciones, Gym lanza dos errores que bloquean la ejecución. Ambos tienen causa y solución directa.

AttributeError: 'MarsExplorerEnv' object has no attribute 'metadata'

Gym espera que cualquier subclase de gym.Env tenga el atributo de clase metadata con los modos de renderizado soportados. Si no está definido, el método render() lanza un AttributeError al intentar leerlo. La solución es añadir metadata = {'render.modes': ['human']} directamente en la definición de la clase.

gym.make() no reconoce el entorno personalizado

Cuando se llama a gym.make('MarsExplorer-v0') sin haber registrado el entorno previamente, Gym devuelve un error porque no encuentra el ID en su registro interno. La solución es llamar a gym.envs.registration.register() con el id y el entry_point antes de cualquier llamada a gym.make().

Las correcciones

Tres cambios, dos errores resueltos: metadata, render() y register()

Cada corrección es puntual y no requiere restructurar el código — se añaden en los lugares exactos donde Gym los espera.

Corrección 1 — Añadir metadata como atributo de clase

Añadir metadata = {'render.modes': ['human']} justo después de la declaración de la clase, antes de __init__. Es un atributo de clase, no de instancia — va al nivel de la clase, no dentro de ningún método. Gym lo lee antes de instanciar el objeto.

AttributeError
Corrección 2 — Validar el modo en render()

Implementar render(self, mode='human') con la validación explícita: si mode != 'human', lanzar NotImplementedError. Esto comunica claramente qué modos están soportados en lugar de dejar que Gym falle de forma críptica en otro punto de la ejecución.

NotImplementedError
Corrección 3 — Registrar con register() antes de gym.make()

Llamar a gym.envs.registration.register(id='MarsExplorer-v0', entry_point='__main__:MarsExplorerEnv') antes de gym.make(). El entry_point debe apuntar al módulo donde está la clase — si el código está en un archivo externo, sustituir __main__ por el nombre del módulo.

gym.make()
Código corregido — recurso para copiar

Implementación completa: entorno, registro y bucle de episodios

Python completo — MarsExplorerEnv corregido

import gym import numpy as np import random from gym.envs.registration import register class MarsExplorerEnv(gym.Env): metadata = {'render.modes': ['human']} # Corrección 1 def __init__(self): super().__init__() self.action_space = gym.spaces.Discrete(2) self.observation_space = gym.spaces.Discrete(10) self.state = 5 self.interest_zone = 9 self.danger_zones = [2, 4, 7] def step(self, action): if action == 0: self.state = max(0, self.state - 1) else: self.state = min(self.observation_space.n - 1, self.state + 1) done = self.state == self.interest_zone reward = 10 if done else (-5 if self.state in self.danger_zones else 0) return self.state, reward, done, {} def reset(self): self.state = 5 return self.state def render(self, mode='human'): # Corrección 2 if mode != 'human': raise NotImplementedError("Solo se admite el modo 'human'") print(f"Estado actual: {self.state}") # Corrección 3 — registrar antes de gym.make() register( id='MarsExplorer-v0', entry_point='__main__:MarsExplorerEnv', ) env = gym.make('MarsExplorer-v0') episodes = 10 for episode in range(1, episodes + 1): state = env.reset() done = False total_reward = 0 while not done: action = env.action_space.sample() state, reward, done, info = env.step(action) total_reward += reward env.render() print(f"Episodio: {episode}, Recompensa total: {total_reward}")

Consejos de implementación

Cuatro buenas prácticas para entornos Gym personalizados

Define siempre metadata en la clase
Cualquier subclase de gym.Env debe declarar metadata = {'render.modes': ['human']} aunque no uses todos los modos. Es un contrato que Gym espera respetar y cuya ausencia genera errores silenciosos en versiones más recientes de la librería.
Registra el entorno antes de gym.make()
La llamada a register() debe ejecutarse antes de cualquier gym.make(). Si el entorno está en un módulo externo, asegúrate de que ese módulo se importa antes de hacer el make, o Gym no encontrará el entry_point en el registro.
Usa seed() para experimentos reproducibles
Implementar el método seed(self, seed=None) en tu entorno y llamar a env.seed(42) antes del bucle de episodios garantiza que los resultados sean reproducibles entre ejecuciones — imprescindible para comparar políticas o depurar comportamientos del agente.
Prueba con env.action_space.sample() antes de tu política
Usar acciones aleatorias con sample() es la forma más rápida de verificar que el entorno funciona correctamente antes de integrar una política de RL más avanzada. Si el bucle de episodios termina con done=True de forma consistente, el entorno está correctamente implementado.
Solución desarrollada por Yel Martínez

Yel Martínez, Digital Strategist & Technologist — especialista en Python, automatización y ciencia de datos aplicada a proyectos técnicos y sostenibilidad digital. Para proyectos de desarrollo Python, automatización o IA aplicada: contacto.

Scroll al inicio