# 웹캠 장치 확인
ls -l /dev/video*
mkdir -p ~/wsStudyROS # 워크스페이스를 호스트에 보존
xhost +local:docker
docker run -it \
--name ros2_humble_dev \
--privileged \
--gpus all \
--network host \
-e DISPLAY=$DISPLAY \
-e QT_X11_NO_MITSHM=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v $HOME/wsStudyROS:/wsStudyROS \
--device=/dev/video0:/dev/video0 \
--ipc=host \
--shm-size=2g \
-w /wsStudyROS \
ros:humble \
bash
install nano
# 1. 패키지 리스트 업데이트
apt-get update
# 2. ROS 2 유틸리티, OpenCV, pip 설치
apt-get install -y python3-pip wget \
ros-humble-cv-bridge \
ros-humble-rqt-image-view \
ros-humble-rqt-graph \
python3-opencv
# 3. (중요) CUDA 11.8 툴킷 설치 (드라이버가 아닌 라이브러리)
# (이 작업은 PyTorch가 GPU를 사용하기 위해 컨테이너 내부에 꼭 필요합니다)
wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-ubuntu2204-11-8-local_11.8.0-520.61.05-1_amd64.deb
dpkg -i cuda-repo-ubuntu2204-11-8-local_11.8.0-520.61.05-1_amd64.deb
cp /var/cuda-repo-ubuntu2204-11-8-local/cuda-*-keyring.gpg /usr/share/keyrings/
apt-get update
apt-get install -y cuda-toolkit-11-8
# 4. PyTorch (CUDA 11.8용) 및 YOLOv8 설치
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip3 install ultralytics
# 5. CUDA 환경 변수 설정
echo 'export PATH=/usr/local/cuda-11.8/bin${PATH:+:${PATH}}' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}' >> ~/.bashrc
source ~/.bashrc
# 6. (선택) 설치 확인
# python3 verify_pytorch_gpu.py
# (실행하여 PyTorch와 nvcc가 모두 잡히는지 확인)
import sys
import subprocess
import platform
# --- 1. 시스템 환경 정보 확인 (NVIDIA 드라이버 및 CUDA 툴킷) ---
print("="*50)
print("1. 시스템 환경 정보 확인")
print("="*50)
# Ubuntu 버전 확인
try:
# lsb_release -d : 배포판 이름과 버전 (예: Description: Ubuntu 22.04.3 LTS)
result = subprocess.run(
['lsb_release', '-d'],
capture_output=True, text=True, check=True, encoding='utf-8'
)
# "Description:" 부분 제거하고 깔끔하게 출력
print(f"💻 Ubuntu 버전: {result.stdout.strip().split(':')[-1].strip()}")
except Exception as e:
print(f"💻 Ubuntu 버전 확인 실패: {e}")
print(" (참고: 'lsb_release' 명령어가 없거나 Linux가 아닐 수 있습니다.)")
# nvidia-smi (NVIDIA 드라이버) 확인
try:
# nvidia-smi 실행
result = subprocess.run(
['nvidia-smi'],
capture_output=True, text=True, check=True, encoding='utf-8'
)
print("\n✅ nvidia-smi (NVIDIA 드라이버)가 성공적으로 실행되었습니다.")
print("-" * 50)
# nvidia-smi 출력 결과 전체를 보여줍니다.
print(result.stdout)
print("-" * 50)
except FileNotFoundError:
print("\n❌ [오류] 'nvidia-smi' 명령어를 찾을 수 없습니다.")
print(" NVIDIA 드라이버가 설치되지 않았거나 PATH에 잡혀있지 않습니다.")
except subprocess.CalledProcessError as e:
print(f"\n❌ [오류] 'nvidia-smi' 실행 실패:")
print(e.stderr)
# nvcc -V (CUDA Toolkit) 확인
try:
# nvcc -V 실행
result = subprocess.run(
['nvcc', '-V'],
capture_output=True, text=True, check=True, encoding='utf-8'
)
print("\n✅ nvcc -V (CUDA Toolkit)가 성공적으로 실행되었습니다.")
# nvcc -V 출력 결과 중 "release"가 포함된 라인 (예: Cuda compilation tools, release 11.8, V11.8.89)
for line in result.stdout.split('\n'):
if 'release' in line:
print(f" {line.strip()}")
break
except FileNotFoundError:
print("\n❌ [오류] 'nvcc' 명령어를 찾을 수 없습니다.")
print(" CUDA Toolkit이 설치되지 않았거나 PATH에 잡혀있지 않습니다.")
print(" (참고: 3단계 환경 변수 설정을 완료했는지, 터미널을 껐다 켰는지 확인하세요.)")
except subprocess.CalledProcessError as e:
print(f"\n❌ [오류] 'nvcc -V' 실행 실패:")
print(e.stderr)
# --- 2. PyTorch GPU 검증 ---
print("\n" + "="*50)
print("2. PyTorch GPU 검증")
print("="*50)
try:
import torch
print(f"✅ PyTorch가 성공적으로 임포트되었습니다.")
print(f" - PyTorch 버전: {torch.__version__}")
# PyTorch가 인식하는 CUDA 버전 (컴파일 시 사용된 버전)
print(f" - PyTorch가 컴파일된 CUDA 버전: {torch.version.cuda}")
# GPU 사용 가능 여부 확인
is_available = torch.cuda.is_available()
if is_available:
print("\n🎉 \033[92m[성공] PyTorch가 GPU를 성공적으로 인식했습니다.\033[0m")
# GPU 장치 수
print(f" - 사용 가능한 GPU 개수: {torch.cuda.device_count()}")
# 현재 GPU 장치 이름 (RTX 3070이 나와야 함)
current_device = torch.cuda.current_device()
print(f" - 현재 GPU 장치 인덱스: {current_device}")
print(f" - 현재 GPU 장치 이름: {torch.cuda.get_device_name(current_device)}")
# 최종 검증: GPU에서 텐서 연산 수행
print("\n [최종 검증] GPU에서 텐서 연산을 시도합니다...")
try:
# GPU 장치 정의
device = torch.device('cuda')
# CPU에서 텐서 생성
x = torch.tensor([1.0, 2.0, 3.0])
print(f" 1. CPU 텐서 생성: {x.device}")
# 텐서를 GPU로 이동
x_gpu = x.to(device)
print(f" 2. GPU로 텐서 이동: {x_gpu.device}")
# GPU에서 텐서 연산 수행
y_gpu = x_gpu + 10.0
print(f" 3. GPU에서 연산 수행 (결과): {y_gpu}")
print("\n \033[92m[최종 성공] GPU에서의 텐서 생성, 이동, 연산이 모두 확인되었습니다.\033[0m")
except Exception as e:
print(f"\n \033[91m[오류] GPU 연산 중 문제가 발생했습니다: {e}\033[0m")
else:
print("\n❌ \033[91m[실패] PyTorch가 GPU를 인식하지 못합니다. (torch.cuda.is_available() == False)\033[0m")
print(" [확인 사항]")
print(" 1. 'nvidia-smi'가 정상 작동하는지 확인하세요.")
print(f" 2. 설치된 PyTorch가 GPU 버(cu118)가 맞는지 확인하세요. (현재: {torch.version.cuda})")
print(" (만약 'cpu'로 나온다면, PyTorch를 GPU 버전으로 다시 설치해야 합니다.)")
except ImportError:
print("\n❌ \Code [오류] PyTorch가 설치되어 있지 않습니다. ('import torch' 실패)")
print(" CUDA 11.8 버전에 맞는 PyTorch를 설치해주세요.")
print(" 설치 명령어 예시:")
print(" pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118")
except Exception as e:
print(f"\n❌ [알 수 없는 오류] 스크립트 실행 중 오류가 발생했습니다: {e}")
print("\n" + "="*50)
print("검증이 완료되었습니다.")
print("="*50)
python3 verify_pytorch_gpu.py

