Documentos de Asset Core

Documentación del motor de estado mundial determinista y referencias de API.

Documentos de Decision Gate

Célula de Robótica: Clasificación por Transportador con Estado Determinista

Un solo robot rastrea artículos en un transportador en movimiento, reconcilia actualizaciones de sensores y realiza la recogida/colocación en contenedores de cuadrícula con reproducción determinista.

Si estás leyendo esto en frío, este es un flujo de trabajo determinista de recogida y colocación con corrección de sensores y recuperación de conflictos. Cada paso es un compromiso auditable con metadatos y claves de idempotencia, por lo que puedes reproducir o diagnosticar ejecuciones sin conjeturas.

El Problema: Cuando los Sensores y los Planificadores No Coinciden

En la robótica de producción, su sistema de visión informa una posición, su encoder dice otra, y su planificador asume una tercera. Cuando una recogida falla, tiene tres registros conflictivos y ninguna fuente única de verdad. ¿Fue esto:

  • ¿Deriva del sensor durante el movimiento de la cinta transportadora?
  • ¿Una posición obsoleta en el planificador de movimientos?
  • ¿Una condición de carrera entre la actualización de visión y el comando de selección?

Sin un estado determinista, la depuración se convierte en arqueología. Con AssetCore, cada cambio de estado es un commit con marca de tiempo y auditable. Las correcciones de sensores, las actualizaciones del planificador y los comandos de ejecución fluyen a través de un registro autoritativo. Cuando algo falla, reproduces la secuencia exacta y ves lo que ocurrió, sin conjeturas.

Este escenario demuestra patrones de calidad de producción: seguimiento continuo de cintas transportadoras, reconciliación de sensores basada en visión, detección de conflictos con replanteamiento automático y reproducción determinista para depuración.

Por qué esto es importante

Las pilas de robótica fallan cuando los planificadores, sensores y la ejecución no están de acuerdo sobre la verdad fundamental. Asset Core hace que el estado sea autoritativo al tratar cada cambio como una transacción determinista y reproducible.

Modelo del sistema

  • Transportador: colocaciones continuas 1D con coordenadas de punto fijo.
  • Bins: contenedor de cuadrícula que impone reglas de ocupación.
  • Gripper: contenedor de ranura por lo que los movimientos son determinísticos en las ranuras.
  • Pose del robot: contenedor 2D continuo para posición y rotación.
  • Clases y formas: registradas para que se apliquen las reglas de colisión y colocación.
  • Unidades: mm (fixed_point_scale=1000).

Flujo de trabajo en un vistazo

  1. Configuración - Registrar clases/formas y crear contenedores.
  2. Semilla - Crear la pose del robot, el ítem entrante y el contenedor bloqueado.
  3. Avance del Transportador - Mover el ítem según la velocidad del transportador y los ticks del encoder.
  4. Conciliación del Transportador - Aplicar corrección del sensor a la posición del ítem.
  5. Movimiento del Robot - Actualizar la pose del robot en 2D continuo.
  6. Seleccionar - Mover el artículo del transportador al agarre.
  7. Lugar Bloqueado - El contenedor bloqueado devuelve POSITION_OCCUPIED.
  8. Grid Free - Encuentra el siguiente ancla de cuadrícula disponible.
  9. Colocar - Colocar el elemento en el ancla de cuadrícula libre.
  10. Repetir Colocación - Repetir la colocación para confirmar la idempotencia.

Diseño del Sistema: Lo Que Elegimos y Por Qué

Este escenario hace elecciones de diseño específicas que reflejan el modelo de estado determinista de AssetCore. Esto es lo que optimizamos y lo que sacrificamos.

Opción: Coordenadas de Punto Fijo

