Search
🌍

deer.a2_6_3. [긴글] title: python 에서 커스텀 ROS 메시지 사용하기, __init__.py, ROS 패키지, python 패키지의 관계

🚀 prev note
♻️ prev note
🚀 next note
♻️ next note
17 more properties
newdolph (newdolph 프로젝트 루트 - 바깥쪽 newdolph)
ROS 는 ROS 만을 위한 setup.py 를 요구한다. 이 setup.py 파일은 유저에 의해 직접 실행되거나 pip 등으로 실행되면 (manually) 안 된다고 한다. 함부로 실행시키지 말아달라는 것을 보면 굉장히 무서워 보이지만 사용방법과 사용효과는 우리가 흔히 아는, 순수한 python 의 setup.py 가 하는 역할과 거의 비슷하다 (참고10). ROS 빌드시스템이 이 setup.py 를 실행해 주면 위 예시 디렉터리 구조의 안쪽 newdolph 를 사용 가능한(import 가 가능한) 형태로 만들어준다.
setup.py - ROS 가 ROS 전용으로 이렇게 만들라고 했다. 코드에 드러나다시피 generate_distutils_setup 이라는 함수는 ROS 의 빌드시스템인 캐킨(catkin) 빌드시스템의 일부이다.
## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD from setuptools import setup from catkin_pkg.python_setup import generate_distutils_setup # fetch values from package.xml setup_args = generate_distutils_setup( packages=['newdolph'] ) setup(**setup_args)
Python
복사
나는 작업을 .../newdolph 에서, -p 플래그를 이용해서 python3 -m newdolph.xxx.xxx ... 와 같은 방식으로 스크립트를 실행시키며 디버깅하고 있기 때문에, 스크립트에서 python 의 import 선언은 다음과 같아야 한다 (참고1, 2).
# 뉴돌프 from newdolph.core.streamer import guidance_exception as errors from newdolph.core.util import time_util from newdolph.core.util.argument_util import ArgumentUtil from newdolph.core.meta.model import ModelMetaDataTool from newdolph.core.meta.model import SemanticSegmentationSingleModelMetaData from newdolph.core.meta.structure import SemanticSegmentationSingleClassMetaData from newdolph.core.streamer.base import VisionStreamer from newdolph.core.statetool import condition, state, dashboard
Python
복사
ROS 패키지의 정의 : 패키지 규약 (참고11) 에 따른 구조를 감싸고 있는 하나의 덩어리 python 패키지의 정의 : 모듈(.py) 하나 이상이 모여 있는 디렉터리 (참고12)
그럼 여기서 의문이 생긴다. ROS 의 패키지 규약 (참고11) 에 따라 보기에는 바깥쪽 newdolph 가 '패키지' 일 것이다. 반면 나의 스크립트 혹은 setup.py 에 따라 보기에는 안쪽 newdolph 가 '패키지' 일 것이고, 바깥쪽 newdolph 는 '작업 디렉터리' 일 뿐인 존재가 된다. 만약 사설 ROS 메시지를 사용하고 싶으면, 이 두 개의 라인 중 어떤 것을 사용해야 할까?
# (1) from newdolph.msg import Guidance # (2) from msg import Guidance
Python
복사
ROS 튜토리얼(참고3) 에 따르면 (1) 과 같은 방식이 맞다. 하지만, 나의 작업 디렉터리의 구조의 root 를 바깥쪽 newdolph 라고 본다면, ROS 가 아닌 파이썬 스크립트 단독 실행 관점에서는 newdolph/msgimport msg 에 해당한다. msg 라는 것은 굉장히 흔한 이름이다. from newdolph ...newdolph 라는 이름의 어떤 프로젝트 안에 존재하는 msg 라는 뜻이 정확히 전달되지만, from msg ... 는 모호하기 짝이 없다. 그렇다고 msg 라는 디렉터리를 안쪽 newdolph 디렉터리 안에 놓을 수는 없다. ROS 의 패키지는 권장하는 구조 표준이 있다. 패키지 내부에는 node, msg, launch 디렉터리들이 병렬적으로 놓여 있어야 한다. 나는 이 표준 구조를 해치고 싶지 않다.
다행히 궁금증은 ROS 의 아주 기본적인 동작 방식을 이해함으로써 풀렸다.
rosmsg show 는 ROS 에서 해당 자료형을 감지하고 있는지 확인할 수 있는 간단한 도구이다 (참고4). catkin_make 를 실행하지 않아도, 적절한 위치에 적절한 파일이 추가되었는지 빠르게 확인해볼 수 있다.
rosmsg show newdolph/Guidance
Python
복사
잘 보인다.
위 결과처럼 정상적으로 출력된다면, catkin_make 을 실행해도 된다.
roscd newdolph cd ../.. catkin_make
Python
복사
이렇게 catkin_make 을 실행했을 때 일어나는 동작을 이해해 보자.
기본적으로 커스텀 ROS 메시지를 작성 (참고7) 하고 catkin_make 을 실행하면 메시지파일(자료형) 은 ROS 에서 지원하는 모든 언어에서 동시에 사용할 수 있는 언어로 변환된다. python 사용자를 위해서 ROS 는 ~/dev/catkin_ws/devel/lib/python2.7/dist-packages/newdolph/msg 디렉터리에 _Guidance.py 를 자동으로 만들어 주며 이 파일 내부에 Guidance 클래스가 존재한다 (참고5).
디어의 $PYTHONPATH 환경변수는 다음과 같다.
/home/deer-agx/dev/rosserial_build_ws/devel/lib/python3/dist-packages: /home/deer-agx/dev/geometry2_build_ws/devel/lib/python3/dist-packages: /home/deer-agx/dev/kdl_py3_build_ws/devel/lib/python3/dist-packages: /home/deer-agx/dev/cv_bridge_build_ws/devel/lib/python3/dist-packages: /home/deer-agx/dev/realsense2_ros_build_ws/devel/lib/python2.7/dist-packages: /home/deer-agx/dev/catkin_ws/devel/lib/python2.7/dist-packages: /opt/ros/melodic/lib/python2.7/dist-packages
Python
복사
위에서 보다시피 ~/dev/catkin_ws/devel/lib/python2.7/dist-packages 가 이미 PYTHONPATH 에 등록되어 있기 때문에 (참고2,5), catkin_make 을 실행한 뒤에 ~/catkin_ws/devel/lib/python2.7/dist-packages/newdolph/msg_Guidance.py 가 (Guidance 클래스) 추가되었을 때 newdolph 패키지의 Guidance 라는 메시지 타입을 이용하고 싶은 python 이용자들은 언제나 from newdolph.msg import Guidance 을 통해 해당 메시지 타입으로 토픽을 발신/수신할 수 있게 되는 것이다.
하지만 여기서 재밌는 문제가 나타난다. 에디터가 보기에는 Guidance 클래스의 위치를 잘 잡아낼 수 있음에도 불구하고, 실제로 실행하면 newdolph.msg 를 찾을 수 없다고 나오는 것이다.
이를 해결하기 위해서는 python 이 __init__.py 를 어떻게 활용하는지 이해해야 한다 (참고2). 파이썬 인터프리터가 sys.path 에 존재하는 모든 것들이 패키지일 것이라고 염두해 두고, 어떤 경로 /A/B 에 딱 찾아들어갔을 때 __init__.py 가 존재하는 순간, 이제 어떤 경로 /A/B 에는 B 라는 '고정된' 패키지가 존재한다고 여기고 더이상의 패키지 탐색을 진행하지 않는다 (참고8, 자세한 내용은 참고2, 9 에 있음).
따라서, 파이썬 스크립트가 newdolph 패키지를 발견하더라도, 다시 sys.path 를 돌면서 다른 디렉터리에 존재하는 msg 파이썬 패키지를 발견할 수 있도록 __init__.py 를 삭제해 주었다.
그리고 나서 publisher, subscriber 데모를 만들어 실행해 보면 의도한대로 잘 동작하는 것을 확인할 수 있다.
참고
7.
9.
10.