3. 개발 PC 환경
3.7워크스페이스와 첫 패키지
3.6장에서 배운 개념을 직접 해 봅니다. 최종 결과는 이렇습니다. 내가 만든 패키지의 노드를 ros2 run으로 실행하면 다른 터미널에서 그 메시지가 보입니다.
이 장에서 쓰는 이름은 3.6장과 같습니다.
| 항목 | 이름 |
|---|---|
| 워크스페이스 | ~/ros2_ws |
| 패키지 | my_first_pkg |
| 노드 파일 | my_first_pkg/my_first_pkg/talker.py |
| 실행 파일 이름 (entry point) | talker |
| 토픽 | /chatter (std_msgs/msg/String) |
먼저 브라우저에서 노드 로직 확인
패키지를 만들고 빌드하는 과정은 VM에서만 되지만, 노드가 하는 일 자체는 1.3장에서 배운 것과 같습니다. 아래 코드는 패키지에 넣을 talker.py의 내용 그대로입니다. 먼저 실행해 보고 어떤 출력이 나올지 감을 잡으세요.
talker.py (노드 로직 미리보기)
이 상자는 교육용 에뮬레이터입니다. 실제 ROS2와 달리 패키지 빌드, setup.py, ros2 run은 없습니다. 아래 VM 실습에서 실제 ROS2로 같은 코드를 실행합니다.
직접 바꿔 보기
- 메시지 문장을 바꿔 보세요. VM에서도 같은 문장이 출력됩니다.
- 타이머 주기를
1.0으로 바꾸면 3초 동안 몇 번 발행될까요?
1단계: 워크스페이스 만들기
Tilix를 열고 ROS2 환경이 적용되어 있는지 확인합니다. OVA에서는 ~/.bashrc에 등록되어 있어 이미 되어 있습니다(3.4장).
echo $ROS_DISTRO
humble이 출력되지 않으면 source /opt/ros/humble/setup.bash를 실행한 뒤 다시 확인하세요.
워크스페이스의 src 폴더를 만들고 이동합니다.
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
2단계: 패키지 생성
ros2 pkg create로 ament_python 패키지의 뼈대를 만듭니다. 반드시 src 폴더 안에서 실행합니다.
ros2 pkg create my_first_pkg --build-type ament_python --license Apache-2.0 --dependencies rclpy std_msgs
my_first_pkg: 패키지 이름입니다. 옵션보다 앞에 씁니다.--build-type ament_python: Python 패키지로 만듭니다.--dependencies rclpy std_msgs:package.xml에 이 패키지들을 의존성으로 넣습니다.
이름을 맨 뒤에 쓰면 실패합니다. --dependencies는 뒤따르는 단어를 모두 의존성으로 읽기 때문에 my_first_pkg까지 의존성 이름으로 삼켜 버리고, the following arguments are required: package_name 오류가 납니다. VM에서 실제로 확인한 동작입니다.
완료되면 생성된 폴더와 파일 이름이 출력됩니다. 구조를 확인해 보세요.
ls my_first_pkg
ls my_first_pkg/my_first_pkg
package.xml, setup.py, setup.cfg, resource, test, LICENSE, 그리고 안쪽에 my_first_pkg(파이썬 모듈 폴더)가 보여야 합니다. 이 구조는 3.6장에서 본 파일 트리 그대로입니다.
- ① 패키지 이름을 앞에 쓴
ros2 pkg create명령 - ② 출력 중간의
dependencies: ['rclpy', 'std_msgs']: 의존성이 제대로 들어갔다는 확인 - ③
ls로 본 패키지 구조. 안쪽my_first_pkg폴더가 파이썬 모듈 폴더입니다.
maintainer가 ros2 <ros2@todo.todo>, description이 TODO: Package description으로 채워져 있는 것도 이 출력에 보입니다. 다음 절에서 고칩니다.
package.xml 정리
생성된 package.xml에는 description, maintainer(이름과 이메일)가 TODO로 표시되어 있습니다. 빌드는 되지만 값을 채우는 것이 좋습니다. OVA에는 VS Code가 없으므로 기본 텍스트 편집기(Text Editor)로 엽니다.
gnome-text-editor ~/ros2_ws/src/my_first_pkg/package.xml &
끝의 &는 편집기를 열어 둔 채 같은 칸에서 계속 명령을 입력하게 해 줍니다. 터미널 안에서 고치고 싶다면 nano 파일이름도 됩니다. my_first_pkg/package.xml에서 description, maintainer(이름과 이메일)를 채웁니다. 같은 값을 setup.py의 maintainer, maintainer_email, description에도 똑같이 적습니다. 두 파일의 값이 다르면 빌드 중 경고나 오류가 날 수 있습니다.
<exec_depend>rclpy</exec_depend>와 <exec_depend>std_msgs</exec_depend> 두 줄이 들어 있는지도 확인하세요.
3단계: 퍼블리셔 노드 작성
~/ros2_ws/src/my_first_pkg/my_first_pkg/ 폴더에 talker.py 파일을 만듭니다. 편집기를 열고 아래 코드를 붙여 넣은 뒤 그 폴더에 talker.py로 저장합니다.
gnome-text-editor ~/ros2_ws/src/my_first_pkg/my_first_pkg/talker.py &
없는 파일 이름을 주고 열면 새 파일이 만들어집니다. 내용은 위 실행기 코드를 그대로 옮기면 됩니다.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('talker')
self.publisher_ = self.create_publisher(String, 'chatter', 10)
self.timer = self.create_timer(0.5, self.timer_callback)
self.count = 0
def timer_callback(self):
msg = String()
msg.data = f'Hello from my_first_pkg: {self.count}'
self.publisher_.publish(msg)
self.get_logger().info(f'Publishing: "{msg.data}"')
self.count += 1
def main(args=None):
rclpy.init(args=args)
node = Talker()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
반드시 파일을 저장(Ctrl+S)하세요. 저장하지 않으면 빌드에 이전 상태가 들어갑니다.
- ① 제목 줄의 경로: 반드시
my_first_pkg/my_first_pkg/안에 있어야 합니다. - ② 토픽 이름
chatter와 주기0.5를 정하는 두 줄 - ③ 보내는 문장을 만드는 줄. 이 문장을 바꾸면 출력이 바뀝니다.
- ④ 저장 버튼 (또는
Ctrl+S)
4단계: entry point 등록
ros2 run이 이 노드를 찾으려면 setup.py에 등록해야 합니다. ~/ros2_ws/src/my_first_pkg/setup.py를 같은 방법으로 열고 맨 아래의 entry_points를 다음과 같이 고칩니다.
entry_points={
'console_scripts': [
'talker = my_first_pkg.talker:main',
],
},
- ①
maintainer,maintainer_email,description:package.xml과 같은 값으로 맞춥니다. 처음에는TODO가 들어 있습니다. - ②
entry_points안의talker등록 줄
해석은 이렇습니다. talker는 ros2 run에서 쓸 실행 파일 이름, my_first_pkg.talker는 모듈(my_first_pkg/talker.py), main은 그 안의 함수입니다. 패키지 이름, 파일 이름, 함수 이름의 철자가 한 글자라도 다르면 실패합니다.
5단계: 빌드
colcon은 반드시 워크스페이스 루트에서 실행합니다.
cd ~/ros2_ws
colcon build --packages-select my_first_pkg
성공하면 Starting >>> my_first_pkg, Finished <<< my_first_pkg, Summary: 1 package finished 같은 줄이 나옵니다. 환경에 따라 setuptools 관련 deprecation 경고가 함께 보일 수 있는데, 빌드가 Summary: 1 package finished로 끝나면 일단 정상입니다. OVA에서는 경고 없이 1~2초 만에 끝났습니다.
ls ~/ros2_ws
build install log src 네 폴더가 보이면 빌드된 것입니다.
- ① 워크스페이스 루트에서 실행한
colcon build명령 - ②
Finished <<<와Summary: 1 package finished: 빌드 성공 - ③
ls ~/ros2_ws에 보이는build install log src
6단계: source 하고 실행
새 터미널을 엽니다. 공식 문서는 빌드한 터미널과 분리하기를 권장합니다. 언더레이를 먼저, 오버레이를 나중에 source하세요. (~/.bashrc에 언더레이를 등록했다면 첫 줄은 이미 되어 있지만 다시 해도 문제없습니다.)
source /opt/ros/humble/setup.bash
cd ~/ros2_ws
source install/setup.bash
실행 파일이 등록되었는지 먼저 확인합니다.
ros2 pkg executables my_first_pkg
my_first_pkg talker가 출력되어야 합니다. 이제 노드를 실행합니다.
ros2 run my_first_pkg talker
Publishing: "Hello from my_first_pkg: 0" 같은 로그가 0.5초마다 나오면 성공입니다. 이 칸은 그대로 두세요. (OVA에서는 칸 2를 노드 실행에 씁니다.)
7단계: 토픽으로 확인
세 번째 칸(칸 3)에서 명령을 입력합니다. 이 터미널에도 ROS2 환경이 필요한데, 이번에는 내 패키지가 아니라 ROS2 기본 도구만 쓰므로 언더레이만 있으면 됩니다.
ros2 topic list
ros2 topic echo /chatter
/chatter가 목록에 보이고 echo에서 data: 'Hello from my_first_pkg: ...' 와 ---가 반복되면 노드가 실제로 토픽을 발행하고 있는 것입니다. 1.3장의 ros2 topic echo와 같은 화면입니다.
- ① 칸 2:
ros2 run my_first_pkg talker가 0.5초마다 찍는 로그 - ② 칸 3의
ros2 topic list에/chatter가 보입니다. - ③
ros2 topic echo /chatter가 받은 메시지. 번호가 칸 2의 로그와 같은 순서로 올라갑니다.
각 칸에서 Ctrl+C로 종료합니다.
ros2 node list
ros2 topic info /chatter
위 명령으로 노드 이름 /talker, 타입 std_msgs/msg/String, 퍼블리셔 1개를 확인할 수 있습니다. (노드가 실행 중일 때만 보입니다.)
코드를 고치고 다시 실행하기
개발은 "고치기 → 빌드 → 실행"의 반복입니다. talker.py의 메시지를 바꿔 봅시다.
talker.py에서 문장을 수정하고 저장합니다.- 빌드한 터미널에서
cd ~/ros2_ws && colcon build --packages-select my_first_pkg - 노드를 실행하는 터미널에서
Ctrl+C로 끈 뒤 다시ros2 run my_first_pkg talker
반복이 번거로우면 --symlink-install 옵션으로 빌드해 보세요. 이후 Python 코드만 고칠 때는 빌드 없이 노드만 다시 실행하면 됩니다.
cd ~/ros2_ws
colcon build --packages-select my_first_pkg --symlink-install
새 파일을 추가하거나 setup.py, package.xml을 바꿨을 때는 이 옵션을 써도 다시 빌드해야 합니다. 일반 빌드와 --symlink-install 빌드를 번갈아 쓰다가 오류가 나면, 아래 "깔끔하게 다시 빌드" 방법을 쓰세요.
자주 만나는 실패와 해결
| 증상 | 원인 | 해결 |
|---|---|---|
ros2: command not found | ROS2 환경을 source하지 않음 | source /opt/ros/humble/setup.bash (3.4장의 .bashrc 설정 확인) |
ros2 run 시 Package 'my_first_pkg' not found | 워크스페이스를 source하지 않았거나, 빌드가 실패했음 | cd ~/ros2_ws && source install/setup.bash 실행. 새 터미널을 열 때마다 필요. 그래도 안 되면 빌드 결과(Summary)를 확인 |
No executable found | setup.py의 entry_points 철자 오류, 등록 누락, 또는 수정 후 다시 빌드하지 않음 | entry point의 패키지.모듈:함수가 실제 파일과 함수 이름과 같은지 확인, 빌드 후 새 터미널(또는 다시 source)에서 ros2 pkg executables my_first_pkg로 확인 |
| 코드를 고쳤는데 이전 동작이 나옴 | 다시 빌드하지 않았음(또는 저장하지 않음), 또는 예전 터미널에서 실행 | 저장 확인, colcon build 다시 실행, 노드를 껐다가 다시 실행 |
colcon build가 아무 패키지도 찾지 못함 | src가 아닌 곳, 또는 워크스페이스 루트가 아닌 곳에서 실행 | cd ~/ros2_ws 후 실행. src/my_first_pkg/package.xml이 있는지 확인 |
ModuleNotFoundError: No module named 'my_first_pkg.talker' | 파일 이름 오타, __init__.py 누락, 파일 위치 오류 | talker.py가 src/my_first_pkg/my_first_pkg/ 안에 있는지 확인 |
| 빌드 중 이름 불일치 오류, 경고 | 폴더, package.xml, setup.py의 패키지 이름이나 maintainer 정보가 다름 | 세 곳을 같은 값으로 맞춤 |
ros2 topic echo에 아무것도 안 나옴 | 노드가 실행 중이 아니거나, 토픽 이름이 다르거나, 다른 ROS_DOMAIN_ID | ros2 topic list로 /chatter 확인, 두 터미널의 echo $ROS_DOMAIN_ID가 같은지 확인 (3.9장) |
깔끔하게 다시 빌드
원인을 알 수 없을 때는 빌드 결과를 모두 지우고 처음부터 빌드하는 것이 빠릅니다. build, install, log는 빌드하면 다시 생기므로 지워도 안전합니다. src는 지우면 안 됩니다.
cd ~/ros2_ws
rm -rf build install log
colcon build --packages-select my_first_pkg
지운 뒤에는 새 터미널을 열어 다시 source install/setup.bash를 해야 합니다.
한눈에 정리
- 워크스페이스는
~/ros2_ws/src부터 만들고 패키지는src안에서ros2 pkg create --build-type ament_python ...으로 만든다. - 노드 파일은
패키지/패키지/안에 두고setup.py의entry_points에이름 = 패키지.모듈:main형태로 등록한다. - 빌드는 워크스페이스 루트에서
colcon build --packages-select <패키지>. - 빌드 후에는 새 터미널마다
source install/setup.bash를 해야ros2 run <패키지> <실행파일>이 동작한다. - 확인은
ros2 pkg executables,ros2 topic list,ros2 topic echo로 한다. - 문제가 꼬이면
build install log를 지우고 다시 빌드한다.
확인 문제
Q1. 빌드는 성공했는데 새 터미널에서 ros2 run my_first_pkg talker를 하면 Package not found가 나옵니다. 가장 먼저 할 일은?
새 터미널에서 cd ~/ros2_ws && source install/setup.bash를 실행합니다. 워크스페이스(오버레이)는 터미널마다 source해야 합니다.
Q2. setup.py에 'talker = my_first_pkg.talker:main'을 쓰고 빌드했습니다. 나중에 파일 이름을 talker.py에서 publisher.py로 바꿨습니다. 무엇을 고쳐야 하나요?
entry_points를 'talker = my_first_pkg.publisher:main'으로 바꾸고 다시 colcon build를 해야 합니다. 파일 이름이 바뀌었으므로 --symlink-install을 썼더라도 재빌드가 필요합니다.
Q3. talker는 잘 실행되는데 다른 터미널의 ros2 topic echo /chatter에서 아무것도 안 보입니다. 확인할 두 가지는?
ros2 topic list에/chatter가 있는지 (노드가 실행 중인지, 토픽 이름이 같은지)- 두 터미널의
ROS_DOMAIN_ID가 같은지 (echo $ROS_DOMAIN_ID)
다음 장
다음 장에서는 VM의 네트워크 모드와 ROS_DOMAIN_ID를 설정해, 다른 컴퓨터(또는 로봇)와 토픽을 주고받는 방법을 배웁니다.