La Elección: Todas las posiciones utilizan enteros de punto fijo (fixed_point_scale=1000 significa 1 unidad = 1mm).

  • Por qué: La aritmética de punto flotante es no determinista entre CPUs; la reproducción podría divergir debido a diferencias de redondeo. El punto fijo garantiza una reproducción idéntica en bytes.
  • Compensación: Debe elegir la precisión de antemano (1000 = 0.001mm de resolución). Para la mayoría de la robótica, la precisión en milímetros es suficiente.
  • Cuando importa: La depuración de viajes en el tiempo, la recuperación ante desastres y las auditorías de cumplimiento dependen de la reproducción determinista.

Opción: Reconciliación de Sensores como Commits Separados

La Elección: El Paso 3 (actualización del codificador) y el Paso 4 (corrección de visión) son commits separados.

  • Por qué: Cada cambio de estado es una decisión explícita con su propia metadata. Si la confianza en la visión es baja, puedes consultar qué correcciones se aplicaron y reproducir sin ellas.
  • Compensación: Dos commits en lugar de uno (sobrecarga de latencia menor, típicamente <1ms por commit).
  • Cuando importa: Depuración de la deriva del sensor, validación de la precisión del sistema de visión, demostración de cumplimiento.

Elección: Detección de Conflictos Antes del Cambio de Estado

La Elección: El paso 7 detecta POSITION_OCCUPIED en el momento de la validación (L2), antes de cualquier mutación de estado.

  • Por qué: Sin commits parciales, sin limpieza, sin “transacciones compensatorias.” Las fallas de validación son baratas (sin I/O), y el estado se mantiene limpio.
  • Compensación: El planificador debe consultar las posiciones libres (Paso 8) después del conflicto. El bloqueo optimista fallaría con menos frecuencia, pero requeriría lógica de reversión.
  • Cuando importa: Celdas de múltiples robots con recogidas/colocaciones concurrentes. El escritor único de AssetCore por espacio de nombres garantiza que los conflictos se detecten de manera determinista.

Elección: Claves de Idempotencia para la Resiliencia de la Red

La elección: Las claves de idempotencia son opcionales, pero, cuando están presentes, deduplican los reintentos basándose en un hash canónico de la carga útil completa de la solicitud.

  • Por qué: La misma clave + la misma carga útil devuelve la respuesta en caché; la misma clave + una carga útil diferente es rechazada. Esto previene los compromisos de movimiento duplicados bajo reintentos.
  • Compensación: El servicio autoritativo debe generar o derivar claves estables por interacción y no debe reenviar claves de cliente no confiables.
  • Cuando importa: Entrega al menos una vez, reintentos a través de enlaces inestables, trabajadores de cola y conmutación por error entre múltiples regiones.

Lo Que No Manejamos (y Por Qué)

  • Planificación de movimientos: AssetCore registra actualizaciones de pose, pero no calcula trayectorias. Integra con tu planificador de movimientos (MoveIt, OMPL, personalizado) y compromete las poses planificadas.
  • Control en tiempo real: AssetCore es para la autoridad estatal, no para bucles de control de 1kHz. Úselo para coordinar planes de alto nivel y registrar los resultados de la ejecución.
  • Fusión de sensores: AssetCore almacena correcciones de sensores (Paso 4), pero no las calcula. Su sistema de visión proporciona la corrección; AssetCore la hace autoritativa y reproducible.

Estos son límites de alcance deliberados: AssetCore proporciona gestión de estado determinista y deja los algoritmos específicos del dominio (planificación de movimiento, fusión de sensores) a herramientas especializadas.

Semántica de idempotencia

Las claves de idempotencia son opcionales y solo controlan la deduplicación de reintentos. Cuando una clave está presente, Asset Core hash el payload completo de la solicitud y utiliza (namespace, key, hash) para decidir:

  • Misma clave + misma carga útil: devuelve la respuesta en caché (sin reejecución).
  • Misma clave + carga útil diferente: rechazar con 409 Conflicto.
  • La validación fallida no reserva la clave; la reutilización después de la corrección es válida.
  • No key: request executes normally, retries re-run. Sin clave: la solicitud se ejecuta normalmente, los reintentos se vuelven a ejecutar. En los despliegues de robótica, el servicio de orquestación genera o deriva claves y no debe reenviar claves de cliente no confiables.

