서비스 기반 통신 개요
ROS에서는 토픽(Topic) 외에도 서비스(Service)를 통해 노드 간 동기식 요청-응답 방식의 통신을 지원합니다. 이 방식은 클라이언트가 특정 작업을 요청하면 서버가 이를 처리하고 결과를 반환하는 모델입니다.
기능 패키지 생성
먼저, 새로운 ROS 패키지를 생성합니다. 아래 명령어는 의존성으로 roscpp, rospy, std_msgs, geometry_msgs, turtlesim을 포함합니다.
cd ~/catkin_ws/src
catkin_create_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
클라이언트 노드 구현
클라이언트는 다음 절차에 따라 작성됩니다:
- ROS 노드 초기화
- 서비스 연결용 클라이언트 객체 생성
- 요청 데이터 구성 및 전송
- 서버 응답 수신 및 처리
C++ 클라이언트 코드 예제
#include <ros/ros.h>
#include "learning_service/Person.h"
int main(int argc, char **argv) {
ros::init(argc, argv, "person_requester");
ros::NodeHandle nh;
// /show_person 서비스가 활성화될 때까지 대기
ros::service::waitForService("/show_person");
ros::ServiceClient client = nh.serviceClient<learning_service::Person>("/show_person");
// 요청 파라미터 설정
learning_service::Person srv;
srv.request.name = "Alice";
srv.request.age = 25;
srv.request.sex = learning_service::Person::Request::female;
ROS_INFO("Sending request: name=%s, age=%d, sex=%d",
srv.request.name.c_str(), srv.request.age, srv.request.sex);
// 서비스 호출 수행
if (client.call(srv)) {
ROS_INFO("Response received: %s", srv.response.result.c_str());
} else {
ROS_ERROR("Failed to call service /show_person");
return 1;
}
return 0;
}
파이썬 클라이언트 코드 예제
#!/usr/bin/env python
import rospy
from learning_service.srv import Person, PersonRequest
def request_person_info():
rospy.init_node('person_requester')
# 서비스 연결 대기
rospy.wait_for_service('/show_person')
try:
proxy = rospy.ServiceProxy('/show_person', Person)
req = PersonRequest()
req.name = "Alice"
req.age = 25
req.sex = PersonRequest.female
resp = proxy(req)
rospy.loginfo("Server response: %s", resp.result)
return resp.result
except rospy.ServiceException as e:
rospy.logerr("Service call failed: %s", str(e))
if __name__ == "__main__":
request_person_info()
서버 노드 구현
서버는 다음과 같은 흐름으로 동작합니다:
- 노드 및 서비스 인스턴스 초기화
- 콜백 함수 등록
- 요청 수신 대기 루프 실행
- 처리 후 응답 반환
C++ 서버 코드 예제
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>
ros::Publisher cmd_pub;
bool enable_movement = false;
bool handle_command(std_srvs::Trigger::Request&,
std_srvs::Trigger::Response& res) {
enable_movement = !enable_movement;
ROS_INFO("Turtle movement: %s", enable_movement ? "Enabled" : "Disabled");
res.success = true;
res.message = "Command state toggled.";
return true;
}
int main(int argc, char **argv) {
ros::init(argc, argv, "movement_controller");
ros::NodeHandle nh;
cmd_pub = nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
ros::ServiceServer srv = nh.advertiseService("/turtle_command", handle_command);
ros::Rate rate(10);
ROS_INFO("Movement controller ready.");
while (ros::ok()) {
if (enable_movement) {
geometry_msgs::Twist msg;
msg.linear.x = 0.6;
msg.angular.z = 0.3;
cmd_pub.publish(msg);
}
ros::spinOnce();
rate.sleep();
}
return 0;
}
파이썬 서버 코드 예제
#!/usr/bin/env python
import rospy
import threading
from geometry_msgs.msg import Twist
from std_srvs.srv import Trigger, TriggerResponse
cmd_enabled = False
cmd_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
def movement_loop():
rate = rospy.Rate(10)
while not rospy.is_shutdown():
if cmd_enabled:
twist = Twist()
twist.linear.x = 0.6
twist.angular.z = 0.3
cmd_pub.publish(twist)
rate.sleep()
def trigger_callback(req):
global cmd_enabled
cmd_enabled = not cmd_enabled
rospy.loginfo("Movement command updated: %s", "ON" if cmd_enabled else "OFF")
return TriggerResponse(success=True, message="State changed")
def start_server():
rospy.init_node('movement_controller')
rospy.Service('/turtle_command', Trigger, trigger_callback)
thread = threading.Thread(target=movement_loop)
thread.start()
rospy.loginfo("Ready to accept movement commands.")
rospy.spin()
if __name__ == '__main__':
start_server()
CMakeLists.txt 설정
C++ 코드를 빌드하기 위해 CMakeLists.txt에 다음 항목을 추가합니다:
add_executable(person_client src/person_client.cpp)
add_executable(movement_controller src/movement_controller.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
target_link_libraries(movement_controller ${catkin_LIBRARIES})
빌드 및 실행 절차
다음 명령어를 순차적으로 실행하여 시스템을 작동시킵니다:
cd ~/catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service movement_controller
rosrun learning_service person_client
또는 파이썬 버전은 바로 실행 가능하며 별도 빌드가 필요 없습니다. 서비스 호출 테스트는 아래 명령어로 직접 확인할 수 있습니다:
rosservice call /turtle_command "{}"