1. 간단한 Action Server 만들기
이제 my_first_package 안에 Action Server 파일을 만듭니다.
my_first_package/
└── my_first_package/
└── dist_turtle_action_server.py
먼저 cd ~/ros2_study/src/my_first_package/my_first_package/ 로 이동하여 dist_turtle_action_server.py 파일을 생성합니다.
cd ~/ros2_study/src/my_first_package/my_first_package/
touch dist_turtle_action_server.py

기본 구조는 다음과 같습니다. VS Code에서 dist_turtle_action_server.py 파일을 불러와서 아래의 코드를 작성합니다.
import rclpy as rp
from rclpy.action import ActionServer
from rclpy.node import Node
from my_first_package_msgs.action import DistTurtle
class DistTurtleServer(Node):
def __init__(self):
super().__init__('dist_turtle_action_server')
self._action_server = ActionServer(
self,
DistTurtle,
'dist_turtle',
self.execute_callback
)
def execute_callback(self, goal_handle):
goal_handle.succeed()
result = DistTurtle.Result()
return result
def main(args=None):
rp.init(args=args)
dist_turtle_action_server = DistTurtleServer()
rp.spin(dist_turtle_action_server)
dist_turtle_action_server.destroy_node()
rp.shutdown()
if __name__ == '__main__':
main()

아직 turtlesim을 움직이지는 않고, Action Goal을 받으면 바로 성공 처리 후 빈 Result를 반환하는 구조입니다.
이 소스는 다음과 같은 역할을 합니다.
Action Client → /dist_turtle로 Goal 전송
Action Server → Goal 수신
Action Server → 성공 처리
Action Server → Result 반환
즉, 이 코드는 “Action Server가 제대로 실행되고 Goal을 받을 수 있는지” 확인하기 위한 기본 뼈대입니다.
a. import 부분
import rclpy as rp
ROS 2 Python 라이브러리인 rclpy를 불러옵니다.
여기서는 이름을 짧게 쓰기 위해 rp라는 별칭을 붙였습니다.
from rclpy.action import ActionServer
Action Server를 만들기 위해 필요한 클래스입니다.
ROS 2 Action 통신에서 서버 역할을 하는 객체를 생성할 때 사용합니다.
from rclpy.node import Node
ROS 2에서 노드를 만들기 위한 기본 클래스입니다.
사용자가 직접 노드를 만들 때는 보통 Node를 상속받아 클래스를 작성합니다.
from my_first_package_msgs.action import DistTurtle
직접 만든 Action 정의 파일인 DistTurtle.action을 불러옵니다.
예를 들어 DistTurtle.action이 다음 구조라면,
float32 linear_x
float32 angular_z
float32 dist
---
float32 pos_x
float32 pos_y
float32 pos_theta
float32 result_dist
---
float32 remained_dist
Python에서는 다음 객체들을 사용할 수 있게 됩니다.
DistTurtle.Goal
DistTurtle.Result
DistTurtle.Feedback
b. Action Server 클래스 선언
class DistTurtleServer(Node):
DistTurtleServer라는 클래스를 만듭니다.
이 클래스는 Node를 상속받기 때문에 ROS 2 노드로 동작할 수 있습니다. 즉, 이 클래스 하나가 하나의 ROS 2 노드입니다.
c. 생성자 함수
def __init__(self):
클래스 객체가 생성될 때 자동으로 실행되는 함수입니다.
super().__init__('dist_turtle_action_server')
부모 클래스인 Node의 생성자를 호출합니다.
여기서 노드 이름을 지정합니다.
노드 이름: dist_turtle_action_server
실행 후 노드 목록을 확인하면 이 이름으로 보입니다.
ros2 node list
d. ActionServer 생성
self._action_server = ActionServer(
self,
DistTurtle,
'dist_turtle',
self.execute_callback
)
이 부분이 코드의 핵심입니다. Action Server를 생성합니다.
각 인자의 의미는 다음과 같습니다.
self
현재 노드 객체입니다.
즉, 이 Action Server가 DistTurtleServer 노드 안에서 동작한다는 뜻입니다.
DistTurtle
사용할 Action 타입입니다.
앞에서 import한 DistTurtle.action 정의를 사용합니다.
'dist_turtle'
Action 이름입니다.
클라이언트는 이 이름으로 Goal을 보냅니다.
실행할 때는 다음처럼 사용합니다.
ros2 action send_goal /dist_turtle my_first_package_msgs/action/DistTurtle "{linear_x: 0.0, angular_z: 0.0, dist: 0.0}"
주의할 점은 코드에서는 'dist_turtle'이라고 쓰지만, 명령어에서는 보통 앞에 /를 붙여 /dist_turtle로 사용합니다.
self.execute_callback
Goal이 들어왔을 때 실행할 함수입니다.
즉, Action Client가 Goal을 보내면 execute_callback() 함수가 호출됩니다.
e. execute_callback 함수
def execute_callback(self, goal_handle):
Action Server가 Goal을 받으면 실행되는 callback 함수입니다.
여기서 goal_handle은 클라이언트가 보낸 Goal을 관리하는 객체입니다. 일반적으로 goal_handle이라는 이름을 많이 사용하지만 사용자가 다른 이름을 사용해도 됩니다.
Goal 요청값 확인, 성공 처리, 실패 처리, Feedback 발행 등을 할 수 있습니다.
예를 들어 사용자가 보낸 값을 확인하려면 다음처럼 접근할 수 있습니다.
goal_handle.request.linear_x
goal_handle.request.angular_z
goal_handle.request.dist
현재 코드에서는 요청값을 사용하지 않고 바로 성공 처리합니다.
f. Goal 성공 처리
goal_handle.succeed()
현재 Goal을 성공 상태로 바꿉니다.
Action은 단순히 Result만 반환하는 것이 아니라, Goal의 상태도 함께 관리합니다.
대표 상태는 다음과 같습니다.
| succeed() | Goal이 성공적으로 완료됨 |
| abort() | Goal 수행 실패 |
| canceled() | Goal이 취소됨 |
현재 코드는 아무 작업도 하지 않고 바로 성공 처리하므로, 클라이언트에서는 SUCCEEDED 상태를 받게 됩니다.
g. Result 객체 생성
result = DistTurtle.Result()
Action 결과를 담을 객체를 생성합니다.
만약 DistTurtle.action의 Result가 다음과 같다면,
float32 pos_x
float32 pos_y
float32 pos_theta
float32 result_dist
원래는 아래처럼 값을 넣어주는 것이 맞습니다.
result.pos_x = 0.0
result.pos_y = 0.0
result.pos_theta = 0.0
result.result_dist = 0.0
하지만 현재 코드에서는 값을 넣지 않았습니다.
그래도 기본값 0.0으로 반환됩니다.
h. Result 반환
return result
Action Client에게 Result를 반환합니다.
즉, 전체 callback 흐름은 다음과 같습니다.
Goal 수신
→ 성공 처리
→ Result 객체 생성
→ Result 반환
i. main 함수
def main(args=None):
ROS 2 노드를 실행하기 위한 진입 함수입니다.
rp.init(args=args)
ROS 2 Python 시스템을 초기화합니다.
노드를 만들기 전에 반드시 실행해야 합니다.
dist_turtle_action_server = DistTurtleServer()
앞에서 만든 Action Server 노드를 생성합니다.
이 순간 __init__()이 실행되고, Action Server도 함께 생성됩니다.
rp.spin(dist_turtle_action_server)
노드를 계속 실행 상태로 유지합니다.
이 코드가 있어야 Action Client의 Goal 요청을 기다릴 수 있습니다.
쉽게 말하면 다음 상태가 됩니다.
서버 실행 중...
Goal 들어오면 execute_callback 실행
dist_turtle_action_server.destroy_node()
노드를 종료할 때 리소스를 정리합니다.
rp.shutdown()
ROS 2 Python 시스템을 종료합니다.
j. Python 실행 진입점
if __name__ == '__main__':
main()
이 파일을 직접 실행했을 때 main() 함수를 호출합니다.
예를 들어 다음처럼 직접 실행하면,
python3 dist_turtle_action_server.py
main()이 실행됩니다. 직접 실행도 가능합니다.
서버를 빌드합니다.
cd ~/ros2_study
colcon build
source install/setup.bash
또는
sl
ros2 run my_first_package dist_turtle_action_server
다른 터미널에서 Feedback 옵션을 붙여 Goal을 보냅니다.
ros2 action send_goal --feedback /dist_turtle my_first_package_msgs/action/DistTurtle "{linear_x: 0, angular_z: 0, dist: 0}"
실행결과를 확인합니다.
ROS 2에서는 보통 setup.py에 entry point를 등록한 뒤 아래처럼 실행합니다.
ros2 run my_first_package dist_turtle_action_server
ROS 2에서 Python 노드를 ros2 run으로 실행하려면 setup.py의 entry_points에 등록해야 합니다.
entry_points={
'console_scripts': [
'dist_turtle_action_server = my_first_package.dist_turtle_action_server:main',
],
},