Cómo leer la guía paso a paso

  • Las lecturas/streams de validación se enumeran por separado después de los pasos.
  • Los pasos de una sola operación utilizan ayudantes de acción para mejorar la legibilidad.
  • Los pasos de múltiples operaciones permanecen como llamadas de confirmación para preservar la atomicidad.
  • Rust HTTP siempre utiliza el endpoint de commit con una o más operaciones.

Artefactos de escenario

Última verificación

  • ID de ejecución: 2026-01-19T18-43-00Z-robotics_cell
  • Estado: Aprobado
  • Hash raíz de integridad: 53624642c9af8f6caf0e4138541c948e0f5bd09b95d480e5e9e233d7416c3d32
  • Checks:
    • {‘kind’: ‘invariantes estructurales’}: aprobado
    • {‘kind’: ‘world_seq_monotonic’}: aprobado
    • {‘kind’: ‘global_seq_monotonic’}: aprobado
    • {‘kind’: ‘replay_idempotent’}: pasado
    • {'kind': 'commit_log_integrity', 'expected_chain_hash': 'db3c030f7451ac6b5c4abfe5af7f08f3df463f6991cd19adb0beeb643781f455', 'expected_prev_chain_hash': '402bf8dde049d865bc48a2af2d9d5d9ab1e996b6da0dad5bb56118adccd5c1a2', 'expected_start_offset': 1, 'expected_end_offset': 7, 'expected_entry_count': 7, 'expected_last_global_seq': 23}: aprobado
    • {‘kind’: ‘runpack_integrity’}: aprobado

Descargas de Runpack

Instantánea del escenario

{
  "scenario_id": "robotics_cell",
  "version": "0.1.0",
  "namespace_id": 1,
  "containers": {
    "conveyor": "container-31001",
    "grid": "container-31002",
    "gripper": "container-31003",
    "robot_pose": "container-31004"
  },
  "classes": {
    "item": "class-41001",
    "robot_pose": "class-41002"
  },
  "units": {
    "fixed_point_scale": 1000,
    "length_unit": "mm",
    "time_unit": "ms"
  }
}

Fallo de rama

POSITION_OCCUPIED -> grid_free -> reintentar con la misma clave de idempotencia

Registro de auditoría (opcional)

La transcripción captura los pares de solicitud y respuesta exactos emitidos durante la ejecución. Aquí hay un extracto compacto que puedes usar para validar el determinismo y la propagación de metadatos:

{
  "kind": "commit",
  "name": "conveyor-reconcile",
  "request": {
    "path": "/v1/write/namespaces/1/commit",
    "idempotency_key": "robotics-cell-reconcile",
    "actor_id": "robotics-cell",
    "metadata": {
      "job_id": "job-robotics-001",
      "sensor_ts_ms": 1725000001250,
      "step": "conveyor-reconcile",
      "trace_id": "trace-robotics-001",
      "vision_confidence": 0.78
    },
    "operations": [
      "MoveInstance"
    ]
  },
  "response": {
    "commit_id": "00000000000000000000000000000002",
    "world_seq_start": 15,
    "world_seq_end": 16,
    "event_count": 2
  }
}

Nota de pre-vuelo

  • Las llamadas a la acción envían confirmaciones por defecto.
  • Establezca ActionOptions(preflight=True) o llame a assetcore_commit_preflight para validación.

Configuración del SDK

from assetcore_sdk import AssetCoreClient
from assetcore_sdk.actions import ActionOptions
from assetcore_sdk.operations import (
    AddInstance,
    CreateContainer,
    MoveInstance,
    RegisterClass,
    RegisterClassContinuousShape1d,
    RegisterClassContinuousShape2d,
    RegisterClassShape,
)

