1. 코드 분석
(1) 테이블 좌표 및 네비게이션 코드
위 코드에서 get_table_coordinates()는 테이블 번호와 해당 테이블의 (x, y) 좌표를 매핑합니다. 예를 들어, 테이블 1의 좌표는 (1.1, 0.8)로 설정되어 있습니다.
(2) 주요 노드 설명
각 노드별 주요 기능을 간단히 정리합니다:
- kitchen_display:
- 테이블에서 받은 주문을 처리하며, 로봇의 네비게이션 명령을 실행합니다.
- navigate_to_pose_send_goal 함수를 통해 TurtleBot3가 지정된 좌표로 이동합니다.
- 로봇 움직임 알고리즘은 A* 를 기반으로 작성하였습니다.
- order_database_server:
- 주문 정보를 저장 및 관리하며, 데이터베이스와의 연동을 처리합니다.
- system_logging:
- 각 노드에서 발생하는 이벤트 및 시스템 로그 데이터를 저장합니다.
- table_order_manager:
- 사용자가 테이블 번호를 선택하고 주문을 입력할 수 있는 인터페이스를 제공합니다.
(3) Docker를 활용한 환경 설정
이 프로젝트는 Docker를 통해 모든 의존성과 환경을 컨테이너화하여 플랫폼 독립적으로 실행할 수 있도록 설계되었습니다. 주요 Docker 설정은 아래와 같습니다.
FROM ros:humble
# 환경 변수 설정
ENV DEBIAN_FRONTEND=noninteractive
ENV TURTLEBOT3_MODEL=waffle_pi
ENV ROS_LOCALHOST_ONLY=0
# 필요한 시스템 패키지 및 의존성 설치
RUN apt-get update && apt-get install -y \
ros-humble-gazebo-ros-pkgs \
ros-humble-gazebo-plugins \
ros-humble-nav2-map-server \
ros-humble-nav2-bringup \
ros-humble-rviz2 \
ros-humble-tf2-tools \
ros-humble-dynamixel-sdk \
ros-humble-turtlebot3-gazebo \
ros-humble-turtlebot3-navigation2 \
ros-humble-rclpy \
python3-colcon-common-extensions \
python3-pip \
git \
wget \
nano \
libpulse-mainloop-glib0 \
pulseaudio \
libasound2-dev \
libgl1-mesa-glx \
libgl1-mesa-dri \
x11-apps \
libosmesa6-dev \
mesa-utils \
imagemagick \
fonts-nanum \
&& rm -rf /var/lib/apt/lists/*
# Python 의존성 설치
RUN pip3 install --no-cache-dir \
PyQt5 \
mysql-connector-python \
&& rm -rf /root/.cache/pip
# 작업 디렉토리 설정
WORKDIR /project_ws
# 소스 코드 및 스크립트 복사
COPY src src
COPY launch_all.sh launch_all.sh
# 실행 스크립트에 실행 권한 부여
RUN chmod +x launch_all.sh
# ROS 종속성 설치
RUN rosdep update && \
rosdep install --from-paths src --ignore-src -r -y || true
# 워크스페이스 빌드
RUN /bin/bash -c "source /opt/ros/humble/setup.bash && colcon build --symlink-install"
# 환경 설정을 위한 설정 파일 추가
RUN echo "source /project_ws/install/local_setup.bash" >> ~/.bashrc
RUN echo "source /usr/share/gazebo/setup.sh" >> ~/.bashrc
RUN echo "export TURTLEBOT3_MODEL=waffle_pi" >> ~/.bashrc
RUN echo "export ROS_LOCALHOST_ONLY=0" >> ~/.bashrc
# PulseAudio 설정
ENV PULSE_SERVER=unix:/run/pulse/native
RUN mkdir -p /run/pulse && \
chmod 777 /run/pulse
# 실행 스크립트를 엔트리포인트로 설정
ENTRYPOINT ["./launch_all.sh"]
해당 dockerfile로 모든 의존성을 설치 후 docker hub에 push 하였습니다.
docker pull seonghoham/foodies:3.5 해당 명령어로 docker file을 받을 수 있습니다.
실행 코드
import os
import subprocess
import sys
def run_command(command, cwd=None):
"""
주어진 명령어를 실행하고, 출력과 오류를 실시간으로 표시합니다.
"""
try:
print(f"실행 중: {' '.join(command)}")
result = subprocess.run(command, cwd=cwd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
except subprocess.CalledProcessError as e:
print(f"명령어 실행 중 오류 발생: {' '.join(command)}", file=sys.stderr)
if e.stdout:
print(e.stdout)
if e.stderr:
print(e.stderr, file=sys.stderr)
sys.exit(e.returncode)
def container_exists(container_name):
"""
주어진 이름의 컨테이너가 존재하는지 확인합니다.
"""
try:
result = subprocess.run(['docker', 'ps', '-a', '-q', '-f', f'name={container_name}'], check=True, stdout=subprocess.PIPE, text=True)
return bool(result.stdout.strip())
except subprocess.CalledProcessError as e:
print(f"컨테이너 확인 중 오류 발생: {container_name}", file=sys.stderr)
sys.exit(e.returncode)
def main():
# 현재 스크립트의 절대 경로를 기준으로 설정
current_dir = os.path.abspath(os.getcwd())
workdir = os.path.join(current_dir, 'project_ws')
# 작업 디렉토리 확인 및 생성
if not os.path.exists(workdir):
try:
os.makedirs(workdir)
print(f"디렉토리 생성: {workdir}")
except Exception as e:
print(f"디렉토리 생성 실패: {workdir}", file=sys.stderr)
print(e, file=sys.stderr)
sys.exit(1)
else:
print(f"디렉토리 존재: {workdir}")
# 작업 디렉토리로 이동
try:
os.chdir(workdir)
print(f"작업 디렉토리로 이동: {workdir}")
except Exception as e:
print(f"디렉토리 이동 실패: {workdir}", file=sys.stderr)
print(e, file=sys.stderr)
sys.exit(1)
# 호스트의 현재 디렉토리 기준 상대 경로 사용
map_yaml_host = os.path.join(workdir, 'map.yaml')
map_pgm_host = os.path.join(workdir, 'map.pgm')
# foodies_ros2 컨테이너가 존재하는지 확인
commands = [
['docker', 'pull', 'seonghoham/foodies:3.5'] # 이미지 다운로드
]
if container_exists('foodies_ros2'):
# 컨테이너가 존재하면 중지 및 삭제 명령 추가
commands.extend([
['docker', 'stop', 'foodies_ros2'],
['docker', 'rm', 'foodies_ros2']
])
else:
print("기존에 실행된 'foodies_ros2' 컨테이너가 없습니다. 중지 및 삭제 단계 생략.")
# Docker 실행 명령어 추가
commands.append([
'docker', 'run', '-it',
'--name', 'foodies_ros2',
'--env', 'DISPLAY',
'--env', 'QT_X11_NO_MITSHM=1',
'--env', 'PULSE_SERVER=unix:/run/pulse/native',
'--volume', '/tmp/.X11-unix:/tmp/.X11-unix:rw',
'--volume', f"{map_yaml_host}:/project_ws/map.yaml:ro",
'--volume', f"{map_pgm_host}:/project_ws/map.pgm:ro",
'--volume', f"{os.getenv('XDG_RUNTIME_DIR')}/pulse/native:/run/pulse/native" if os.getenv('XDG_RUNTIME_DIR') else '/run/pulse/native:/run/pulse/native',
'--volume', '/etc/machine-id:/etc/machine-id',
'--network', 'host',
'seonghoham/foodies:3.5'
])
# 각 명령어 순차적으로 실행
for cmd in commands:
run_command(cmd, cwd=workdir)
print("\n모든 명령어가 성공적으로 실행되었습니다.")
if __name__ == "__main__":
main()
해당 파이썬 파일로 docker hub 에서 pull 하고 ROS2 humble
프로젝트 파일을 실행 할 수 있습니다.
짧은 시간이였지만 완성도 있는 산출물을 위해 노력하였습니다.
'ROS2' 카테고리의 다른 글
[두산로보틱스] 멀티 로봇 제어 및 Digital Twin 프로젝트 (0) | 2025.01.05 |
---|---|
[두산로보틱스] AMR 관제 시스템 구축 프로젝트 (0) | 2025.01.05 |
[두산로보틱스] 협동로봇 스포츠 스태킹 프로젝트 (0) | 2025.01.05 |
[두산로보틱스] 협동로봇 젠가 쌓기 (1) | 2025.01.05 |
Turtlebot3 자율주행 서빙로봇 프로젝트 [1] (0) | 2024.11.17 |