API Example Reference
RaisinClient 래퍼 클래스를 사용한 예제 코드입니다. 필요에 따라 수정하여 사용하세요.
헤더 포함
#include "raisin_sdk/raisin_client.hpp"
기본 워크플로우
// 1. 클라이언트 생성
raisin_sdk::RaisinClient client("my_app");
// 2. 로봇 연결
client.connect("ROBOT_ID");
// 3. 로봇에 저장된 맵 로드 (graph, waypoints 자동 로드)
auto mapResult = client.loadMap("my_map");
if (mapResult.success) {
std::cout << "Graph nodes: " << mapResult.graphNodes.size() << std::endl;
std::cout << "Waypoints: " << mapResult.waypoints.size() << std::endl;
}
// 4. 초기 위치 설정 (Localization 시작)
client.setInitialPose(0.0, 0.0, 0.0); // x, y, yaw
// 5. Waypoint 설정 (frame은 반드시 map_name과 동일해야 함!)
std::vector<raisin_sdk::Waypoint> waypoints = {
raisin_sdk::Waypoint("my_map", 5.0, 0.0), // "my_map" 사용
raisin_sdk::Waypoint("my_map", 5.0, 5.0),
};
client.setWaypoints(waypoints, 1);
// 6. 상태 모니터링
auto status = client.getMissionStatus();
Data Types
Waypoint - 네비게이션 목표 지점
struct Waypoint {
std::string frame; // 좌표 프레임 (중요!)
double x, y, z; // 좌표
bool use_z; // Z 좌표 사용 여부
};
// 생성 방법
Waypoint("map_name", x, y); // 맵 좌표
Waypoint::GPS(latitude, longitude); // GPS 좌표 (GPS 모듈 필요)
Warning
frame 이름은 loadMap()에서 지정한 map_name과 반드시 일치해야 합니다!
// 올바른 예시
client.loadMap("office_map");
client.setInitialPose(0, 0, 0);
Waypoint("office_map", 5.0, 0.0); // ✓ 동일한 이름
// 잘못된 예시
client.loadMap("office_map");
Waypoint("map", 5.0, 0.0); // ✗ 이름 불일치 - 동작 안함!
MissionStatus - 미션 상태
struct MissionStatus {
std::vector<Waypoint> waypoints; // 현재 waypoint 목록
uint8_t current_index; // 현재 목표 인덱스
uint8_t repetition; // 남은 반복 횟수 (infinite_loop가 true면 무시됨)
bool infinite_loop; // 무한 순찰 여부
bool valid; // 데이터 유효성
};
ServiceResult - 서비스 호출 결과
struct ServiceResult {
bool success; // 성공 여부
std::string message; // 결과 메시지
};
RobotState - 로봇 상태 (Odometry)
struct RobotState {
double x, y, z; // 위치 (meters)
double yaw; // 방향 (radians)
double vx, vy; // 선속도 (m/s)
double omega; // 각속도 (rad/s)
bool valid;
};
Point3D - 포인트 클라우드 점
struct Point3D { float x, y, z; };
GraphNode - 경로 그래프 노드
struct GraphNode {
int32_t id; // 노드 ID
double x, y, z; // 좌표
};
GraphEdge - 경로 그래프 엣지
struct GraphEdge {
int32_t from_node; // 시작 노드 ID
int32_t to_node; // 끝 노드 ID
double cost; // 비용 (거리)
};
LoadMapResult - 맵 로드 결과
struct LoadMapResult {
bool success;
std::string message;
std::string mapName; // 로드된 맵 이름
std::vector<GraphNode> graphNodes; // 자동 로드된 그래프 노드
std::vector<GraphEdge> graphEdges; // 자동 로드된 그래프 엣지
std::vector<Waypoint> waypoints; // 자동 로드된 기본 경로 (route_1)
std::vector<std::string> availableRoutes; // 사용 가능한 경로 목록
};
LoadGraphResult - 그래프 로드 결과
struct LoadGraphResult {
bool success;
std::string message;
std::vector<GraphNode> nodes;
std::vector<GraphEdge> edges;
};
RefineWaypointsResult - 경로 최적화 결과
struct RefineWaypointsResult {
bool success;
std::string message;
std::vector<Waypoint> refined_waypoints; // 최적화된 waypoints
std::vector<int32_t> path_node_ids; // 경로를 구성하는 노드 ID
};
ActuatorInfo - 액추에이터(모터) 정보
struct ActuatorInfo {
std::string name; // 모터 이름 (e.g., "FR_hip", "FL_thigh")
uint16_t status; // CiA402 상태 코드 (아래 참조)
double temperature; // 모터 온도 (°C)
double position; // 관절 위치 (rad)
double velocity; // 관절 속도 (rad/s)
double effort; // 관절 토크 (Nm)
};
CiA402StatusWord - 액추에이터 상태 코드 (EtherCAT CiA402 표준)
enum class CiA402StatusWord : uint16_t {
NOT_READY_TO_SWITCH_ON = 0, // 준비 안됨 (에러 상태)
FAULT = 8, // 결함 (에러 상태)
READY_TO_SWITCH_ON = 33, // 스위치온 준비 (정상 대기)
SWITCHED_ON = 35, // 스위치온 (정상)
OPERATION_ENABLED = 39, // 동작 활성화 (정상 운전)
ECAT_CONN_ERROR = 99 // EtherCAT 연결 에러
};
Note
status 값 해석:
0,8,99= 에러 상태 (빨간색 경고 필요)33,35= 정상 대기 (모터 준비/대기 중)39= 정상 운전 (모터 작동 중)
ResumePatrolResult - 순찰 재개 결과
struct ResumePatrolResult {
bool success; // 성공 여부
std::string message; // 결과 메시지
uint8_t waypoint_index; // 재개 시작 waypoint 인덱스
};
ListFilesResult - 파일 목록 조회 결과
struct ListFilesResult {
bool success; // 성공 여부
std::string message; // 결과 메시지
std::vector<std::string> files; // 파일 이름 목록
};
LocomotionState - 로봇 locomotion 상태 열거형
enum class LocomotionState : int32_t {
COMM_DISABLED = 0, // 통신 비활성
COMM_ENABLED = 1, // 통신 활성
MOTOR_READY = 2, // 모터 준비
MOTOR_COMMUTATION = 3, // 모터 정류 중
MOTOR_ENABLED = 4, // 모터 활성
IN_TEST_MODE = 5, // 테스트 모드
STANDING_MODE = 6, // 서있는 상태
IN_CONTROL = 7, // 제어 중 (걷기)
SITDOWN_MODE = 8, // 앉은 상태
MOTOR_DISABLED = 9 // 모터 비활성
};
JoySourceType - 조이스틱 제어 소스 타입
enum class JoySourceType : int32_t {
JOY = 0, // 수동 조이스틱 제어
VEL_CMD = 1, // 자율주행 속도 명령
NUM_SOURCES = 2 // 제어 소스 없음
};
ExtendedRobotState - 확장 로봇 상태 (배터리, 모터 상태 포함)
struct ExtendedRobotState {
// 위치 및 속도
double x, y, z; // 위치 (meters)
double yaw; // 방향 (radians)
double vx, vy; // 선속도 (m/s)
double omega; // 각속도 (rad/s)
// Locomotion 상태
int32_t locomotion_state; // LocomotionState 열거형 값 (0-9)
// 배터리 정보
double voltage; // 현재 전압 (V)
double current; // 전류 (A)
double max_voltage; // 최대 전압
double min_voltage; // 최소 전압
// 온도
double body_temperature; // 본체 온도 (°C)
// 조이스틱 제어 상태
int32_t joy_listen_type; // JoySourceType 열거형 값
// 액추에이터 상태
std::vector<ActuatorInfo> actuators;
bool valid;
// 유틸리티 메서드
std::string getLocomotionStateName() const; // 상태명 문자열
std::string getJoySourceName() const; // 제어 소스명 문자열
bool isOperational() const; // 서있거나 걷는 중인지
bool hasActuatorError() const; // 모터 에러 여부
std::vector<std::string> getActuatorsWithErrors() const; // 에러 모터 목록
bool allActuatorsOperational() const; // 모든 모터 정상 여부
};
RaisinClient Methods
connect()
bool connect(const std::string& robot_id, int timeout_sec = 10,
std::atomic<bool>* cancel_token = nullptr);
로봇에 연결합니다.
robot_id: Robot ID 또는 IP 주소timeout_sec: 연결 타임아웃 (초)cancel_token: 연결 취소용 플래그 (optional)반환: 연결 성공 여부
disconnect()
void disconnect();
로봇과의 연결을 종료합니다.
isConnected()
bool isConnected() const;
연결 상태를 확인합니다.
Map Management (새로운 방식)
listMapFiles()
ListFilesResult listMapFiles();
로봇에 저장된 맵 파일 목록을 조회합니다.
auto result = client.listMapFiles();
if (result.success) {
std::cout << "Available maps:" << std::endl;
for (const auto& file : result.files) {
std::cout << " - " << file << std::endl;
}
}
loadMap()
LoadMapResult loadMap(const std::string& name);
로봇에 저장된 맵을 로드합니다. 연관된 그래프와 기본 경로(route_1)를 자동으로 로드합니다.
name: 맵 이름 (예: “office_floor1”)반환: 로드 결과 (그래프 노드/엣지, 웨이포인트, 사용 가능한 경로 목록 포함)
auto result = client.loadMap("office_floor1");
if (result.success) {
std::cout << "Map loaded: " << result.mapName << std::endl;
std::cout << "Graph: " << result.graphNodes.size() << " nodes, "
<< result.graphEdges.size() / 2 << " edges" << std::endl;
std::cout << "Default route: " << result.waypoints.size() << " waypoints" << std::endl;
std::cout << "Available routes:" << std::endl;
for (const auto& route : result.availableRoutes) {
std::cout << " - " << route << std::endl;
}
}
setInitialPose()
ServiceResult setInitialPose(double x, double y, double yaw);
맵 로드 후 초기 위치를 설정하여 Localization을 시작합니다.
x,y: 맵 내 초기 위치 (로봇이 물리적으로 있는 위치)yaw: 초기 방향 (radians)반환: 서비스 호출 결과
// loadMap() 호출 후 사용
auto result = client.setInitialPose(0.0, 0.0, 0.0);
if (result.success) {
std::cout << "Localization started" << std::endl;
}
Note
loadMap() 을 먼저 호출해야 합니다. 맵 없이 호출하면 실패합니다.
getLoadedMapName()
std::string getLoadedMapName() const;
현재 로드된 맵 이름을 반환합니다.
std::string mapName = client.getLoadedMapName();
if (!mapName.empty()) {
std::cout << "Current map: " << mapName << std::endl;
}
Subscription Methods
subscribeMapOdometry()
void subscribeMapOdometry(OdometryCallback callback);
맵 프레임에서의 Odometry 데이터를 실시간 구독합니다.
setInitialPose() 성공 후 사용해야 맵 좌표계 위치를 받을 수 있습니다.
client.subscribeMapOdometry([](const raisin_sdk::RobotState& state) {
std::cout << "Map position: " << state.x << ", " << state.y << std::endl;
});
subscribeOdometry()
void subscribeOdometry(OdometryCallback callback);
Odom 프레임에서의 Odometry 데이터를 실시간 구독합니다 (Fast-LIO 원본 출력).
맵 좌표가 필요하면 subscribeMapOdometry() 를 사용하세요.
subscribePointCloud()
void subscribePointCloud(PointCloudCallback callback);
LiDAR 포인트 클라우드를 실시간 구독합니다.
getRobotState() / getLatestPointCloud()
RobotState getRobotState();
std::vector<Point3D> getLatestPointCloud();
마지막으로 수신한 데이터를 반환합니다 (thread-safe).
subscribeRobotState()
void subscribeRobotState(ExtendedRobotStateCallback callback);
확장 로봇 상태를 실시간 구독합니다. 배터리 정보, locomotion 상태, 액추에이터 상태 등을 포함합니다.
client.subscribeRobotState([](const raisin_sdk::ExtendedRobotState& state) {
std::cout << "Locomotion: " << state.getLocomotionStateName() << std::endl;
std::cout << "Battery: " << state.voltage << "V" << std::endl;
std::cout << "Control: " << state.getJoySourceName() << std::endl;
if (state.hasActuatorError()) {
std::cerr << "Warning: Actuator error detected!" << std::endl;
}
});
getExtendedRobotState()
ExtendedRobotState getExtendedRobotState();
마지막으로 수신한 확장 로봇 상태를 반환합니다 (thread-safe).
subscribeRobotState() 를 먼저 호출해야 유효한 데이터를 얻을 수 있습니다.
Control Mode Switching
findGuiNetworkId()
std::string findGuiNetworkId(const std::string& prefix = "gui");
연결된 GUI의 네트워크 ID를 찾습니다.
prefix: GUI ID 접두사 (기본값: “gui”)반환: GUI 네트워크 ID (예: “gui53-230486654196”), 없으면 빈 문자열
setManualControl()
ServiceResult setManualControl(const std::string& gui_network_id = "");
수동 조이스틱 제어를 활성화합니다 (joy/gui). GUI 네트워크 ID를 자동 감지하여 해당 GUI에서 조이스틱 명령을 받을 수 있도록 합니다.
auto result = client.setManualControl();
if (result.success) {
std::cout << "Manual control enabled" << std::endl;
}
setAutonomousControl()
ServiceResult setAutonomousControl();
자율주행 제어를 활성화합니다 (vel_cmd/autonomy).
auto result = client.setAutonomousControl();
if (result.success) {
std::cout << "Autonomous control enabled" << std::endl;
}
releaseControl()
ServiceResult releaseControl(const std::string& source = "joy/gui");
제어권을 해제합니다 (None 상태로 전환).
source: 해제할 제어 소스 (“joy/gui” 또는 “vel_cmd/autonomy”)
client.releaseControl("joy/gui");
client.releaseControl("vel_cmd/autonomy");
setListenSource() (저수준 API)
ServiceResult setListenSource(const std::string& topic_name,
const std::string& network_id = "");
직접 listen source를 설정합니다.
Locomotion Control
standUp()
ServiceResult standUp();
로봇을 일어서게 합니다.
Warning
로봇이 안전한 위치에 있는지 확인한 후 호출하세요.
locomotion 상태가 SITDOWN_MODE 또는 STANDING_MODE 일 때 호출해야 합니다.
sitDown()
ServiceResult sitDown();
로봇을 앉게 합니다.
Note
로봇이 IN_CONTROL (걷는 중) 상태에서도 호출 가능합니다.
로봇은 안전하게 멈추고 앉습니다.
Patrol Route Management
listWaypointsFiles()
ListFilesResult listWaypointsFiles(const std::string& directory = "");
저장된 순찰 경로 파일 목록을 조회합니다.
auto result = client.listWaypointsFiles();
if (result.success) {
std::cout << "Available routes:" << std::endl;
for (const auto& file : result.files) {
std::cout << " - " << file << std::endl;
}
}
loadWaypointsFile()
ServiceResult loadWaypointsFile(const std::string& name);
저장된 순찰 경로 파일을 로드합니다.
name: 로드할 파일 이름 (예: “my_map/paths/route_1”)
auto result = client.loadWaypointsFile("office_floor1/paths/route_1");
if (result.success) {
std::cout << "Route loaded successfully" << std::endl;
}
saveWaypointsFile()
ServiceResult saveWaypointsFile(const std::string& name);
현재 waypoints를 파일로 저장합니다.
name: 저장할 파일 이름 (확장자 제외)
auto result = client.saveWaypointsFile("my_map/paths/custom_route");
if (result.success) {
std::cout << "Route saved successfully" << std::endl;
}
resumePatrol()
ResumePatrolResult resumePatrol();
현재 로드된 순찰 경로에서 가장 가까운 waypoint부터 순찰을 재개합니다.
auto result = client.resumePatrol();
if (result.success) {
std::cout << "Resuming from waypoint " << (int)result.waypoint_index << std::endl;
}
Note
loadWaypointsFile() 로 경로를 먼저 로드해야 합니다.
Graph Management
loadGraphFile()
LoadGraphResult loadGraphFile(const std::string& name);
그래프 파일을 로드합니다.
name: 그래프 파일 이름 (예: “my_map/graph”)
auto result = client.loadGraphFile("office_floor1/graph");
if (result.success) {
std::cout << "Graph loaded: " << result.nodes.size() << " nodes, "
<< result.edges.size() << " edges" << std::endl;
}
saveGraphFile()
ServiceResult saveGraphFile(const std::string& name,
const std::vector<GraphNode>& nodes,
const std::vector<GraphEdge>& edges);
그래프를 파일로 저장합니다.
std::vector<raisin_sdk::GraphNode> nodes = {
{0, 0.0, 0.0, 0.0},
{1, 5.0, 0.0, 0.0},
{2, 5.0, 5.0, 0.0},
};
std::vector<raisin_sdk::GraphEdge> edges = {
{0, 1, 5.0},
{1, 0, 5.0},
{1, 2, 5.0},
{2, 1, 5.0},
};
auto result = client.saveGraphFile("my_map/graph", nodes, edges);
refineWaypoints()
RefineWaypointsResult refineWaypoints(const std::vector<Waypoint>& waypoints,
const std::vector<GraphNode>& nodes,
const std::vector<GraphEdge>& edges);
A* 알고리즘을 사용하여 그래프 기반으로 waypoints를 최적화합니다. 입력 waypoints 사이의 경로를 그래프 노드를 따라가도록 세분화합니다.
// 시작점과 끝점만 지정
std::vector<raisin_sdk::Waypoint> waypoints = {
raisin_sdk::Waypoint("office_floor1", 0.0, 0.0),
raisin_sdk::Waypoint("office_floor1", 10.0, 10.0),
};
// 그래프 기반 경로 최적화
auto result = client.refineWaypoints(waypoints, graphNodes, graphEdges);
if (result.success) {
std::cout << "Refined path: " << result.refined_waypoints.size() << " waypoints" << std::endl;
std::cout << "Path nodes: ";
for (auto id : result.path_node_ids) {
std::cout << id << " -> ";
}
std::cout << "end" << std::endl;
// 최적화된 경로로 네비게이션 시작
client.setWaypoints(result.refined_waypoints, 1);
}
Actuator Status Helpers
isActuatorStatusError()
bool isActuatorStatusError(uint16_t status);
액추에이터 상태 코드가 에러 상태인지 확인합니다.
for (const auto& act : state.actuators) {
if (raisin_sdk::isActuatorStatusError(act.status)) {
std::cerr << act.name << " has error!" << std::endl;
}
}
getActuatorStatusName()
std::string getActuatorStatusName(uint16_t status);
액추에이터 상태 코드를 사람이 읽을 수 있는 문자열로 변환합니다.
코드 |
반환값 |
설명 |
|---|---|---|
0 |
“NOT_READY” |
준비 안됨 (에러) |
8 |
“FAULT” |
결함 (에러) |
33 |
“READY” |
스위치온 준비 |
35 |
“SWITCHED_ON” |
스위치온 |
39 |
“ENABLED” |
동작 중 (정상) |
99 |
“ECAT_ERROR” |
EtherCAT 에러 |
기타 |
“UNKNOWN(N)” |
알 수 없는 상태 |
GPS 사용 시 주의사항
Waypoint::GPS(37.5665, 126.9780);
GPS 모듈이 로봇에 장착 되어 있어야 합니다
GPS fix가 유효해야 합니다 (
/fix토픽)GPS가 없으면
setWaypoints()는 성공하지만 로봇이 목표에 도달하지 못합니다