# XLOC Manual Control API Guide Now you have **FULL MANUAL CONTROL** over XLOC SLAM operations! Control via: 1. C# Service Methods 2. REST API Endpoints 3. SignalR Hub Methods --- ## 1. C# Service Methods (Direct) Inject `XlocIntegrationService` into your service: ```csharp public class MyNavigationService { private readonly XlocIntegrationService _xloc; public MyNavigationService(XlocIntegrationService xloc) { _xloc = xloc; } public void StartLocalizationWithMap() { // Activate map if (_xloc.ActivateMap("/maps/factory_floor.pbstream")) { // Start localization _xloc.StartLocalization(); } } public void BeginMapping() { _xloc.StartMapping(); } public void SaveAndStopMapping() { _xloc.StopMapping("/maps/new_map.pbstream"); } } ``` --- ## 2. REST API Endpoints ### Activate Map ```bash curl -X POST "https://localhost:7002/api/xloc/activate-map?mapPath=/maps/factory.pbstream" ``` Response: ```json { "success": true, "message": "Map activated" } ``` ### Start Mapping ```bash dotnet build # Restart app pkill -f dotnet ./run-quiet.sh # Test mapping curl -k -X POST https://127.0.0.1:7002/api/motion/ps5/enable curl -k -X POST https://localhost:7002/api/xloc/mapping/start # ... di chuyển robot ... ``` ### Stop Mapping ```bash curl -k -X POST https://localhost:7002/api/xloc/mapping/stop \ -H "Content-Type: application/json" \ -d '{"map_file_path": "test14"}' ``` ### Active Map ```bash curl -k -X POST https://localhost:7002/api/xloc/map/activate \ -H "Content-Type: application/json" \ -d '{"map_file_path": "test10"}' ``` ### Start Localization ```bash curl -k -X POST https://localhost:7002/api/xloc/localization/start ``` ### Stop Localization ```bash curl -k -X POST https://localhost:7002/api/xloc/localization/stop ``` ### Reset SLAM State ```bash # Reset SLAM error state (automatically called before start mapping/localization) # Useful if you manually need to clear previous trajectory state curl -k -X POST https://localhost:7002/api/xloc/slam/reset ``` **Note:** `StartMapping()` and `StartLocalization()` now automatically call reset before starting, so you typically don't need to call this manually. ### Stop Mapping & Save **Option 1: Manual (will crash, but map is saved)** ```bash # Save with timestamp MAP_NAME="map_$(date +%Y%m%d_%H%M%S).pbstream" curl -k -X POST "https://localhost:7002/api/xloc/stop-mapping?savePath=/home/robotics/sonvh/RobotNet10/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp/Xloc/map/$MAP_NAME" # Or save with custom name (MUST include .pbstream extension!) curl -k -X POST "https://localhost:7002/api/xloc/stop-mapping?savePath=/home/robotics/sonvh/RobotNet10/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp/Xloc/map/my_map.pbstream" cd Xloc ./map-and-save.sh my_office_map.pbstream # Note: Application will crash due to XLOC Cairo bug, but map is saved successfully # Restart with: ./run-quiet.sh ``` **Option 2: Automated script (recommended)** ```bash # Use automated script that handles crash and restart cd Xloc ./map-and-save.sh my_map.pbstream # Script will: # 1. Wait for you to drive robot # 2. Save map on ENTER # 3. Handle crash gracefully # 4. Auto-restart application ``` ### Get Current Pose ```bash curl "https://localhost:7002/api/xloc/pose" ``` Response: ```json { "x": 1.234, "y": 5.678, "yaw": 1.57, "yawDegrees": 90.0 } ``` --- ## 3. SignalR Hub Methods ### TypeScript/JavaScript Client ```typescript import * as signalR from "@microsoft/signalr"; const connection = new signalR.HubConnectionBuilder() .withUrl("https://localhost:7002/hubs/xloc/pose") .build(); // Subscribe to pose updates (realtime streaming) connection.on("ReceivePose", (pose) => { console.log(`Position: (${pose.x}, ${pose.y}), Heading: ${pose.yawDegrees}°`); }); await connection.start(); // Control SLAM manually async function startLocalization() { const success = await connection.invoke("StartLocalization"); console.log("Localization started:", success); } async function activateMap(mapPath: string) { const success = await connection.invoke("ActivateMap", mapPath); console.log("Map activated:", success); } async function startMapping() { const success = await connection.invoke("StartMapping"); console.log("Mapping started:", success); } async function stopMapping(savePath: string) { const success = await connection.invoke("StopMapping", savePath); console.log("Map saved:", success); } async function getCurrentPose() { const pose = await connection.invoke("GetCurrentPose2D"); console.log("Current pose:", pose); // { x: 1.234, y: 5.678, yaw: 1.57, yawDegrees: 90.0 } } ``` ### React Example ```tsx import { HubConnectionBuilder } from '@microsoft/signalr'; import { useState, useEffect } from 'react'; export function XlocControl() { const [connection, setConnection] = useState(null); const [pose, setPose] = useState(null); useEffect(() => { const conn = new HubConnectionBuilder() .withUrl("https://localhost:7002/hubs/xloc/pose") .build(); conn.on("ReceivePose", (data) => { setPose(data); }); conn.start(); setConnection(conn); return () => conn.stop(); }, []); const handleStartLocalization = async () => { const result = await connection.invoke("StartLocalization"); console.log("Started:", result); }; const handleStartMapping = async () => { const result = await connection.invoke("StartMapping"); console.log("Mapping started:", result); }; return (