# 1. 워크스페이스 및 src 디렉터리 생성
mkdir -p /wsStudyROS/ros2_ws/src
cd /wsStudyROS/ros2_ws/src
# 2. 커스텀 메시지 패키지 생성
ros2 pkg create --build-type ament_cmake yolo_msgs
# 3. YOLO 노드 패키지 생성
ros2 pkg create --build-type ament_python yolo_detector
# YOLO 검출 결과(클래스, 확률, 좌표)를 담을 메시지를 정의합니다.
# 1. msg 디렉터리 생성
mkdir /wsStudyROS/ros2_ws/src/yolo_msgs/msg
nano /wsStudyROS/ros2_ws/src/yolo_msgs/msg/BoundingBox.msg
# YOLOv8 검출 결과 (개별 박스)
string class_id
float32 probability
int64 x_min
int64 y_min
int64 x_max
int64 y_max
Ctrl + O > Enter > Ctrl + x
nano /wsStudyROS/ros2_ws/src/yolo_msgs/msg/BoundingBoxes.msg
# BoundingBox 메시지의 배열
std_msgs/Header header
yolo_msgs/BoundingBox[] detections
Ctrl + O > Enter > Ctrl + x
nano /wsStudyROS/ros2_ws/src/yolo_msgs/package.xml
기존의 것을 아래로 교체

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>yolo_msgs</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hglab99@gmail.com">root</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
**<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<depend>std_msgs</depend>**
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Ctrl + O > Enter > Ctrl + x
nano /wsStudyROS/ros2_ws/src/yolo_msgs/CMakeLists.txt
기존의 것을 아래로 교체