# Snippets assume an async context (e.g., inside async def main()).
write_client = AssetCoreClient(
    base_url="http://localhost:8080",
    api_key="WRITE_API_KEY",
)
read_client = AssetCoreClient(
    base_url="http://localhost:8081",
    api_key="READ_API_KEY",
)

Guía

Paso 1: Configuración

Registrar clases/formas y crear contenedores.

Por qué este paso es importante: Los flujos de trabajo de robótica necesitan que la geometría de los contenedores y las formas de clase estén definidas antes de que se pueda validar cualquier colocación o movimiento.

Precondiciones:

  • Ninguno. Este es el commit de arranque.

Lo que este commit crea:

  • Transportador (continuo 1D), contenedores (rejilla), pinza (ranuras), pose del robot (continuo 2D).
  • Clases de ítems y robots con formas de cuadrícula y continuas.

Cambio de estado esperado:

  • Los contenedores y las formas están registrados para que las futuras operaciones de MoveInstance puedan ser validadas.

Operaciones

  • CreateContainer (x4) - Crea un contenedor (región de memoria estructurada) con el tipo solicitado.
  • RegisterClass (x2) - Registra una definición de clase para que las operaciones futuras puedan hacer referencia a ella.
  • RegisterClassShape - Registra una huella de forma de cuadrícula para una clase o variante de clase.
  • RegisterClassContinuousShape1d - Registra un intervalo continuo 1D para una clase o variante de clase.
  • RegisterClassContinuousShape2d - Registra un rectángulo continuo 2D para una clase o variante de clase.
await write_client.commit_operations(
    [
        CreateContainer(
            kind={
                "bucket_cell_size": 1000,
                "max_x": 39999,
                "min_x": 0,
                "quantization_inv": 1000,
                "type": "continuous_line_1d",
            },
            policies=None,
            external_id="container-31001",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={"capacity": 16, "grid_width": 4, "type": "grid"},
            policies=None,
            external_id="container-31002",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={"count": 2, "type": "slots"},
            policies=None,
            external_id="container-31003",
            owner_external_id="owner-77",
        ),
        CreateContainer(
            kind={
                "bucket_cell_size": 1000,
                "max_x": 9999,
                "max_y": 9999,
                "min_x": 0,
                "min_y": 0,
                "quantization_inv": 1000,
                "type": "continuous_grid_2d",
            },
            policies=None,
            external_id="container-31004",
            owner_external_id="owner-77",
        ),
        RegisterClass(
            request={
                "behavior": {"balance_scale": 1},
                "class_id": "class-41001",
                "flags": 0,
                "name": "robotics-item-41001",
            },
        ),
        RegisterClass(
            request={
                "behavior": {"balance_scale": 1},
                "class_id": "class-41002",
                "flags": 0,
                "name": "robotics-robot-41002",
            },
        ),
        RegisterClassShape(
            request={"class_id": "class-41001", "shape": {"height": 1, "width": 1}},
        ),
        RegisterClassContinuousShape1d(
            request={"class_id": "class-41001", "span": {"length": 2000}},
        ),
        RegisterClassContinuousShape2d(
            request={
                "class_id": "class-41002",
                "rect": {"height": 1000, "width": 1000},
            },
        ),
    ],
    namespace_id=1,
    idempotency_key="robotics-cell-setup",
    actor_id="robotics-cell",
    metadata={
        "job_id": "job-robotics-001",
        "step": "setup",
        "trace_id": "trace-robotics-001",
    },
)

Paso 2: Semilla

Crear pose de robot, artículo entrante y contenedor bloqueado.

Por qué este paso es importante: La siembra proporciona al flujo de trabajo instancias concretas para mover: una pose de robot, un artículo entrante y un contenedor deliberadamente bloqueado.

Precondiciones:

  • La confirmación de configuración ha creado contenedores y registrado clases/formas.