XLOC Control Panel

{pose && (

X: {pose.x.toFixed(2)}m

Y: {pose.y.toFixed(2)}m

Heading: {pose.yawDegrees.toFixed(1)}°

)}
); } ``` --- ## Typical Workflows ### Workflow 1: Localization (Using Existing Map) ```bash # 1. Activate map POST /api/xloc/activate-map?mapPath=/maps/factory.pbstream # 2. Start localization POST /api/xloc/start-localization # 3. Robot is now localizing! # Pose updates stream automatically via SignalR # 4. When done POST /api/xloc/stop-localization ``` ### Workflow 2: Mapping (Create New Map) ```bash # 1. Start mapping POST /api/xloc/start-mapping # 2. Drive robot around # Map is being created in realtime # 3. Save and stop POST /api/xloc/stop-mapping?savePath=/maps/new_building.pbstream ``` --- ## Available Methods in All Interfaces | Method | XlocIntegrationService | REST API | SignalR Hub | |--------|----------------------|----------|-------------| | ActivateMap | ✅ `ActivateMap(mapPath)` | ✅ `POST /api/xloc/activate-map` | ✅ `connection.invoke("ActivateMap", mapPath)` | | StartLocalization | ✅ `StartLocalization()` | ✅ `POST /api/xloc/start-localization` | ✅ `connection.invoke("StartLocalization")` | | StopLocalization | ✅ `StopLocalization()` | ✅ `POST /api/xloc/stop-localization` | ✅ `connection.invoke("StopLocalization")` | | StartMapping | ✅ `StartMapping()` | ✅ `POST /api/xloc/start-mapping` | ✅ `connection.invoke("StartMapping")` | | StopMapping | ✅ `StopMapping(savePath)` | ✅ `POST /api/xloc/stop-mapping` | ✅ `connection.invoke("StopMapping", savePath)` | | GetCurrentPose2D | ✅ `GetCurrentPose2D()` | ✅ `GET /api/xloc/pose` | ✅ `connection.invoke("GetCurrentPose2D")` | --- ## Important Notes ✅ **No Auto-Start**: SLAM does NOT start automatically anymore! ✅ **Manual Control Only**: You must explicitly call start methods ✅ **Sensor Data Streaming**: Continues automatically at 20Hz (Odom + IMU) ✅ **Pose Streaming**: Broadcasts via SignalR at 5Hz when SLAM is running ## Configuration ```json { "Xloc": { "Integration": { "Enabled": true, "Mode": "Mapping", // Ignored - now manual control "UpdateRateHz": 20, "MapFilePath": "", // Ignored - call ActivateMap() manually "SaveMapFilePath": "/tmp/xloc_map.pbstream" } } } ``` Note: `Mode` and `MapFilePath` in config are now ignored. Use manual control methods instead! --- ## Troubleshooting ### Cannot start mapping after stop & save **Problem:** After stopping localization or mapping, `StartMapping()` fails. **Root Cause:** XLOC library retains the finished trajectory state. Starting a new mapping/localization session requires clearing this state. **Solution (Automatic):** `StartMapping()` and `StartLocalization()` now automatically call `ResetSlamError()` before starting, which clears the previous trajectory state. **Manual Reset (if needed):** ```bash # If automatic reset doesn't work, manually reset SLAM state curl -k -X POST https://localhost:7002/api/xloc/slam/reset # Then try starting mapping again curl -k -X POST https://localhost:7002/api/xloc/mapping/start ``` **What the fix does:** - Clears finished trajectory (trajectory ID from previous session) - Resets SLAM error state to Idle - Prepares XLOC library for new mapping/localization session ### General Workflow After Fix ```bash # 1. Stop previous session (if any) curl -k -X POST https://localhost:7002/api/xloc/localization/stop # 2. Start mapping (automatic reset happens internally) curl -k -X POST https://localhost:7002/api/xloc/mapping/start # 3. Drive robot around... # 4. Stop and save curl -k -X POST https://localhost:7002/api/xloc/mapping/stop \ -H "Content-Type: application/json" \ -d '{"map_file_path": "my_new_map"}' # 5. Start mapping again (works now!) curl -k -X POST https://localhost:7002/api/xloc/mapping/start ```