cmake_minimum_required(VERSION 3.8)
project(yolo_msgs)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/BoundingBox.msg"
"msg/BoundingBoxes.msg"
DEPENDENCIES std_msgs
)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
install(
DIRECTORY "msg"
DESTINATION "share/${PROJECT_NAME}/"
)
ament_package()
mkdir /wsStudyROS/ros2_ws/src/yolo_detector/yolo_detector
nano /wsStudyROS/ros2_ws/src/yolo_detector/yolo_detector/webcam_yolo_pub.py
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
from ultralytics import YOLO
# 1. 커스텀 메시지 임포트
from yolo_msgs.msg import BoundingBox, BoundingBoxes
class YoloPublisher(Node):
def __init__(self):
super().__init__('yolo_publisher_node')
# 2. YOLO 모델 로드 (v8n이 가장 가볍고 빠름)
# GPU가 있으면 자동으로 CUDA를 사용합니다.
self.model = YOLO('yolov8n.pt')
self.get_logger().info('YOLOv8 model loaded successfully.')
# 3. OpenCV 웹캠 설정 (컨테이너에 /dev/video0가 연결되어 있어야 함)
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
self.get_logger().error('Failed to open webcam (device 0).')
rclpy.shutdown()
# 4. ROS 2 퍼블리셔 및 CvBridge 생성
self.annotated_image_pub = self.create_publisher(Image, '/video/image_annotated', 10)
self.detection_pub = self.create_publisher(BoundingBoxes, '/yolo/detections', 10)
self.bridge = CvBridge()
# 5. 메인 루프 (초당 30회)
self.timer = self.create_timer(1.0 / 30.0, self.timer_callback)
def timer_callback(self):
ret, frame = self.cap.read()
if not ret:
self.get_logger().warn('Failed to read frame from webcam.')
return
# 6. YOLO 추론
results = self.model(frame)
# 7. 검출 결과(BoundingBoxes) 메시지 생성
detection_msg = BoundingBoxes()
detection_msg.header.stamp = self.get_clock().now().to_msg()
annotated_frame = frame.copy() # 원본 프레임 복사
for result in results:
boxes = result.boxes
for box in boxes:
b = BoundingBox()
class_id_int = int(box.cls)
b.class_id = self.model.names[class_id_int]
b.probability = float(box.conf)
coords = box.xyxy[0].cpu().numpy().astype(int)
b.x_min, b.y_min, b.x_max, b.y_max = map(int, coords)
detection_msg.detections.append(b)
# [목표 1] 실시간 영상에 바운딩 박스 그리기
cv2.rectangle(annotated_frame, (b.x_min, b.y_min), (b.x_max, b.y_max), (0, 255, 0), 2)
cv2.putText(annotated_frame, f'{b.class_id}: {b.probability:.2f}',
(b.x_min, b.y_min - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 8. 메시지 퍼블리시
# [목표 1] 주석이 달린 이미지 퍼블리시
self.annotated_image_pub.publish(self.bridge.cv2_to_imgmsg(annotated_frame, "bgr8"))
# [목표 3] 검출 결과 퍼블리시
self.detection_pub.publish(detection_msg)
self.get_logger().info(f'Published {len(detection_msg.detections)} detections.')
def main(args=None):
rclpy.init(args=args)
yolo_publisher = YoloPublisher()
rclpy.spin(yolo_publisher)
yolo_publisher.cap.release()
yolo_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
nano /wsStudyROS/ros2_ws/src/yolo_detector/yolo_detector/detection_sub.py
import rclpy
from rclpy.node import Node
from yolo_msgs.msg import BoundingBoxes
class DetectionSubscriber(Node):
def __init__(self):
super().__init__('detection_subscriber_node')
# 1. '/yolo/detections' 토픽을 구독
self.subscription = self.create_subscription(
BoundingBoxes,
'/yolo/detections',
self.listener_callback,
10)
self.get_logger().info('Detection subscriber node started. Waiting for data...')
def listener_callback(self, msg):
# 2. [목표 3] 콜백 함수: 수신한 데이터를 터미널에 출력
self.get_logger().info(f'--- New Detections Received (Total: {len(msg.detections)}) ---')
for detection in msg.detections:
self.get_logger().info(
f" Class: {detection.class_id}, "
f"Prob: {detection.probability:.2f}, "
f"Box: [{detection.x_min}, {detection.y_min}, {detection.x_max}, {detection.y_max}]"
)
def main(args=None):
rclpy.init(args=args)
detection_subscriber = DetectionSubscriber()
rclpy.spin(detection_subscriber)
detection_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
nano /wsStudyROS/ros2_ws/src/yolo_detector/package.xml
기존의 것을 아래로 교체

GNU nano 6.2 /wsStudyROS/ros2_ws/src/yolo_detector/package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>yolo_detector</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="hglab99@gmail.com">root</maintainer>
<license>TODO: License declaration</license>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<depend>rclpy</depend>
<depend>sensor_msgs</depend>
<depend>cv_bridge</depend>
<depend>yolo_msgs</depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
nano /wsStudyROS/ros2_ws/src/yolo_detector/setup.py
기존의 것을 아래로 교체

GNU nano 6.2 /wsStudyROS/ros2_ws/src/yolo_detector/setup.py * M
from setuptools import find_packages, setup
package_name = 'yolo_detector'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='root',
maintainer_email='hglab99@gmail.com',
description='TODO: Package description',
license='TODO: License declaration',
extras_require={
'test': [
'pytest',
],
},
entry_points={
'console_scripts': [
'yolo_publisher = yolo_detector.webcam_yolo_pub:main',
'detection_subscriber = yolo_detector.detection_sub:main',
],
},
)
cd /wsStudyROS/ros2_ws
# 1. (중요) 커스텀 메시지 패키지를 먼저 빌드
colcon build --packages-select yolo_msgs
# 2. (중요) 빌드된 메시지를 셸에 인식시킴
source install/setup.bash
# 3. 나머지 패키지(yolo_detector) 빌드
colcon build --packages-up-to yolo_detector
# 4. (중요) 셸에 최종 결과 인식
source install/setup.bash




# ROS 2 Humble 기본 환경 로드
source /opt/ros/humble/setup.bash
# 워크스페이스 인식 (필수)
source /wsStudyROS/ros2_ws/install/setup.bash
# Publisher 노드를 백그라운드(&)로 실행
ros2 run yolo_detector yolo_publisher
xhost +local:docker
docker exec -it ros2_humble_dev bash
# ROS 2 Humble 기본 환경 로드
source /opt/ros/humble/setup.bash
# 워크스페이스 인식 (필수)
source /wsStudyROS/ros2_ws/install/setup.bash
# Subscriber 노드를 포어그라운드로 실행
ros2 run yolo_detector detection_subscriber
넘파이 버전 오류 발생시
Docker Container 내부에서
pip3 install numpy==1.26.4
xhost +local:docker
docker exec -it ros2_humble_dev bash
# ROS 2 Humble 기본 환경 로드
source /opt/ros/humble/setup.bash
# 워크스페이스 인식
source /wsStudyROS/ros2_ws/install/setup.bash
# RQT 이미지 뷰 실행
rqt_image_view
만약 rqt_image 등과 같은 GUI 도구 미설치시
# 1. (선택) 패키지 리스트 업데이트
apt-get update
# 2. (필수) RQT 도구 설치 (image_view와 graph 둘 다 설치)
apt-get install -y ros-humble-rqt-image-view ros-humble-rqt-graph
그래도 안되면
apt-get install --reinstall ros-humble-rqt-image-view ros-humble-rqt-graph
hash -r
source /opt/ros/humble/setup.bash
그래도 안되면

xhost +local:docker
docker exec -it ros2_humble_dev bash
# ROS 2 Humble 기본 환경 로드
source /opt/ros/humble/setup.bash
# 워크스페이스 인식
source /wsStudyROS/ros2_ws/install/setup.bash
# RQT 그래프 실행
rqt_graph