Qué notar en el código:

  • La pose del robot se coloca en 2D continua.
  • El artículo entrante se coloca en la línea de transporte.
  • El artículo de la papelera bloqueada ocupa la celda de la cuadrícula objetivo.

Cambio de estado esperado:

  • Las instancias de robot, artículo entrante y bloqueador existen en sus ubicaciones iniciales.

Operaciones

  • AddInstance (x3) - Crea una nueva instancia y la coloca en una ubicación objetivo.
await write_client.commit_operations(
    [
        AddInstance(
            class_id="class-41002",
            client_tag="robot-arm-1",
            key=None,
            location={
                "container_id": "container-31004",
                "coord": {"x": 2000, "y": 3000},
                "kind": "continuous_2d",
                "rotation_millideg": 0,
            },
        ),
        AddInstance(
            class_id="class-41001",
            client_tag="inbound-item",
            key=None,
            location={
                "container_id": "container-31001",
                "coord": {"x": 4000},
                "kind": "continuous_1d",
            },
        ),
        AddInstance(
            class_id="class-41001",
            client_tag="blocked-bin",
            key=None,
            location={
                "container_id": "container-31002",
                "kind": "grid_cell",
                "position": 6,
                "rotation": None,
            },
        ),
    ],
    namespace_id=1,
    idempotency_key="robotics-cell-seed",
    actor_id="robotics-cell",
    metadata={
        "job_id": "job-robotics-001",
        "sensor_ts_ms": 1725000000000,
        "step": "seed",
        "trace_id": "trace-robotics-001",
        "vision_confidence": 0.82,
    },
)

Paso 3: Avance del Transportador

Mover el elemento en función de la velocidad del transportador y los ticks del codificador.

Por qué este paso es importante: La cinta transportadora mueve los elementos en función de los ticks del codificador. En un sistema no determinista, confiarías en el conteo de ticks del codificador y esperarías que coincida con la realidad. AssetCore registra la posición exacta como un commit - con marca de tiempo y metadatos del codificador - así que si una recogida falla más tarde, puedes reproducir desde este punto y ver si la posición del codificador era correcta o si ocurrió un desplazamiento del sensor.

Precondiciones:

  • El elemento entrante existe en la cinta transportadora desde el compromiso inicial.

Lo que AssetCore proporciona:

  • Las actualizaciones de posición son confirmaciones atómicas (sin movimientos parciales)
  • Los metadatos capturan los ticks del codificador y la velocidad para análisis forenses
  • La clave de idempotencia asegura que reproducir este commit produzca el mismo estado

Qué notar en el código:

  • encoder_ticks: 1825 y speed_mm_s: 450 en los metadatos - capturado para auditoría
  • Coordenada 1D continua x: 10000 (10mm con fixed_point_scale=1000)
  • La clave de idempotencia robotics-cell-advance previene movimientos duplicados en reintentos

Cambio de estado esperado:

  • El elemento entrante se mueve a la nueva coordenada del transportador.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31001",
        "coord": {"x": 10000},
        "kind": "continuous_1d",
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-advance",
        actor_id="robotics-cell",
        metadata={
            "encoder_ticks": 1825,
            "job_id": "job-robotics-001",
            "speed_mm_s": 450,
            "step": "conveyor-advance",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Paso 4: Conciliación del Transportador

Aplicar corrección del sensor a la posición del ítem.

Por qué este paso es importante: El sistema de visión detecta que la posición real difiere de la estimación del encoder (10000mm encoder vs 11250mm visión). AssetCore aplica la corrección como un commit separado con los metadatos vision_confidence: 0.78. Si reproduces desde este punto, verás la corrección exacta del sensor que se aplicó.

Precondiciones:

  • El artículo entrante permanece en la cinta transportadora después del paso de avance.

Lo que AssetCore proporciona:

  • Las correcciones del sensor son compromisos de primera clase con metadatos
  • Las correcciones de baja confianza se registran para un análisis posterior
  • La repetición muestra la delta exacta entre las estimaciones del codificador y la visión.

Qué notar en el código:

  • Delta de 1250mm (12.5% de desviación respecto a la estimación del encoder)
  • Confianza de visión 0.78 capturada en los metadatos
  • El commit separado permite consultar “qué correcciones se aplicaron”

Cambio de estado esperado:

  • El elemento entrante se mueve a la coordenada corregida.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31001",
        "coord": {"x": 11250},
        "kind": "continuous_1d",
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-reconcile",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "sensor_ts_ms": 1725000001250,
            "step": "conveyor-reconcile",
            "trace_id": "trace-robotics-001",
            "vision_confidence": 0.78,
        },
    ),
)