등록 후 다시 빌드합니다. 그리고 환경을 다시 적용합니다.
cd ~/ros2_study
colcon build
source install/setup.bash
또는
sl

서버를 실행합니다.
ros2 run my_first_package dist_turtle_action_server

새 터미널을 열고 같은 ROS 2 환경을 적용한 뒤 Action Goal을 보냅니다.
ros2 action send_goal /dist_turtle my_first_package_msgs/action/DistTurtle "{linear_x: 0, angular_z: 0, dist: 0}"
정상 동작하면 Goal이 수락되고 Result가 출력됩니다.

여기까지 성공했다면 Action Server의 기본 연결은 완료된 것입니다.
파일 압축 명령 예 :
tar zcvf ros2_study_260522.tar.gz src
파일 압축 해제 명령 예 :
tar zxvf ros2_study_260522.tar.gz
2026년 5얼 22일까지 작업한 파일 :
'강좌 > ROS2' 카테고리의 다른 글
| 2일차 강의 (0) | 2026.05.23 |
|---|---|
| 1일차 강의 (0) | 2026.05.22 |
| 로봇 개발자를 위한 Python 기초 교육 #1 (0) | 2026.05.22 |
| 4일차 강의 (0) | 2026.05.21 |
| 서비스 서버 만들기 (0) | 2026.05.21 |