AprilTag ๊ธฐ๋ฐ ์ค์๊ฐ 6-DOF ํฌ์ฆ ์ถ์ ์์คํ
- ๋ฐ๋ชจ
- ๊ฐ์
- ์ฃผ์ ๊ธฐ๋ฅ
- ์์คํ ๊ตฌ์กฐ
- ํ๋ก์ ํธ ๊ตฌ์กฐ
- ๋น ๋ฅธ ์์
- ์์คํ ์๊ตฌ์ฌํญ
- ์ค์น
- ๋น๋
- ์คํ
- ์ฌ์ฉ๋ฒ
- ์ค์
- API / ROS ์ธํฐํ์ด์ค
- ๋ฌธ์ ํด๊ฒฐ
- ๋ผ์ด์ ์ค
- Maintainer
AprilTag Pose ROS2๋ ROS2 ํธํ RGB ์นด๋ฉ๋ผ์ AprilTag ๋ง์ปค๋ฅผ ํ์ฉํ์ฌ ํ๊ฒ ๊ฐ์ฒด์ 6์์ ๋(6-DOF) ์์น์ ์์ธ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ถ์ ํ๋ ์์คํ ์ ๋๋ค. ๋ค์ค ๋ง์ปค ์ตํฉ ์๊ณ ๋ฆฌ์ฆ(Multi-tag PnP)์ ์ ์ฉํ์ฌ ๋จ์ผ ๋ง์ปค ๋๋น ๋์ ์ ํ๋๋ฅผ ๋ฌ์ฑํ๋ฉฐ, SLERP ๊ธฐ๋ฐ ์ฟผํฐ๋์ธ ํํฐ๋ก ์์ ์ ์ธ ํฌ์ฆ ์ถ๋ ฅ์ ์ ๊ณตํฉ๋๋ค.
- apriltag_pose_estimator (C++): AprilTag ๊ฒ์ถ, ๋ค์ค ๋ง์ปค ํฌ์ฆ ์ตํฉ, SLERP ํํฐ๋ง
- apriltag_pose_visualizer (Python): ์ค์๊ฐ ๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ, Roll/Pitch/Yaw ๊ณ์ฐ, 3D ์ฐ์ ๋
- apriltag_pose_estimator_msgs (C++): ROS2 ์ปค์คํ ๋ฉ์์ง ๋ฐ ์๋น์ค ์ธํฐํ์ด์ค
์นด๋ฉ๋ผ ๋๋ผ์ด๋ฒ๋ ๋ณธ ํจํค์ง์ ํฌํจ๋์ด ์์ง ์์ต๋๋ค.
sensor_msgs/Image+CameraInfo๋ฅผ ํผ๋ธ๋ฆฌ์ํ๋ ROS2 ์นด๋ฉ๋ผ ๋๋ผ์ด๋ฒ๋ฅผ ์ฌ์ฉ ํ๊ฒฝ์ ๋ง๊ฒ ๋ณ๋๋ก ์คํํ์ธ์.
- ๋ก๋ด ๋น์ ์์คํ ์ ๊ฐ์ฒด ์์น ์ถ์
- ์์จ ๋ค๋น๊ฒ์ด์ ์ ์ํ ๊ณต๊ฐ ์ธ์
- ์ฐ์ ์๋ํ ์์คํ ์ ์ ๋ฐ ํฌ์ง์ ๋
- AR/VR ๋ง์ปค ๊ธฐ๋ฐ ์์น ์ถ์
- ์ฐ๊ตฌ ๊ฐ๋ฐ ๋ฐ ๊ต์ก
- ๋ค์ค ๋ง์ปค PnP ์ตํฉ: ๋ณต์์ AprilTag ์ฝ๋๋ฅผ ๋์์ ํ์ฉํ์ฌ ๋จ์ผ ๋ง์ปค ๋๋น ํฅ์๋ 6-DOF ํฌ์ฆ ์ถ์
- SLERP ์ฟผํฐ๋์ธ ํํฐ: Gimbal lock ์๋ ํ์ ๋ณด๊ฐ๊ณผ ์ ์ํ EMA๋ก ํ๋ ์ ๊ฐ ์งํฐ ์ต์ (์ฐ์ 5ํ๋ ์ ๋ฏธ๊ฒ์ถ ์ ์๋ ๋ฆฌ์ ๋์ด ์ฌ๋ฑ์ฅ ์ ์ pose์ ๋ธ๋ ๋ฉ ๋ฐฉ์ง)
- Reprojection Error ๊ธฐ๋ฐ ์ด์์น ์ ๊ฑฐ: 15ํฝ์ ์๊ณ๊ฐ์ผ๋ก ๋ถ๋ ๊ฒ์ถ ์๋ ํํฐ๋ง (์ด์ ์ ํจ pose ์ฌ์ฌ์ฉ์ ์ฐ์ 5ํ๋ ์์ผ๋ก ์ ํ, ์ฌ์ฌ์ฉ pose๋ ์ถ ์๊ฐํ ์ ์ธ)
- ๊ณ ์ฑ๋ฅ C++ ํ์ดํ๋ผ์ธ: ์ค์๊ฐ ์ฒ๋ฆฌ์ ์ ํฉํ ๋ค์ดํฐ๋ธ ๊ตฌํ (5-8ms/frame @ 1280x720)
- Multi-Group ๋์ ์ถ์ : ํ ์นด๋ฉ๋ผ ํ๋ ์์์ ์ฌ๋ฌ ๋ง์ปค ๊ทธ๋ฃน์ base pose๋ฅผ ๋์์ publish (๊ทธ๋ฃน๋ณ ๋ ๋ฆฝ SLERP ํํฐ)
- YAML ๊ธฐ๋ฐ ์ ์ฐํ ์ค์ : ๋ง์ปค ID, ํฌ๊ธฐ, ๊ทธ๋ฃน๋ณ ์คํ์ ์ ์ค์ ํ์ผ๋ก ๊ด๋ฆฌ
- ์ค์๊ฐ 3D ์๊ฐํ: ์์น(tvec) ๋ฐ ํ์ (RPY) ํ์คํ ๋ฆฌ์ 3D ์ฐ์ ๋์ ํต๊ณ ์ค๋ฒ๋ ์ด
- ์๋น์ค ์ธํฐํ์ด์ค: ์จ๋๋งจ๋ ํฌ์ฆ ์ฟผ๋ฆฌ๋ฅผ ์ํ ROS2 ์๋น์ค ์ ๊ณต
- Docker ์ง์: ๋น๋ ๋ฐ ์คํ ์คํฌ๋ฆฝํธ ํฌํจ, ์ํด๋ฆญ ์ปจํ ์ด๋ ํ๊ฒฝ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AprilTag Pose ROS2 System โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ROS2 RGB Camera
โ
โ USB 3.0
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โ Camera Driver Node โ
โ - Camera streaming โ
โ - Intrinsics publish โ
โโโโโโโโโโโโโฌโโโโโโโโโโโโ
โ sensor_msgs/Image
โ sensor_msgs/CameraInfo
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ sensor_msgs/Image
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ
โ apriltag_pose_ โ TagDetection โ apriltag_pose_ โ
โ estimator โโโโโโโโโโโโโโโโโถโ visualizer โ (Python)
โ - Tag detection โ โ - Overlay drawing โ
โ - Multi-tag PnP โ โ - RPY calculation โ
โ - SLERP filtering โ โ - 3D scatter plots โ
โ - Outlier rejection โ โโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโฌโโโโโโโโโโโโ
โ /target_poses (PoseArray, group_names order)
โ /group_names (latched CSV)
โ /group_status (per-group valid bits)
โ /detection_image (BGR8 overlay)
โ /tag_detections (raw, debug โ visualizer ์
๋ ฅ)
โผ
Service Client
(target_point_pose query, group_name dispatch)
AprilTag_Pose_ROS2/
โโโ apriltag_pose_estimator/ # ํฌ์ฆ ์ถ์ ํจํค์ง (C++)
โ โโโ src/
โ โ โโโ pose_estimator_node.cpp # ROS2 ๋
ธ๋ ๊ตฌํ
โ โ โโโ april_tag_detector.cpp # AprilTag ๊ฒ์ถ ๋ํผ
โ โ โโโ multi_tag_pose_estimator.cpp # ๋ค์ค ๋ง์ปค PnP ์ตํฉ
โ โ โโโ slerp_pose_filter.cpp # ์ฟผํฐ๋์ธ SLERP ํํฐ
โ โ โโโ main.cpp # ์ํธ๋ฆฌ ํฌ์ธํธ
โ โโโ include/apriltag_pose_estimator/
โ โ โโโ pose_estimator_node.hpp
โ โ โโโ april_tag_detector.hpp
โ โ โโโ multi_tag_pose_estimator.hpp
โ โ โโโ slerp_pose_filter.hpp
โ โ โโโ tag_config.hpp # ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ ์
โ โโโ config/
โ โ โโโ pose_estimator.yaml # ์ค์ ํ์ผ
โ โโโ launch/
โ โ โโโ apriltag_estimator.launch.py # ๋ฐ์น ํ์ผ
โ โโโ scripts/
โ โ โโโ jitter_probe.py # ์๋น์ค ์๋ต ์งํฐ ์ธก์ ์คํฌ๋ฆฝํธ
โ โโโ test/
โ โ โโโ test_multi_tag_pose_estimator.cpp # estimator ๋จ์ ํ
์คํธ (gtest)
โ โโโ CMakeLists.txt
โ โโโ package.xml
โโโ apriltag_pose_estimator_msgs/ # ์ปค์คํ
์ธํฐํ์ด์ค
โ โโโ msg/
โ โ โโโ TagDetection.msg
โ โโโ srv/
โ โ โโโ TargetPointPose.srv
โ โโโ CMakeLists.txt
โ โโโ package.xml
โโโ apriltag_pose_visualizer/ # ์๊ฐํ ํจํค์ง (Python)
โ โโโ apriltag_pose_visualizer/
โ โ โโโ pose_visualizer_node.py
โ โโโ setup.py
โ โโโ package.xml
โโโ realsense-ros/ # RealSense ROS2 ๋๋ผ์ด๋ฒ (์ธ๋ถ ์ ์ฅ์, git ๋ฏธ์ถ์ )
โโโ docker/ # Docker ํ๊ฒฝ
โ โโโ Dockerfile
โ โโโ build.sh
โ โโโ run.sh
โ โโโ entrypoint.sh
โ โโโ commands.sh
โ โโโ config.sh.example
โโโ docs/
โโโ launch_ros_graph.png
# 1. Docker ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
docker pull hhanoo/project:apriltag-pose-ros2-humble
# 2. ์ปจํ
์ด๋ ์คํ (X11 ํฌ์๋ฉ ํฌํจ)
cd AprilTag_Pose_ROS2/docker
./run.sh
# 3. ์ปจํ
์ด๋ ๋ด๋ถ์์ ๋น๋ (RealSense ๋๋ผ์ด๋ฒ๋ ์ํฌ์คํ์ด์ค์ ํฌํจ๋์ด ์์)
cd /ros2_ws
colcon build
# 4. ์คํ (ํฐ๋ฏธ๋ 2๊ฐ)
ros2 launch <your_camera_driver> ... # ์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ (RealSense๋ ๋ฏธ๋ฆฌ ์ ์๋ run-camera ์ฌ์ฉ)
run-estimator # ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py# 1. ์์กด์ฑ ์ค์น ๋ฐ ๋น๋ (์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ๋ ์ฌ์ ์ค์น ํ์)
cd ~/ros2_ws
rosdep install --from-paths src --ignore-src -r -y
colcon build
source install/setup.bash
# 2. ์คํ (ํฐ๋ฏธ๋ 2๊ฐ)
ros2 launch <your_camera_driver> ...
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py| ํญ๋ชฉ | ์๊ตฌ์ฌํญ |
|---|---|
| OS | Ubuntu 22.04 LTS |
| ROS2 | Humble |
| Python | 3.10+ |
| C++ | C++17 (GCC 9+, Clang 10+) |
| ํญ๋ชฉ | ์ฌ์ |
|---|---|
| Camera | ROS2 ํธํ RGB ์นด๋ฉ๋ผ (sensor_msgs/Image + CameraInfo ํผ๋ธ๋ฆฌ์) |
| USB | USB 3.0 ํฌํธ (ํ์) |
| CPU | ๋ฉํฐ์ฝ์ด ํ๋ก์ธ์ (Intel i5+ ๊ถ์ฅ) |
| RAM | 4GB ์ด์ (8GB ๊ถ์ฅ) |
์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ:
libapriltag-dev- AprilTag C ๋ผ์ด๋ธ๋ฌ๋ฆฌlibopencv-dev- OpenCV 4.xlibeigen3-dev- Eigen3 ์ ํ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ- ์ฌ์ฉ ์นด๋ฉ๋ผ์ ๋ง๋ ROS2 ๋๋ผ์ด๋ฒ (๋ณ๋ ์ค์น)
Python ํจํค์ง:
numpy(<2.0, cv_bridge ํธํ)dt-apriltags(==3.1.7)opencv-python(==4.10.0.84)matplotlib(3D ์๊ฐํ)
Docker๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์์กด์ฑ์ด ์๋์ผ๋ก ์ค์น๋ฉ๋๋ค.
# 1. Docker ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
docker pull hhanoo/project:apriltag-pose-ros2-humble
# 2. ์ปจํ
์ด๋ ์คํ
cd AprilTag_Pose_ROS2/docker
./run.sh์ง์ ๋น๋ (๊ฐ๋ฐ์์ฉ)
# 0. ํ๋ก์ ํธ ๋ฃจํธ๋ก ์ด๋
cd AprilTag_Pose_ROS2/docker
# 1. ์ค์ ํ์ผ ์์ฑ ํ IMAGE_NAME์ ๋ก์ปฌ ์ด๋ฆ์ผ๋ก ๋ณ๊ฒฝ
cp config.sh.example config.sh
# config.sh์์ IMAGE_NAME="apriltag-pose-ros2-humble" ๋ก ์์
# 2. Docker ์ด๋ฏธ์ง ๋น๋
./build.sh
# 3. ์ปจํ
์ด๋ ์คํ
./run.shROS2 Humble ๊ณต์ ์ค์น ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์ธ์.
sudo apt update
sudo apt install -y \
build-essential cmake git \
python3-pip python3-colcon-common-extensions python3-rosdep \
libapriltag-dev libopencv-dev libeigen3-dev์ฌ์ฉ ์นด๋ฉ๋ผ์ ๋ง๋ ROS2 ๋๋ผ์ด๋ฒ๋ ๋ณ๋๋ก ์ค์นํ์ธ์.
pip3 install \
setuptools==58.2.0 packaging==21.3 wheel \
'numpy>=1.21.0,<2.0' \
dt-apriltags==3.1.7 \
opencv-python==4.10.0.84cd ~/ros2_ws
rosdep update
rosdep install --from-paths src --ignore-src -r -ycd ~/ros2_ws
colcon build --symlink-install
source install/setup.bashcolcon build --symlink-install --packages-select apriltag_pose_estimator
source install/setup.bashcolcon build --cmake-args -DCMAKE_BUILD_TYPE=Releasecd ~/ros2_ws
rm -rf build install log
colcon build --symlink-install
source install/setup.bash์คํ ์์๋ฅผ ๋ฐ๋์ ์ค์ํ์ธ์. apriltag_estimator.launch.py์๋ ์นด๋ฉ๋ผ๊ฐ ํฌํจ๋์ด ์์ง ์์ต๋๋ค.
# ํฐ๋ฏธ๋ 1: ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ ์คํ (๋จผ์ )
source ~/ros2_ws/install/setup.bash
ros2 launch <your_camera_driver> ...
# ํฐ๋ฏธ๋ 2: AprilTag ํฌ์ฆ ์ถ์ ๊ธฐ ์คํ
source ~/ros2_ws/install/setup.bash
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py
# ํฐ๋ฏธ๋ 3 (์ ํ): ์๊ฐํ ๋
ธ๋ ์คํ
source ~/ros2_ws/install/setup.bash
ros2 run apriltag_pose_visualizer pose_visualizer_node# ์ปค์คํ
์นด๋ฉ๋ผ ํ ํฝ ์ง์
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py \
camera_topic:=camera/camera/color/image_raw \
camera_info_topic:=camera/camera/color/camera_info
# ํ๊ทธ ํ๋ผ๋ฏธํฐ ๋ณ๊ฒฝ
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py \
tag_size:=0.05 \
tag_family:=tag36h11
# ์ปค์คํ
์ค์ ํ์ผ ์ฌ์ฉ
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py \
config_file:=/path/to/custom_config.yaml
# GUI ์๋์ฐ ๋นํ์ฑํ (Docker / headless ํ๊ฒฝ)
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py \
show_service_result_window:=false๊ถ์ฅ: ์ง์
docker exec๋ก ์ปจํ ์ด๋์ ์ง์ ํ์ง ๋ง๊ณ ํญ์ run.sh๋ฅผ ์ฌ์ฉํ์ธ์.
run.sh๋ ๋์ปค ์ด๋ฏธ์ง ํ์ธ ยท X11 ๊ถํ ยท ๋ง์ดํธ ยท ํธ์คํธ ๊ถํ ๋ณต์(HOST_UID/HOST_GID) ยท ๊ธฐ์กด ์ปจํ ์ด๋ ์ฌ์ฌ์ฉ์ ํ ๋ฒ์ ์ฒ๋ฆฌํฉ๋๋ค.
docker pull hhanoo/project:apriltag-pose-ros2-humble
cd docker
./run.sh
# ์ปจํ
์ด๋ ๋ด๋ถ
cd /ros2_ws
colcon build && source install/setup.bash
ros2 launch <your_camera_driver> ... # ์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ (RealSense๋ run-camera ์ฌ์ฉ)์ ์ฒด command ์ ์๋ commands.sh๋ฅผ ์ฐธ๊ณ ํ์ธ์.
| Command | ์ค๋ช | ์ฐธ๊ณ |
|---|---|---|
build |
์ํฌ์คํ์ด์ค ๋น๋ | colcon build --symlink-install + overlay ์๋ source |
run-tests |
๋จ์ ํ ์คํธ ์คํ | colcon test (apriltag ํจํค์ง) + ๊ฒฐ๊ณผ ์์ฝ |
run-camera |
RealSense ์นด๋ฉ๋ผ ์คํ | rs_launch.py (1280x720x30, depth ๋นํ์ฑ) |
run-estimator |
ํฌ์ฆ ์ถ์ ๊ธฐ ์คํ | apriltag_estimator.launch.py |
run-visualizer |
์๊ฐํ ๋ ธ๋ ์คํ | pose_visualizer_node.py |
echo-pose |
ํฌ์ฆ ํ ํฝ ๋ชจ๋ํฐ๋ง | -- |
call-pose |
ํฌ์ฆ ์๋น์ค ํธ์ถ | TargetPointPose.srv |
cmd-help |
์ฌ์ฉ ๊ฐ๋ฅํ command ๋ชฉ๋ก | ์ปจํ ์ด๋ ์ ์ ์ ์๋ ์ถ๋ ฅ |
run-camera๋ ์ํฌ์คํ์ด์ค์ ํฌํจ๋ realsense-ros ๊ธฐ์ค์ ๋๋ค. ๋ค๋ฅธ ์นด๋ฉ๋ผ๋ฅผ ์ธ ๊ฒฝ์ฐ ํด๋น ROS2 ๋๋ผ์ด๋ฒ๋ฅผ ์ง์ ์คํํ์ธ์
(estimator ๊ตฌ๋ ํ ํฝ:camera/color/image_raw,camera/color/camera_info).
์นด๋ฉ๋ผ ์ฐ๊ฒฐ ํ์ธ โโโโโโโถ ์์คํ
์คํ โโโโโโโโถ ํ ํฝ ๋ชจ๋ํฐ๋ง โโโโโโโถ ์๋น์ค ํธ์ถ
โ โ โ โ
์นด๋ฉ๋ผ ๋๋ผ์ด๋ฒ launch x2 topic echo service call
์ง๋จ ๋๊ตฌ (cam + est) /target_poses /target_point_pose
์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ์ ์ง๋จ ๋๊ตฌ๋ก ์ฐ๊ฒฐ ์ํ๋ฅผ ํ์ธํ์ธ์.
# 1) ์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ ์คํ
ros2 launch <your_camera_driver> ...
# 2) ํฌ์ฆ ์ถ์ ๊ธฐ ์คํ
ros2 launch apriltag_pose_estimator apriltag_estimator.launch.py# ํ์ฑ ํ ํฝ ํ์ธ
ros2 topic list
# ๊ฒ์ถ๋ ํฌ์ฆ ํ์ธ
ros2 topic echo /pose_estimator_node/target_poses
# ์ด๋ฏธ์ง ํ์ธ
ros2 run rqt_image_view rqt_image_viewros2 service call /pose_estimator_node/target_point_pose \
apriltag_pose_estimator_msgs/srv/TargetPointPose \
"{request_time: {sec: 0, nanosec: 0}, group_name: ''}"scripts/jitter_probe.py๋ target_point_pose ์๋น์ค๋ฅผ Nํ ๋ฐ๋ณต ํธ์ถํ์ฌ ์๋ต ํฌ์ฆ์ ํ๋ค๋ฆผ(์งํฐ)์ ์์น (mm) / ํ์ (deg) ํต๊ณ๋ก ๋ณด์ฌ์ฃผ๋ ์ง๋จ ์คํฌ๋ฆฝํธ์
๋๋ค. ๋
ธ๋๊ฐ ์คํ ์ค์ผ ๋ ๋ณ๋ ํฐ๋ฏธ๋์์ ์คํํ์ธ์. ๋น๋ ๋ถํ์(์์ Python).
๊ธฐ๋ณธ ์คํ
# 100ํ ํธ์ถ, 0.1์ด ๊ฐ๊ฒฉ, default ๊ทธ๋ฃน ์ถ์
python3 ros2_ws/src/apriltag_pose_estimator/scripts/jitter_probe.py์คํ ์์ ์ ์๋์ผ๋ก timestamp ๊ธฐ๋ฐ ๋ก๊ทธ/CSV ํ์ผ์ ์์ฑํ๊ณ ๊ฒฝ๋ก๋ฅผ ์ถ๋ ฅํฉ๋๋ค (์: jitter_probe_20260424_175126.log / .csv). CSV์๋ ๋งค ํธ์ถ์ raw ๊ฐ(idx, ok, x_mm, y_mm, z_mm, roll_deg, pitch_deg, yaw_deg, message)์ด ๊ธฐ๋ก๋์ด ์ธ๋ถ ๋ถ์/๊ทธ๋ํ ๋๊ตฌ๋ก ํ์ฉํ ์ ์์ต๋๋ค.
์ต์
| ์ต์ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
-n, --count |
100 | ์๋น์ค ํธ์ถ ํ์ |
-i, --interval |
0.1 | ํธ์ถ ๊ฐ๊ฒฉ (์ด) |
--service |
/pose_estimator_node/target_point_pose |
์๋น์ค ์ด๋ฆ |
--group |
"" |
์กฐํํ ๊ทธ๋ฃน ์ด๋ฆ (๋น ๋ฌธ์์ด = ์ฒซ/default ๊ทธ๋ฃน) |
--log <path> |
์๋ timestamp | ๋ก๊ทธ ํ์ผ ๊ฒฝ๋ก (์ํ ๊ฒฝ์ฐ ์ง์ ์ง์ ๊ฐ๋ฅ) |
--csv <path> |
๋ก๊ทธ์ ๋์ผ ์์น .csv |
CSV ํ์ผ ๊ฒฝ๋ก (์ํ ๊ฒฝ์ฐ ์ง์ ์ง์ ๊ฐ๋ฅ) |
--no-log |
off | ํ์ผ ์ ์ฅ์ ๋๊ณ ์ฝ์๋ง ์ฌ์ฉ |
์ฌ์ฉ ์์
# 200ํ ํธ์ถ, 50ms ๊ฐ๊ฒฉ
python3 .../jitter_probe.py -n 200 -i 0.05
# ํน์ ๊ทธ๋ฃน ์ถ์ (multi-group yaml ํ๊ฒฝ)
python3 .../jitter_probe.py --group secondary
# ๋ก๊ทธ/CSV ์ ์ฅ ๊ฒฝ๋ก ์ง์ ์ง์
python3 .../jitter_probe.py --log /tmp/run1.log --csv /tmp/run1.csv
# ํ์ผ ์ ์ฅ ๋๊ณ ์ฝ์ ์ถ๋ ฅ๋ง
python3 .../jitter_probe.py --no-log์ถ๋ ฅ ์์
position (mm):
x mean= -14.5532 mm std= 0.2340 min= -14.9100 max= -14.1200 p2p= 0.7900
y mean= -47.2991 mm std= ...
z mean= +270.3540 mm std= ...
orientation (deg):
roll mean= ...
pitch mean= ...
yaw mean= ...
mean: ํ๊ท (์ ์ ์์น / ๋ฐ๋ณต ๊ฐ๋ฅ์ฑ ๊ฒ์ฆ)std: 1-ฯ ํ์คํธ์ฐจ (๋ฎ์์๋ก ์์ )p2p: peak-to-peak (maxโmin, ์ค์ ๋จ๋ฆผ ํญ)
IMAGE_NAME="hhanoo/project:apriltag-pose-ros2-humble" # Docker Hub ์ด๋ฏธ์ง (๊ธฐ๋ณธ๊ฐ)
CONTAINER_NAME="apriltag-pose-ros2-humble" # Docker ์ปจํ
์ด๋ ์ด๋ฆ
ROS_DOMAIN_ID= # ROS2 ๋๋ฉ์ธ ID
XAUTHORITY_PATH="$HOME/.Xauthority" # X11 .Xauthority ๊ฒฝ๋ก (GUI/RViz ํ์์ฉ)
run.sh์คํ ์ docker pull hhanoo/project:apriltag-pose-ros2-humble๋ก ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ค์ธ์.์ง์ ๋น๋ํ๋ ค๋ฉด
IMAGE_NAME์"apriltag-pose-ros2-humble"๋ฑ์ผ๋ก ๋ณ๊ฒฝ ํ./build.sh๋ฅผ ์คํํ์ธ์.
apriltag_pose_estimator/config/pose_estimator.yaml
์ ๊ท ์ฌ์ฉ์๋ multi-group ํ์์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. ๋จ์ผ ๊ทธ๋ฃน ์ด์๋ group_names์ ๊ทธ๋ฃน ํ๋๋ง ๋๋ฉด ๋์ผํ ์ธํฐํ์ด์ค๋ก ๋์ํฉ๋๋ค.
pose_estimator_node:
ros__parameters:
# ๊ณตํต AprilTag ์ค์
tag_size: 0.02778
tag_family: 'tagStandard41h12'
# ์
๋ ฅ / ์ถ๋ ฅ ํ ํฝ (yaml์ ์ ์ผ๋ฉด ๊ทธ ๊ฐ, ์์ผ๋ฉด ~/<topic> ์๋ ์ฌ์ฉ)
camera_topic: 'camera/camera/color/image_raw'
camera_info_topic: 'camera/camera/color/camera_info'
camera_frame: 'camera_color_optical_frame'
use_distortion_from_camera_info: false
target_poses_topic: 'pose_estimator_node/target_poses'
group_names_topic: 'pose_estimator_node/group_names' # latched
group_status_topic: 'pose_estimator_node/group_status'
detection_image_topic: 'pose_estimator_node/detection_image'
publish_detection_image: true
# ---- Multi-group ์ ์ ----
# ๊ฐ ๊ทธ๋ฃน์ base marker์ pose 1๊ฐ๋ฅผ ์ถ๋ ฅ. group_names ์์๊ฐ PoseArray / group_status ์์.
group_names:
- default
- secondary
groups:
default:
marker_ids: [0, 1, 2]
base_marker_id: 1
# ๋ง์ปค๋ง๋ค 6 doubles [x, y, z, roll, pitch, yaw] (m, rad), T_base <- marker_i
# base_marker entry๋ ์ ์์ ๋ชจ๋ 0 (์๋ ๊ฐ์ )
marker_offsets: [
-0.065, 0.000, 0.000, 0.0000, 0.0000, 0.0000, # marker 0
0.000, 0.000, 0.000, 0.0000, 0.0000, 0.0000, # marker 1 (base)
0.065, 0.000, 0.000, 0.0000, 0.0000, 0.0000, # marker 2
]
secondary:
marker_ids: [10, 11, 12]
base_marker_id: 11
marker_offsets: [
-0.065, 0.000, 0.000, 0.0000, 0.0000, 0.0000,
0.000, 0.000, 0.000, 0.0000, 0.0000, 0.0000,
0.065, 0.000, 0.000, 0.0000, 0.0000, 0.0000,
]v3.0.0 ABI break
- yaml:
group_names+groups.<name>.{...}์คํค๋ง๊ฐ ํ์. ๋จ์ผ ๊ทธ๋ฃน๋ง ์ฌ์ฉํด๋group_names: [<your_group>]+groups.<your_group>.{...}ํํ๋ก ๋ช ์ํด์ผ ํจ. ์ด์ ๋จ์ผ ๊ทธ๋ฃน ํ์(marker_ids/marker_offsets/target_points์ต์์)์ ๋ ์ด์ ์ง์ํ์ง ์์.- srv:
TargetPointPose.srvrequest์string group_nameํ๋ ์ถ๊ฐ. ๋น ๋ฌธ์์ด์ group_names์ ์ฒซ ๊ทธ๋ฃน์ ๊ฐ๋ฆฌํด.- ์ถ๋ ฅ:
target_points๊ฐ๋ ์ ๊ฑฐ. ๊ฐ ๊ทธ๋ฃน์ base marker pose 1๊ฐ๋ง ์ถ๋ ฅํ๋ฏ๋กPoseArray.poses[i]๊ฐgroup_names[i]์ 1:1 ๋งคํ.
- tag_size: AprilTag ๊ฒ์์ ์ฌ๊ฐํ์ ํ ๋ณ ๊ธธ์ด(m). ์ ํ๋์ ์ง์ ์ ์ผ๋ก ์ํฅ
- marker_offsets: ๋ง์ปค๋ณ base_marker ๊ธฐ์ค 6-DoF ๋ณํ (T_base โ marker_i).
marker_ids์์๋๋ก ๋ง์ปค๋ง๋ค 6๊ฐ ๊ฐ[x, y, z, roll, pitch, yaw](m, rad)์ ํ๋ฉด ๋ฆฌ์คํธ๋ก ๋์ด.base_marker_id์ ํด๋นํ๋ ํญ๋ชฉ์ ์ ์์ identity (๋ชจ๋ 0)์ฌ์ผ ํ๋ฉฐ, 0์ด ์๋๋ฉด WARN ํ ๊ฐ์ 0 ์ฒ๋ฆฌ๋จ. ๋ง์ปค ์๊ฐN์ด๋ฉด ์ด6N๊ฐ. ํ์ ํฉ์ฑ์R = Rz(yaw) * Ry(pitch) * Rx(roll)(intrinsic Z-Y-X) - use_distortion_from_camera_info: PnP ์ ์ฌ์ฉํ ์๊ณก ๊ณ์ ์์ค ์ ํ
false(๊ธฐ๋ณธ): ์๊ณก ๊ณ์๋ฅผ 0 ์ผ๋ก ๊ฐ์ฃผ. ์ด๋ฏธ rectified ๋ ์ด๋ฏธ์ง๋ฅผ ํผ๋ธ๋ฆฌ์ํ๋ ์นด๋ฉ๋ผ์ ์ฌ์ฉtrue:camera_info.d์ ๊ฐ์ ๊ทธ๋๋ก ์ฌ์ฉ. raw ์ด๋ฏธ์ง๋ฅผ ํผ๋ธ๋ฆฌ์ํ๋ ์นด๋ฉ๋ผ์ ์ฌ์ฉ
- display_width / display_height: ์๋น์ค ๊ฒฐ๊ณผ ์๋์ฐ ํฌ๊ธฐ.
0์ด๋ฉด ์๋ณธ ํด์๋ ๊ทธ๋๋ก ํ์
| ์ธ์ | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
config_file |
pose_estimator.yaml |
YAML ์ค์ ํ์ผ ๊ฒฝ๋ก |
show_service_result_window |
"" (YAML ๊ฐ ์ฌ์ฉ) |
์๋น์ค ๊ฒฐ๊ณผ ์๋์ฐ ํ์ ์ฌ๋ถ |
camera_topic |
"" (YAML ๊ฐ ์ฌ์ฉ) |
์นด๋ฉ๋ผ ์ด๋ฏธ์ง ํ ํฝ ์ค๋ฒ๋ผ์ด๋ |
camera_info_topic |
"" (YAML ๊ฐ ์ฌ์ฉ) |
์นด๋ฉ๋ผ ์ ๋ณด ํ ํฝ ์ค๋ฒ๋ผ์ด๋ |
tag_family |
"" (YAML ๊ฐ ์ฌ์ฉ) |
ํ๊ทธ ํจ๋ฐ๋ฆฌ ์ค๋ฒ๋ผ์ด๋ |
tag_size |
"" (YAML ๊ฐ ์ฌ์ฉ) |
ํ๊ทธ ํฌ๊ธฐ ์ค๋ฒ๋ผ์ด๋ |
| ๋ ธ๋ | ์ธ์ด | ํจํค์ง | ์ค๋ช |
|---|---|---|---|
pose_estimator_node |
C++ | apriltag_pose_estimator | AprilTag ๊ฒ์ถ ๋ฐ ํฌ์ฆ ์ถ์ |
pose_visualizer_node |
Python | apriltag_pose_visualizer | ๊ฒ์ถ ๊ฒฐ๊ณผ ์๊ฐํ ๋ฐ RPY ๊ณ์ฐ |
์นด๋ฉ๋ผ ๋๋ผ์ด๋ฒ ๋ ธ๋๋ ๋ณธ ํจํค์ง ์ธ๋ถ ์์กด์ฑ์ผ๋ก, ์ฌ์ฉ ํ๊ฒฝ์ ๋ง๊ฒ ๋ณ๋๋ก ์คํํด์ผ ํฉ๋๋ค.
| ํ ํฝ | ํ์ | QoS / ๋น๊ณ | ์ค๋ช |
|---|---|---|---|
/pose_estimator_node/tag_detections |
apriltag_pose_estimator_msgs/TagDetection |
KEEP_LAST(10) | Raw ๊ฒ์ถ ๋ฐ์ดํฐ (debug. multi-group์์ ์ฒซ ๊ทธ๋ฃน rvec/tvec) |
/pose_estimator_node/target_poses |
geometry_msgs/PoseArray |
KEEP_LAST(10) | ๊ฐ ๊ทธ๋ฃน์ base marker pose (group_names ์์, ์คํจ ๊ทธ๋ฃน์ NaN) |
/pose_estimator_node/group_names |
std_msgs/String |
transient_local + KEEP_LAST(1) (latched) | ๊ทธ๋ฃน ์ด๋ฆ CSV. boot ๋๊ธฐํ์ฉ |
/pose_estimator_node/group_status |
std_msgs/UInt8MultiArray |
KEEP_LAST(10) | group_names ์์๋๋ก 0(invalid) / 1(valid) |
/pose_estimator_node/detection_image |
sensor_msgs/Image (BGR8) |
KEEP_LAST(10), subscriber 0์ด๋ฉด publish skip | ๋ชจ๋ ๊ทธ๋ฃน ๊ฒ์ถ ๊ฒฐ๊ณผ๋ฅผ ํ ์ด๋ฏธ์ง์ ๋์ ์ค๋ฒ๋ ์ด |
| ํ ํฝ | ํ์ | ์๋ธ์คํฌ๋ผ์ด๋ฒ | ์ค๋ช |
|---|---|---|---|
/camera/color/image_raw |
sensor_msgs/Image |
pose_estimator_node | ์ ๋ ฅ ์นด๋ฉ๋ผ ์ด๋ฏธ์ง |
/camera/color/camera_info |
sensor_msgs/CameraInfo |
pose_estimator_node | ์นด๋ฉ๋ผ ์บ๋ฆฌ๋ธ๋ ์ด์ ์ ๋ณด |
| ์๋น์ค | ํ์ | ์ ๊ณต์ | ์ค๋ช |
|---|---|---|---|
/pose_estimator_node/target_point_pose |
TargetPointPose |
pose_estimator_node | ์ต์ ๊ฒ์ถ ํฌ์ฆ ์ฟผ๋ฆฌ |
TagDetection.msg:
bool tag_detected # ํ๊ทธ ๊ฒ์ถ ํ๋๊ทธ
float64[9] camera_matrix # 3x3 ๋ด๋ถ ํ๋ผ๋ฏธํฐ ํ๋ ฌ (row-major)
float64[] dist_coeffs # ์๊ณก ๊ณ์ (OpenCV ์์)
float64[3] rvec # Rodrigues ํ์ ๋ฒกํฐ
float64[3] tvec # ๋ณ์ง ๋ฒกํฐ (m)
float64 tag_size # ํ๊ทธ ํฌ๊ธฐ (m)TargetPointPose.srv:
# Request
builtin_interfaces/Time request_time # ์์ฒญ ์๊ฐ
string group_name # ์กฐํ ๊ทธ๋ฃน ์ด๋ฆ ("" = ์ฒซ/default ๊ทธ๋ฃน)
---
# Response
bool success # ์ฑ๊ณต ์ฌ๋ถ
string message # ์ํ ๋ฉ์์ง
builtin_interfaces/Time data_time # ๋ฐ์ดํฐ ํ์์คํฌํ
geometry_msgs/PoseArray poses # ํด๋น ๊ทธ๋ฃน์ base marker pose (์์ 1๊ฐ)
group_name์ด ๋น ๋ฌธ์์ด์ด๋ฉดgroup_names์ ์ฒซ ๊ทธ๋ฃน์ ๊ฐ๋ฆฌํด. ์ ์ ์๋ ๊ทธ๋ฃน ์ด๋ฆ์ service error.
| ํญ๋ชฉ | ๊ฐ | ์ค๋ช |
|---|---|---|
| Network mode | host |
ํธ์คํธ ๋คํธ์ํฌ ๊ณต์ |
| ROS_DOMAIN_ID | 99 |
ROS2 ๋๋ฉ์ธ ID |
| IPC | host |
IPC ๋ค์์คํ์ด์ค ๊ณต์ |
| X11 | /tmp/.X11-unix |
GUI ํฌ์๋ฉ |
์นด๋ฉ๋ผ ๋๋ผ์ด๋ฒ๊ฐ ๋๋ฐ์ด์ค๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ์ผ๋ฐ์ ์ผ๋ก USB ์ฐ๊ฒฐยท๊ถํยท์ปค๋ ๋ชจ๋ ๋ฌธ์ ์ ๋๋ค.
# USB ์ฐ๊ฒฐ ํ์ธ
lsusb
# ์ปค๋ ๋ชจ๋ ๋ก๋ (UVC ์นด๋ฉ๋ผ)
sudo modprobe uvcvideo
# ๊ถํ ์ค์
sudo usermod -aG video $USER
sudo usermod -aG plugdev $USER
# ๋ก๊ทธ์์ ํ ์ฌ๋ก๊ทธ์ธ์นด๋ฉ๋ผ ๋ชจ๋ธ๋ณ ์ถ๊ฐ ์ง๋จ ์ ์ฐจ๋ ์ฌ์ฉ ROS2 ๋๋ผ์ด๋ฒ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
[WARN] No tags detected in current frame
# ํ์ธ ์ฌํญ:
# - ์กฐ๋ช
: ๋ฐ๊ณ ๊ท ์ผํ ์กฐ๋ช
(๊ทธ๋ฆผ์ ์ต์ํ)
# - ์ด์ : ์นด๋ฉ๋ผ ์ด์ ์ด ํ๊ทธ์ ๋ง์ถฐ์ ธ ์๋์ง ํ์ธ
# - ๊ฑฐ๋ฆฌ: ์นด๋ฉ๋ผ-ํ๊ทธ ๊ฐ 0.3m ~ 3m ๊ถ์ฅ
# - ๊ฐ๋: ํ๊ทธ๊ฐ ์นด๋ฉ๋ผ๋ฅผ ํฅํ๋๋ก (< 45๋)
# config์์ ํ๊ทธ ํฌ๊ธฐ์ ํจ๋ฐ๋ฆฌ๊ฐ ์ค์ ์ ์ผ์นํ๋์ง ํ์ธ
# tag_size: 0.02778 (์ค์ธก๊ฐ์ ๋ฏธํฐ ๋จ์๋ก)
# tag_family: 'tagStandard41h12' (์ธ์ํ ํ๊ทธ์ ๋์ผํด์ผ ํจ)# ํฌ์ฆ ์ถ์ ๊ฐ์ด ํฌ๊ฒ ํ๋ค๋ฆฌ๋ ๊ฒฝ์ฐ
# 1. tag_size ๊ฐ์ด ์ค์ธก์น์ ์ ํํ ์ผ์นํ๋์ง ํ์ธ (mm -> m ๋ณํ ์ฃผ์)
# 2. marker_offsets ๊ฐ์ด ๋ง์ปค ๊ฐ ์ค์ ๊ฑฐ๋ฆฌ/ํ์ ๊ณผ ์ผ์นํ๋์ง ํ์ธ (๋ง์ปค๋ณ 6-DoF)
# 3. ํด์๋๋ฅผ ๋์ด๊ฑฐ๋ ํ๋ ์ ๋ ์ดํธ๋ฅผ ๋ฎ์ถฐ์ ์ด๋ฏธ์ง ํ์ง ๊ฐ์
# (์ฌ์ฉ ์นด๋ฉ๋ผ ROS2 ๋๋ผ์ด๋ฒ launch์์ ํด์๋/FPS ์ต์
์ ์กฐ์ )
# 4. scripts/jitter_probe.py ๋ก ์ ๋ ์ธก์ (์ฌ์ฉ๋ฒ ์น์
5๋ฒ ์ฐธ๊ณ )
python3 ros2_ws/src/apriltag_pose_estimator/scripts/jitter_probe.pyCMake Error: Could not find libapriltag
sudo apt update
sudo apt install -y libapriltag-dev
# ์ฌ์ ํ ์คํจ ์
sudo add-apt-repository universe
sudo apt update
sudo apt install -y libapriltag-devImportError: numpy.core.multiarray failed to import
pip3 install 'numpy>=1.21.0,<2.0'์ด ํ๋ก์ ํธ๋ MIT ๋ผ์ด์ ์ค๋ก ๋ฐฐํฌ๋ฉ๋๋ค. ์์ธํ ๋ด์ฉ์ LICENSE ํ์ผ์ ์ฐธ์กฐํ์ธ์.
hhanoo (woo980711@gmail.com)