Paso 5: Movimiento del Robot

Actualiza la pose del robot en 2D continuo.

Por qué este paso es importante: Las actualizaciones de la pose del robot son cambios de estado autoritativos, no sugerencias del planificador. Registrarlas como confirmaciones las hace listas para auditoría de movimiento.

Precondiciones:

  • La instancia del robot existe en el contenedor de pose a partir del commit inicial.

Qué notar en el código:

  • MoveInstance apunta a un contenedor 2D continuo con coordenadas de punto fijo.

Cambio de estado esperado:

  • La pose del robot se actualiza a las nuevas coordenadas X/Y.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-1",
    to={
        "container_id": "container-31004",
        "coord": {"x": 3500, "y": 4000},
        "kind": "continuous_2d",
        "rotation_millideg": 0,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-robot-move",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "robot-move",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Paso 6: Elegir

Mover el artículo del transportador al agarre.

Por qué este paso es importante: La selección es un movimiento determinista del transportador al agarre. No hay estado oculto: el artículo sale de un contenedor y entra en otro.

Precondiciones:

  • El artículo entrante está en la cinta transportadora.
  • La ranura del gripper objetivo está vacía.

Qué notar en el código:

  • MoveInstance apunta a un contenedor de ranura (gripper).

Cambio de estado esperado:

  • El artículo sale del contenedor del transportador y ocupa la ranura del agarre.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-2",
    to={"container_id": "container-31003", "kind": "slot", "slot_index": 1},
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-pick",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "pick",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Paso 7: Colocar Bloqueado (Error esperado)

El contenedor bloqueado devuelve POSITION_OCCUPIED.

Por qué este paso es importante: La posición del contenedor 6 está ocupada. AssetCore devuelve el error POSITION_OCCUPIED antes de cualquier cambio de estado. No se requiere ningún compromiso parcial ni limpieza. La detección de conflictos ocurre en el momento de la validación (L2), por lo que su estado permanece limpio.

Precondiciones:

  • El artículo está en la ranura del agarre.
  • La celda de la cuadrícula objetivo está ocupada por el bloqueador.

Lo que AssetCore proporciona:

  • Conflictos detectados antes de la mutación del estado (validación económica)
  • La respuesta de error incluye detalles exactos del conflicto (qué posición, qué elemento)
  • Clave de idempotencia preservada para reintento con posición corregida

Qué notar en el código:

  • El error es determinista (no es una condición de carrera)
  • retryable: false significa que el cliente debe cambiar la solicitud (consultar por posición libre)
  • La misma clave de idempotencia se reutiliza después de la replanificación

Cambio de estado esperado:

  • No se aplican cambios de estado; este es un fallo de validación determinista.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.

Error esperado

{
  "code": "POSITION_OCCUPIED",
  "detail": "Position 6 in container 2 is occupied by item at anchor 6",
  "hint": "Select an unoccupied position or move the existing item first.",
  "retryable": false,
  "status": 409,
  "title": "ConflictError",
  "type": "urn:assetcore:error:POSITION_OCCUPIED"
}
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 6,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "step": "place-attempt",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Paso 8: Sin Rejilla

Encuentra el siguiente ancla de cuadrícula disponible.

Por qué este paso es importante: Después de detectar el conflicto, el planificador consulta las posiciones de cuadrícula libres. Esta operación de lectura ve la proyección actual: sin bloqueos, sin interrupciones. La escalabilidad de lectura de AssetCore significa que los planificadores pueden consultar continuamente las posiciones disponibles sin afectar el rendimiento de escritura.

Precondiciones:

  • Un intento de colocación falló debido a una celda de la cuadrícula ocupada.

Lo que AssetCore proporciona:

  • Las consultas de lectura nunca bloquean las escrituras (arquitectura CQRS)
  • La consulta sin cuadrícula devuelve la primera posición disponible de manera determinista
  • El retraso de frescura está acotado y es medible (ver metadatos de respuesta)

Qué notar en el código:

  • Parámetros de consulta: height=1, width=1 (huella del ítem)
  • La respuesta devuelve la posición 1 (primer ancla libre en orden de fila)
  • No se requiere transacción (consulta de solo lectura)

Cambio de estado esperado:

  • Ninguno. Esta es una consulta de planificación de solo lectura.
await read_client.get_container_grid_free(
    container_id="container-31002",
    namespace_id=1,
    height=1,
    width=1,
)

Paso 9: Colocar

Colocar el elemento en el ancla de cuadrícula libre.

Por qué este paso es importante: La colocación replanificada compromete el ancla corregida en un solo movimiento atómico del gripper a la cuadrícula.

Precondiciones:

  • El elemento permanece en el gripper.
  • El ancla libre devuelta por grid_free sigue disponible.

Qué notar en el código:

  • MoveInstance apunta a la celda de la cuadrícula libre devuelta por el paso de lectura.

Cambio de estado esperado:

  • El elemento sale del agarrador y ocupa la celda de la cuadrícula libre.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 1,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "replan_reason": "grid_free",
            "step": "place",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Paso 10: Colocar Repetición

Reproduce la colocación para confirmar la idempotencia.

Por qué este paso es importante: Repetir el mismo commit exacto (mismo idempotency key) devuelve éxito sin efectos duplicados. Así es como AssetCore garantiza la idempotencia: repetir el registro siempre produce el mismo estado. En un sistema distribuido, esto significa que los reintentos de red son seguros.

Precondiciones:

  • El compromiso de colocación ya ha tenido éxito.

Lo que AssetCore proporciona:

  • Idempotencia a nivel de commit (no solo HTTP)
  • Replay produce el mismo commit_id y world_seq que el original
  • Seguro volver a intentar cualquier operación sin corrupción del estado

Qué notar en el código:

  • Carga idéntica a la colocación exitosa (misma clave, mismas operaciones)
  • La respuesta confirma que no se generaron nuevos eventos (idempotente)
  • Así es como funciona la recuperación ante desastres: reproducir todo el registro

Cambio de estado esperado:

  • Ninguno. La reproducción devuelve el commit en caché.

Operaciones

  • MoveInstance - Mueve una instancia existente a una nueva ubicación.
await write_client.actions.move_instance(
    instance="inst-2",
    to={
        "container_id": "container-31002",
        "kind": "grid_cell",
        "position": 1,
        "rotation": None,
    },
    options=ActionOptions(
        namespace_id=1,
        idempotency_key="robotics-cell-place",
        actor_id="robotics-cell",
        metadata={
            "job_id": "job-robotics-001",
            "replan_reason": "grid_free",
            "step": "place",
            "trace_id": "trace-robotics-001",
        },
    ),
)

Validación

Estas lecturas y flujos confirman el estado final después de los pasos sin mutar nada.

Leer 1: Leer Transportador

Verifique que las ubicaciones de los transportadores estén vacías.

await read_client.get_container_continuous_placements_1d(
    container_id="container-31001",
    namespace_id=1,
)

Leer 2: Leer Slots

Verifique que las ranuras del gripper estén vacías.

await read_client.get_container_slots(
    container_id="container-31003",
    namespace_id=1,
)

Leer 3: Leer Rejilla

Verifique que las ubicaciones de la cuadrícula contengan bloqueador y elemento.

await read_client.get_container_grid(
    container_id="container-31002",
    namespace_id=1,
)

Leer 4: Leer Robot

Verificar la colocación de la pose del robot.

await read_client.get_container_continuous_placements_2d(
    container_id="container-31004",
    namespace_id=1,
)

Stream 5: Compromiso de Flujo

Lee el compromiso de colocación desde SSE.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "assetcore_read_stream",
  "params": {
    "from_world_seq": 22,
    "limit": 1,
    "namespace_id": 1
  }
}

