Si alguna vez imaginaste controlar a Mario con tu propio cuerpo en lugar de un control tradicional, este proyecto te va a encantar.
La MaixCAM, una potente placa de desarrollo con arquitectura RISC-V e inteligencia artificial integrada, será el cerebro detrás de esta idea. Gracias a su cámara y capacidades de visión por computadora, puede detectar movimientos en tiempo real y convertirlos en acciones dentro de un emulador de Super Mario Bros.
¿Saltar en la vida real? Mario salta. ¿Inclinarte? Mario se mueve. Así de simple… y así de divertido. Este proyecto combina tecnología, creatividad y un toque de nostalgia gamer para transformar la forma en que interactuamos con los videojuegos clásicos y hoy te enseño a cómo hacerlo.
Introducción
Antes de empezar con este proyecto, se deben de comprender ciertos temas de importancia; esto ayudará a saber cómo funciona el sistema y si lo deseamos, hacer algunos cambios en este.
¿Qué es la MaixCAM?
Es una placa de desarrollo basada en arquitectura RISC-V que combina un procesador potente, un acelerador de IA (NPU) y periféricos como cámara, pantalla y conectividad inalámbrica, todo en un único módulo compacto. Esto la hace ideal para crear dispositivos con capacidades de visión inteligente y procesamiento de IA sin depender de un ordenador externo.

Componentes Clave.
| Componente | Descripción | Función principal |
|---|---|---|
| CPU RISC-V (C906 ~1 GHz) | Procesador principal basado en arquitectura RISC-V | Ejecuta el sistema operativo y la lógica del programa |
| NPU (~1 TOPS) | Unidad de Procesamiento Neuronal integrada | Acelera modelos de IA como detección y clasificación de objetos |
| Memoria RAM | Memoria integrada de alta velocidad | Manejo de datos, buffers de imagen y ejecución de modelos |
| Almacenamiento (MicroSD/TF) | Soporte para tarjeta externa | Guarda modelos, imágenes, videos y programas |
| Cámara (hasta ~5MP) | Sensor de imagen compatible | Captura video e imágenes para visión artificial |
| Pantalla (~2.3” táctil) | Display integrado | Visualización de resultados, interfaz gráfica y debugging |
| Wi-Fi 6 | Conectividad inalámbrica | Comunicación en red y envío de datos a la nube |
| Bluetooth LE | Comunicación inalámbrica de bajo consumo | Conexión con dispositivos móviles o periféricos |
| GPIO / Interfaces (UART, I2C, SPI, etc.) | Pines de expansión | Conexión de sensores, actuadores y módulos externos |
Usos.
La placa se emplea principalmente para desarrollar e implementar aplicaciones de inteligencia artificial en el borde (edge AI), es decir, que el procesamiento de IA se hace directamente en el dispositivo y no en la nube. Algunos ejemplos incluyen:
- Reconocimiento y detección de objetos.
- Clasificación o análisis de imágenes en tiempo real.
- Proyectos de IoT inteligentes.
- Robótica con visión integrada.
- Sistemas de seguridad con IA.
¿Qué es YOLO?
YOLO Pose es una versión de la familia de modelos YOLO (You Only Look Once) diseñada para hacer estimación de pose humana en tiempo real. En lugar de solo detectar objetos (como personas, autos, etc.), YOLO Pose detecta personas y además localiza puntos clave del cuerpo (keypoints), como:
- Cabeza.
- Ojos.
- Manos.
- Rodillas.
- Pies.
El programa que se usará es el de “pose”, que es una técnica de visión por computadora que identifica la posición de articulaciones del cuerpo humano en una imagen o video, generando un “esqueleto digital” sobre la persona detectada.
Entre los usos que les puedes dar está:
- Control de videojuegos con el cuerpo.
- Análisis de ejercicios y postura.
- Seguimiento de movimientos en tiempo real.
- Interacción humano-máquina.
- Motion capture básico sin trajes especiales.
Ahora que ya conocemos qué es la MaixCAM, sus componentes clave, así como una breve explicación de cómo funciona, además del conocimiento de los programas YOLOS, podemos poner manos a la obra para empezar a jugar con Mario.
Materiales
- MaixCAM Placa de Desarrollo AI RISCV.
- Memoria Micro SD Kingston 16, 32 y 64 GB Clase 10.
- Adaptador de Micro SD a USB 2.0.
- Monitor de Pantalla.
- PC / Ordenador.
- MaixVision.
- BalenaEtcher.
- Mesen.
- Y a ti…..
Requerimientos Previos.
Para usar la MaixCAM debemos cargar la imagen de su sistema; para ello podemos ver la entrada de nuestro blog Mis Primeros Pasos con la MaixCAM AI RISCV, se usará el software de BalenaEtcher para el grabado y MaixVision para el código en Python; sin embargo, para el emulador usaremos Mesen.
Instalar Mesen.
La instalación de este emulador es muy sencilla, primero debemos ir a su página oficial Mesen, se abrirá la siguiente página:

Después de seleccionar el sistema operativo, tendremos la carpeta ZIP del programa, la cual se debe descomprimir; dentro de ella se tendrá un archivo .exe que se instalará como administrador.

Se abrirá la ventana de la imagen 3; en ella solo configuraremos los controles de entrada como flechas y mando; realmente esto no tiene tanta importancia, ya que se puede cambiar desde el juego mismo, sin embargo, para practicidad activemos la casilla de “Crear acceso directo en el escritorio”.
Una vez terminado, solo hay que dar click en “CONFIRMAR”, y se abrirá el emulador teniendo el software ya instalado, como dato interesante, también se pueden cargar juegos y usarlos desde ese programa.

Código de Programación
El código se realizó desde el software MaixVision, e igual se ejecuta desde ese IDE, a continuación, lo dejamos para que lo uses.
"""
========================================================================================================================
Nombre del archivo: Version_Gama.py
Proyecto: UNIT Electronics Blog - Juega con la IA y….¿Mario?
Área: Desarrollo
Autor: Gonzalo González
Fecha: 19/02/2025
========================================================================================================================
Descripción:
Este código usa el módelo de IA YOLO Pose para detectar movimientos del cuerpo humano y clasificarlos para un Emulador de Juegos, estos datos se envían a través del puerto UART para funcionar como teclas HDI.
========================================================================================================================
Desarrollado para UNIT Electronics / Desarrollo / Blog UNIT Electronics
========================================================================================================================
"""
from maix import camera, display, image, nn, app, time, hid
SHOW_IMG = True
model_path = "/root/models/yolo11n_pose.mud"
KEY_LEFT = 80
KEY_RIGHT = 79
KEY_UP = 22
KEY_DOWN = 81
KEY_ATTACK = 29
KEY_RUN = 225
class PoseRecognizer:
POSE_LEFT = 1
POSE_RIGHT = 2
POSE_UP = 3
POSE_DOWN = 4
POSE_ATTACK = 5
POSE_RUN = 6
def __init__(self, img_w, img_h):
self.img_w = img_w
self.img_h = img_h
# ========= AUTOCALIBRACIÓN =========
self.calibrating = True
self.calibration_start = time.ticks_ms()
self.calibration_samples = []
self.neutral_mid_y = 0
# ========= MOVIMIENTO L/R =========
self.lr_state = 0
self.lr_enter = 0.18
self.lr_exit = 0.10
# ========= SALTO PRO =========
self.jump_threshold = 5
self.last_jump_time = 0
self.jump_hold_until = 0
self.base_jump_hold = 380
self.max_jump_hold = 650
self.y_points = []
# ========= DOWN PROFESIONAL =========
self.down_state = False
self.down_enter = 35
self.down_exit = 20
self.down_hold_until = 0
self.min_down_hold = 120
# ========= RUN =========
self.wrist_history = []
# ========= ESTABILIDAD =========
self.pose_buffer = {}
self.pose_stable_frames = 1
# ===============================
# FILTRO ESTABLE
# ===============================
def stabilize_pose(self, poses):
stable = []
for p in poses:
if p not in self.pose_buffer:
self.pose_buffer[p] = 1
else:
self.pose_buffer[p] += 1
for p in list(self.pose_buffer.keys()):
if p not in poses:
self.pose_buffer[p] = 0
for p, count in self.pose_buffer.items():
if count >= self.pose_stable_frames:
stable.append(p)
return stable
# ===============================
# DETECCIÓN PRINCIPAL
# ===============================
def run(self, obj):
res = []
shoulder_l = [obj.points[10], obj.points[11]]
shoulder_r = [obj.points[12], obj.points[13]]
wrist_l = [obj.points[18], obj.points[19]]
wrist_r = [obj.points[20], obj.points[21]]
hip_l = [obj.points[22], obj.points[23]]
hip_r = [obj.points[24], obj.points[25]]
shoulder_len = abs(shoulder_l[0] - shoulder_r[0])
if shoulder_len < 20:
return False, res
shoulder_mid = [
(shoulder_l[0] + shoulder_r[0]) * 0.5,
(shoulder_l[1] + shoulder_r[1]) * 0.5
]
hip_mid = [
(hip_l[0] + hip_r[0]) * 0.5,
(hip_l[1] + hip_r[1]) * 0.5
]
mid_y = (shoulder_mid[1] + hip_mid[1]) * 0.5
# ========= AUTOCALIBRACIÓN =========
if self.calibrating:
self.calibration_samples.append(mid_y)
if time.ticks_ms() - self.calibration_start > 2000:
self.neutral_mid_y = sum(self.calibration_samples) / len(self.calibration_samples)
self.calibrating = False
print("CALIBRADO ✓")
return False, []
# ========= IZQUIERDA / DERECHA =========
v = (shoulder_mid[0] - hip_mid[0]) / shoulder_len
if self.lr_state == 0:
if v > self.lr_enter:
self.lr_state = 1
elif v < -self.lr_enter:
self.lr_state = -1
elif self.lr_state == 1 and v < self.lr_exit:
self.lr_state = 0
elif self.lr_state == -1 and v > -self.lr_exit:
self.lr_state = 0
if self.lr_state == 1:
res.append(self.POSE_RIGHT)
elif self.lr_state == -1:
res.append(self.POSE_LEFT)
# ========= DOWN PROFESIONAL =========
now = time.ticks_ms()
delta_down = mid_y - self.neutral_mid_y
if not self.down_state and delta_down > self.down_enter:
self.down_state = True
self.down_hold_until = now + self.min_down_hold
elif self.down_state and delta_down < self.down_exit:
if now > self.down_hold_until:
self.down_state = False
if self.down_state:
res.append(self.POSE_DOWN)
# ========= SALTO DINÁMICO LARGO =========
self.y_points.append(mid_y)
if len(self.y_points) > 4:
self.y_points.pop(0)
if len(self.y_points) >= 3:
velocity = (self.y_points[-3] - self.y_points[-2]) + \
(self.y_points[-2] - self.y_points[-1])
if velocity > self.jump_threshold and now - self.last_jump_time > 600:
hold = self.base_jump_hold + int((velocity ** 1.1) * 25)
hold = min(hold, self.max_jump_hold)
self.jump_hold_until = now + hold
self.last_jump_time = now
if now < self.jump_hold_until:
res.append(self.POSE_UP)
# ========= RUN =========
wrist_diff = wrist_l[0] - wrist_r[0]
self.wrist_history.append(wrist_diff)
if len(self.wrist_history) > 6:
self.wrist_history.pop(0)
if len(self.wrist_history) >= 4:
changes = 0
for i in range(1, len(self.wrist_history)):
if self.wrist_history[i] * self.wrist_history[i-1] < 0:
changes += 1
if changes >= 2:
res.append(self.POSE_RUN)
# ========= ATTACK =========
if wrist_l[1] < shoulder_l[1] or wrist_r[1] < shoulder_r[1]:
res.append(self.POSE_ATTACK)
return True, res
# ===============================
# HID
# ===============================
last_keys = set()
def send_keys(poses, keyboard):
global last_keys
key_map = {
PoseRecognizer.POSE_LEFT: KEY_LEFT,
PoseRecognizer.POSE_RIGHT: KEY_RIGHT,
PoseRecognizer.POSE_UP: KEY_UP,
PoseRecognizer.POSE_DOWN: KEY_DOWN,
PoseRecognizer.POSE_ATTACK: KEY_ATTACK,
PoseRecognizer.POSE_RUN: KEY_RUN,
}
current = set()
for p in poses:
current.add(key_map[p])
if current == last_keys:
return
cmd = [0, 0] + list(current)
cmd += [0] * (8 - len(cmd))
keyboard.write(cmd)
last_keys = current
# ===============================
# OVERLAY VISUAL
# ===============================
def draw_overlay(img, recognizer, poses):
if recognizer.calibrating:
img.draw_string(10, 10, "CALIBRANDO...", image.COLOR_RED, scale=2)
return
y = 10
labels = {
PoseRecognizer.POSE_LEFT: ("LEFT", image.COLOR_BLUE),
PoseRecognizer.POSE_RIGHT: ("RIGHT", image.COLOR_BLUE),
PoseRecognizer.POSE_UP: ("JUMP", image.COLOR_GREEN),
PoseRecognizer.POSE_DOWN: ("DOWN", image.COLOR_YELLOW),
PoseRecognizer.POSE_ATTACK: ("ATTACK", image.COLOR_RED),
PoseRecognizer.POSE_RUN: ("RUN", image.COLOR_PURPLE),
}
for p in poses:
text, color = labels[p]
img.draw_string(10, y, text, color, scale=2)
y += 25
# ===============================
# MAIN
# ===============================
def main():
detector = nn.YOLO11(model=model_path, dual_buff=True)
cam = camera.Camera(detector.input_width(),
detector.input_height(),
detector.input_format())
cam.hmirror(1)
recognizer = PoseRecognizer(cam.width(), cam.height())
keyboard = hid.Hid(hid.DeviceType.DEVICE_KEYBOARD)
disp = display.Display()
while not app.need_exit():
img = cam.read()
objs = detector.detect(img, conf_th=0.45, iou_th=0.45)
poses = []
if len(objs) > 0:
obj = max(objs, key=lambda o: o.w * o.h)
valid, poses = recognizer.run(obj)
if valid:
poses = recognizer.stabilize_pose(poses)
send_keys(poses, keyboard)
detector.draw_pose(img, obj.points, 4, image.COLOR_GREEN)
else:
send_keys([], keyboard)
draw_overlay(img, recognizer, poses)
disp.show(img)
if __name__ == '__main__':
main()
Con este código podrás:
- Mover a Mario a la izquierda/derecha inclinando tu cuerpo.
- Saltar.
- Agacharte (debes agacharte por completo).
- Correr (balanceando los brazos).
- Atacar (levantando una mano).
Funcionamiento
Ya tenemos todo para hacer funcionar nuestra IA con Mario, así que empecemos a configurar todo.
Configuración del Emulador Mesen.
Como primer paso, deberás descargar el juego de Super Mario Bros en formato .NES en cualquier sitio web, posteriormente, se tendrá que abrir o cargar, para ello debes presionar el apartado de “File > Open” y buscarlo en donde esté descargado, una vez que lo hayas hecho, hay que configurar las teclas o botones.
Nos dirigiremos a “Settings > Input”, se abrirá una ventana como está:

Una vez abierta la ventana, daremos click en “Input” de la parte superior del menú:

Ahora click en “Setup”:

Abrirá una ventana para la selección de teclas, la tendremos que configurar de la siguiente manera:

Se deben seleccionar las teclas, los botones A/B y la parte media; para hacerlo, solo se da click en cada recuadro y posteriormente presionamos la tecla, muy sencillo, una vez hechos los cambios, se dará click en “Ok” en todas las ventanas hasta dejar únicamente el juego.
Configuración de la MaixCAM.
Teniendo el Emulador configurado hay que hacer algo parecido a la MaixCAM, hay que hacer que está funcione como un periférico de PC, es decir, como un componente HID, esto es muy rápido.
Hay que conectarla a nuestra PC a través de su cable USB C, una vez que corra la imagen del sistema, nos iremos a “Settings > USB Settinggs” en este apartado tendremos que bajar hasta que veamos un botón titulado “HID Mouse” ese no lo toques o actives; vamos a activar el que está al lado, “HID Keyboard”, así como se ve en la siguiente imagen:

Como recordatorio, la MaixCAM y el ordenador/PC que usaremos deben estar conectados a la misma red local; de lo contrario, no funcionará correctamente el programa.
Prueba
Abriremos el software MaixVision, y crearemos un nuevo archivo, copiaremos el código y conectaremos la MaixCAM a través de la IP que tenga:

Una vez conectados, solo daremos click en “RUN”, ahora bien, una vez que cargué el programa, primero se calibrará por lo que el jugador ya debe estar posicionado a una distancia de 1.8 a 2 metros de la MaixCAM, además que estará deberá estar a una altura considerable para que tenga buen campo de visión; otro punto importante es que no olvides enfocar bien lente de lo contrario tendrás una imagen borrosa y nada nítida, por último no olvides seleccionar la ventana del Emulador de lo contrario no se mandarán los comandos detectados.

Así se verá el software de MaixCAM cuando hagamos los gestos y se reconozcan, imprimiendo en pantalla lo que se esté detectando, mientras que del otro lado tendremos a Mario interactuando como si lo estuviéramos controlando con un teclado físico:

¿Quieres ver cómo funciona? Te dejo un video demostrativo:
Conclusiones
Este proyecto demuestra que la inteligencia artificial ya no es una tecnología lejana o exclusiva de grandes industrias, sino una herramienta accesible que podemos integrar en proyectos creativos y divertidos. Al utilizar la MaixCAM con arquitectura RISC-V y el modelo YOLO Pose, logramos transformar movimientos reales del cuerpo en acciones dentro de un videojuego clásico como Super Mario Bros.
La combinación de visión por computadora, procesamiento en el borde (Edge AI) y emulación nos permitió crear una nueva forma de interacción humano-máquina, donde el teclado tradicional es reemplazado por gestos naturales. Esto no solo abre la puerta a nuevas experiencias de juego, sino también a aplicaciones en educación, rehabilitación, deporte y robótica.
Además, el desarrollo mostró la importancia de comprender tanto el hardware como el software: desde la configuración del emulador y el modo HID, hasta la calibración y optimización del reconocimiento de poses para reducir latencia y mejorar la experiencia de usuario.
En conclusión, integrar IA con creatividad y nostalgia gamer nos permite reinventar la manera en que interactuamos con la tecnología. Y lo mejor de todo: este es solo el comienzo de lo que se puede lograr cuando combinamos imaginación, código y un poco de espíritu maker.

