Recon
Mapas y puntos de ruta de Recon
Trabaja con el mapa de un robot Recon a través de la API: encuentra el mapa asignado, dibújalo con la posición del robot y las zonas de exclusión, convierte entre píxeles de la imagen y coordenadas del mapa, y guarda puntos de ruta para las rutas.
En esta página
Esta página se tradujo automáticamente. Si encuentras errores o tienes sugerencias, escríbenos.
Todo lo que hace un robot ocurre en su mapa: las poses, los puntos de ruta y las rutas son coordenadas en el sistema de coordenadas del mapa. Esta guía lee el mapa, lo dibuja y coloca puntos de ruta en él. Los ejemplos usan la función auxiliar robot_api de la descripción general.
Necesitas una clave de API con acceso de solo lectura a la ubicación del robot para leer el mapa, y acceso de administrador a esa ubicación para guardar puntos de ruta.
Encuentra el mapa del robot
El robot crea su mapa durante la configuración, y cada robot tiene un mapa asignado. getMapAssignment lo devuelve:
assignment = robot_api("getMapAssignment", {"deviceUuid": ROBOT_UUID})
map_meta = assignment.get("mapMeta")
if not map_meta:
raise SystemExit("This robot has no assigned map yet")
MAP_UUID = map_meta["uuid"]
keepouts = [f for f in assignment.get("mapFilters", []) if f["type"] == "KEEPOUT"]mapMetadescribe el mapa:uuid,locationUuid,resolution,originy, cuando el mapa está colocado sobre un plano de planta,floorPlan.mapFiltersenumera las zonas de exclusión aplicadas en este mapa (abajo).- Un robot sin mapa asignado no devuelve ninguno de los dos. Las rutas y las patrullas necesitan uno.
Para listar todos los mapas de una ubicación, por ejemplo para elegir uno antes de crear puntos de ruta, llama a findMaps con un locationUuid (o sin él, para toda la organización).
Dibuja el mapa
getMapImage devuelve el mapa asignado del robot como una imagen PGM en escala de grises (image/x-portable-graymap): el espacio ocupado en oscuro y el espacio libre en claro. La mayoría de las bibliotecas de imágenes leen PGM:
import io
from PIL import Image
response = requests.post(f"{API}/getMapImage", headers=HEADERS, json={"deviceUuid": ROBOT_UUID}, timeout=60)
response.raise_for_status()
map_image = Image.open(io.BytesIO(response.content)).convert("RGB")
map_image.save("map.png")Un robot sin mapa asignado responde 404.
Píxeles y coordenadas del mapa
getMapMeta devuelve los dos valores que relacionan la imagen con las coordenadas del mapa:
resolution: metros por píxel.origin: la posición en el mapa de la esquina inferior izquierda de la imagen. Usa sus dos primeros valores,xey.
Las filas de la imagen se cuentan de arriba hacia abajo, mientras que la y del mapa crece hacia arriba:
meta = robot_api("getMapMeta", {"deviceUuid": ROBOT_UUID})
RES = meta["resolution"]
OX, OY = meta["origin"][0], meta["origin"][1]
HEIGHT = map_image.height
def map_to_pixel(x, y):
return (x - OX) / RES, HEIGHT - (y - OY) / RES
def pixel_to_map(px, py):
return px * RES + OX, (HEIGHT - py) * RES + OYPara mostrar dónde está el robot y hacia dónde va, lee pose, goalPose y navPath (su trayecto planificado, una lista de puntos [x, y, z]) de getFullState y dibújalos con map_to_pixel:
from PIL import ImageDraw
state = robot_api("getFullState", {"deviceUuid": ROBOT_UUID, "force": False})["fullState"]
draw = ImageDraw.Draw(map_image)
path = [map_to_pixel(p[0], p[1]) for p in state.get("navPath") or []]
if len(path) > 1:
draw.line(path, fill=(0, 128, 0), width=2)
if state.get("pose"):
px, py = map_to_pixel(*state["pose"]["position"][:2])
draw.ellipse([px - 4, py - 4, px + 4, py + 4], fill=(0, 102, 204))
map_image.save("map-with-robot.png")Para mostrar un punto que se mueva con el robot, el socket en vivo del robot transmite la pose a medida que cambia.
Zonas de exclusión
Una zona de exclusión es un área en la que el robot nunca entra. Cada una de las que aparecen en mapFilters tiene un uuid y un name; getMapFilterImage devuelve su máscara, una imagen PGM del mismo tamaño que el mapa:
for zone in keepouts:
response = requests.post(
f"{API}/getMapFilterImage",
headers=HEADERS,
json={"deviceUuid": ROBOT_UUID, "mapFilterUuid": zone["uuid"]},
timeout=60,
)
response.raise_for_status()
mask = Image.open(io.BytesIO(response.content))
mask.save(f"keepout-{zone['name']}.png")En la máscara, 0 (negro) es un área en la que el robot nunca entra, 255 (blanco) no tiene efecto, y los valores intermedios son áreas que el robot evita cuando puede. Dibuja los píxeles negros sobre el mapa con un color de advertencia y no coloques puntos de ruta allí.
Puntos de ruta
Un punto de ruta es una pose con nombre en un mapa. Las paradas de una ruta pueden hacer referencia a un punto de ruta en lugar de repetir su pose, así que varias rutas pueden compartir el mismo lugar.
| Operación | Cuerpo |
|---|---|
createRobotWaypoint | mapUuid, name, pose |
findRobotWaypointsByMap | mapUuid |
getRobotWaypoint | mapUuid, waypointUuid |
updateRobotWaypoint | waypointUuid, y name o pose (un campo que omitas no cambia) |
deleteRobotWaypoint | mapUuid, waypointUuid |
Guarda el lugar donde está el robot
La pose más fiable es una que el robot ya alcanzó: llévalo hasta el lugar (en la consola o por el socket en vivo) y guarda su pose actual:
state = robot_api("getFullState", {"deviceUuid": ROBOT_UUID, "force": False})["fullState"]
waypoint = robot_api("createRobotWaypoint", {
"mapUuid": state["mapId"],
"name": "Loading dock door",
"pose": state["pose"],
})["waypoint"]
print(waypoint["uuid"])Coloca uno desde el mapa
Para que alguien elija un lugar haciendo clic en la imagen del mapa, convierte el clic con pixel_to_map y el rumbo elegido con heading_to_orientation de la descripción general:
import math
x, y = pixel_to_map(412, 238)
waypoint = robot_api("createRobotWaypoint", {
"mapUuid": MAP_UUID,
"name": "North fence",
"pose": {"position": [x, y, 0.0], "orientation": heading_to_orientation(math.pi / 2)},
})["waypoint"]Antes, compara el píxel seleccionado con el mapa y con las máscaras de exclusión: el robot no puede llegar a un punto de ruta que está dentro de una pared o de una zona de exclusión.
Antes de eliminar uno
Al eliminar un punto de ruta no se revisa si alguna ruta lo usa, y una parada cuyo punto de ruta ya no existe se omite. Busca su uuid en el waypointRefUuid de las paradas de cada ruta (findRobotRoutesByMap) y actualiza esas rutas primero.