Lo que esto demuestra: Robótica de grado de producción con garantías determinísticas

Este escenario demuestra la complejidad de los flujos de trabajo de producción de recogida y colocación: seguimiento continuo de la posición, reconciliación de sensores, detección de conflictos y replanteamiento automático.

Punto de Prueba 1: Cada Cambio de Estado es Auditable

Cada commit lleva metadatos: job_id, trace_id, sensor_ts_ms, vision_confidence. Cuando una selección falla, no adivinas; consultas el registro de commits y ves:

  • Posición exacta del codificador en el avance del transportador
  • Corrección de visión aplicada (delta: 1250mm, confianza: 0.78)
  • Qué posición de la cuadrícula fue bloqueada
  • Nueva posición de la cuadrícula seleccionada tras la replanificación

Punto de Prueba 2: Detección de Conflictos Sin Bloqueo

El paso 7 muestra la detección de POSITION_OCCUPIED en el momento de la validación. Sin bloqueos distribuidos, sin sobrecarga de coordinación. La arquitectura de un solo escritor de AssetCore hace que los conflictos sean deterministas: se detectan antes de cualquier cambio de estado, y la respuesta de error te indica exactamente qué debes corregir.

Punto de Prueba 3: Reproducción Idempotente para Depuración

El paso 10 demuestra la idempotencia: reproducir el mismo commit (mismo idempotency key, mismas operaciones) tiene éxito sin efectos secundarios. Así es como se depuran las fallas en producción:

  1. Exportar el registro de confirmaciones desde la ventana de fallo
  2. Repetir localmente para reproducir el estado exacto
  3. Inspeccionar el estado intermedio en cualquier commit
  4. Soluciona el problema y verifica con la reproducción

Valor Empresarial: De “Falló” a “Aquí Está el Porqué”

Los pilas de robótica tradicionales te dan tres registros en conflicto (visión, planificación, ejecución) y te obligan a reconstruir lo que sucedió. AssetCore te proporciona un registro autoritativo donde cada cambio de estado está sellado con una marca de tiempo, atribuido (actor_id) y es reproducible.

Para la puesta en marcha: Repetir las ejecuciones de producción para validar el comportamiento de la celda

Para depuración: Viajar en el tiempo hasta el commit exacto donde ocurrió la falla

Para cumplimiento: Exportar un registro de auditoría inmutable con metadatos del sensor

Para la optimización: Analizar los metadatos de los commits para identificar cuellos de botella

Esto es lo que permite una herramienta de clase mundial: gestión de estado determinista que te permite centrarte en la lógica del dominio de la robótica en lugar de luchar contra errores de reconciliación de estado.

Próximos Pasos

  • Pruébalo localmente: Clona el repositorio y ejecuta el escenario para llevar a cabo este flujo de trabajo
  • Amplíalo: Añade coordinación entre múltiples robots mediante la fragmentación a través de espacios de nombres
  • Integrarlo: Consulte la Referencia del SDK para patrones de integración en producción
  • Pregúntanos: Reserva una sesión técnica para discutir tu caso de uso de robótica