설정 참조
설정 우선순위
여러 설정 소스가 사용 가능한 경우, 다음의 우선순위가 적용됩니다 (높은 것부터):
- 프로그래밍 방식 설정 (코드에 직접 설정)
- 런치 파일 파라미터 (명령줄 오버라이드)
- 환경 변수 (시스템 전역 기본값)
- 로봇 프로파일 (인스턴스별 캘리브레이션)
- 모델 기본값 (컴파일 타임 상수)
SerialArmConfig 구조체
SerialArmConfig<DOF>는 모든 직렬 매니퓰레이터 설정의 기본 템플릿입니다. 런타임 설정 인터페이스를 제공합니다.
완전한 구조체
template <size_t DOF>
struct SerialArmConfig {
static constexpr size_t dof = DOF;
// =====================================================================
// 필드버스 설정
// =====================================================================
/// EtherCAT 네트워크 인터페이스 (예: "eth0", "enp3s0")
std::string network_interface = "eth0";
/// EtherCAT 마스터 인덱스 (0 기반)
unsigned int master_index = 0;
/// 제어 루프 주기 시간 (일반적으로 1kHz의 경우 1000µs)
std::chrono::microseconds cycle_time{1000};
/// 실시간 스레드 우선순위 (SCHED_FIFO, 일반적으로 98)
int rt_priority = 98;
// =====================================================================
// 로봇 프로파일 파라미터
// =====================================================================
/// 프로파일 검색을 위한 로봇 시리얼 번호
/// 설정되면 joint_offsets는 ~/.plem/profiles/<serial>.json에서 로드됨
std::optional<std::string> robot_serial;
/// 관절 엔코더 오프셋 (로봇별 캘리브레이션 값)
/// robot_serial 프로파일에서 채워지거나 직접 설정
std::array<int32_t, DOF> joint_offsets{};
// =====================================================================
// 의존성 주입
// =====================================================================
// =====================================================================
// 인스턴스 식별
// =====================================================================
/// 로봇 인스턴스 식별자 (다중 로봇 지원)
/// 빈 문자열(기본값)은 단일 로봇 하위 호환
std::string robot_id;
// =====================================================================
// 의존성 주입
// =====================================================================
/// 커스텀 필드버스 구현 (nullptr = 기본 EtherCAT 사용)
/// MockBus로 테스트하거나 대체 프로토콜에 사용
std::shared_ptr<fieldbus::IBus> custom_bus;
};
멤버 설명
network_interface
타입: std::string
기본값: "eth0"
설명: EtherCAT 통신을 위한 네트워크 인터페이스 이름.
유효한 값:
- 물리 인터페이스:
"eth0","eth1","enp3s0","enp2s0" - 유효하지 않음: 무선 인터페이스 (
"wlan0"), 루프백 ("lo")
인터페이스 찾는 방법:
# 모든 네트워크 인터페이스 나열
ip link show
# EtherCAT 마스터 설정 확인
cat /opt/etherlab/etc/ethercat.conf | grep MASTER0_DEVICE
일반적인 시나리오:
- 단일 이더넷 포트가 있는 데스크톱:
"eth0"또는"enp3s0" - W-RC/임베디드:
"eth0" - 다중 NIC 시스템: 전용 RT 네트워크 인터페이스 사용
master_index
타입: unsigned int
기본값: 0
설명: 다중 마스터가 있는 시스템을 위한 EtherCAT 마스터 인덱스.
일반적인 사용:
- 단일 로봇:
0(기본값) - 하나의 호스트에 여러 로봇:
0,1,2, 등 - 사용 가능한 마스터 확인:
ls /dev/EtherCAT*
다중 로봇 설정 예시:
config_a.network_interface = "eth0";
config_a.master_index = 0;
config_b.network_interface = "eth1";
config_b.master_index = 1;
Builder에서 with_robot_id("arm_left") 등으로 IPC/ROS2 네임스페이스를 격리합니다. 자세한 내용은 로봇 통합 가이드를 참조하세요.
cycle_time
타입: std::chrono::microseconds
기본값: 1000 (1ms = 1kHz)
설명: 제어 루프 주기 시간.
유효 범위:
- 최소:
250µs(4kHz, 실험적) - 일반적:
1000µs(1kHz, 권장) - 최대:
2000µs(500Hz)
성능 고려사항:
- 높은 주파수 = 더 나은 제어, 더 높은 CPU 부하
/opt/etherlab/etc/ethercat.conf의 EtherCAT 주기 시간과 일치해야 함- 1000µs 미만은 RT 커널 필요
rt_priority
타입: int
기본값: 98
설명: SCHED_FIFO 실시간 스레드 우선순위.
유효 범위:
- 최소:
1(가장 낮은 RT 우선순위) - 권장:
98(높은 우선순위, 커널 스레드 아래) - 최대:
99(가장 높음, 중요한 커널 작업을 위해 예약됨)
rt_priority=99는 절대 사용하지 마십시오. 이 값은 중요한 커널 워치독 및 인터럽트 핸들러를 위해 예약되어 있습니다. rt_priority=99를 사용하면 시스템 불안정성, 데드락 또는 전체 시스템 정지가 발생할 수 있습니다.
안전한 값:
- 개발/테스트:
50-80 - 프로덕션 RT 제어:
95-98
robot_serial
타입: std::optional<std::string>
기본값: std::nullopt
설명: 프로파일 기반 설정을 위한 고유 시리얼 번호.
형식: 제조사별 (예: Neuromeka의 경우 "PE07I7E0E001")
동작:
- 설정된 경우:
joint_offsets는~/.plem/profiles/<serial>.json에서 로드됨 - 설정되지 않은 경우:
joint_offsets배열을 직접 사용
예제 워크플로우:
// 옵션 1: 로봇 시리얼 사용 (프로파일 자동 로드)
config.robot_serial = "PE07I7E0E001";
// 옵션 2: 오프셋 직접 설정
config.joint_offsets = {-535768, 195386, 191634, 34542, 24035, 21232};
joint_offsets
타입: std::array<int32_t, DOF>
기본값: 모두 0
설명: 관절별 엔코더 오프셋 캘리브레이션 값 (원시 엔코더 카운트).
단위: 원시 엔코더 카운트 (라디안 아님)
소스 우선순위:
robot_serial이 설정된 경우 프로파일에서 로드- 코드에서 직접 설정
- 0으로 기본값 (미캘리브레이션)
캘리브레이션 프로세스:
- 로봇을 알려진 영점 위치로 이동 (홈)
- 원시 엔코더 값 읽기
- 값을 부정하여 오프셋 얻기
- 프로파일에 저장하거나 설정에 설정
custom_bus
타입: std::shared_ptr<fieldbus::IBus>
기본값: nullptr
설명: 커스텀 필드버스 구현을 위한 의존성 주입.
사용 시나리오:
MockBus로 테스트:config.custom_bus = std::make_shared<MockBus>(6);- 대체 프로토콜:
IBus인터페이스 구현 - 하드웨어 인 더 루프: 커스텀 브릿지 구현
기본 동작: nullptr인 경우, 팩토리가 자동으로 EtherCAT 버스 생성.
YAML 설정 파일
YAML 파일은 ROS2 통합 및 MoveIt 파라미터를 위한 외부 설정을 제공합니다. PLEM 코어 라이브러리는 YAML 파일 로드를 위해 ConfigLoader를 사용합니다.
ConfigLoader 사용
#include <plem/core/config/config_loader.hpp>
using namespace plem::core;
// 스칼라 값 로드
auto interface = load_config<std::string>("config.yaml", "network_interface");
auto priority = load_config<int>("config.yaml", "rt_priority");
// 배열 로드
auto offsets = load_config<std::vector<int32_t>>("config.yaml", "joint_offsets");
// 중첩 값 로드 (점 표기법)
auto timeout = load_config<int>("config.yaml", "robot.control.timeout");
// optional로 안전하게 로드
auto opt_value = load_config_optional<double>("config.yaml", "optional_param");
if (opt_value) {
// *opt_value 사용
}
// 기본값 폴백으로 로드
auto retry_count = ConfigLoader::get_instance()
.load_or_default<int>("config.yaml", 5, "retry_count");
YAML 파일 위치
ROS2 패키지 리소스:
wim_description/urdf/config/<robot_type>/
├── joint_limits.yaml # MoveIt 관절 제한
├── kinematics.yaml # 기구학 솔버 설정
├── physical_parameters.yaml # 질량, 관성
└── visual_parameters.yaml # 메시 경로, 색상
런치 설정:
plem_bringup/config/
└── controllers.yaml # ros2_control 컨트롤러
예제: joint_limits.yaml
# indy7_v2의 관절 제한
# 단위: radians, rad/s, rad/s², Nm
# 참고: 조인트 이름은 URDF에 정의된 대로 사용 (Indy7: joint0-joint5, 스캐폴드 생성: joint1-joint6)
joint_limits:
joint0:
has_velocity_limits: true
has_acceleration_limits: true
has_position_limits: true
max_velocity: 2.618 # 150 deg/s
max_acceleration: 5.0
max_effort: 431.97 # Nm
max_position: 3.054 # 175 deg
min_position: -3.054 # -175 deg
joint1:
has_velocity_limits: true
has_acceleration_limits: true
has_position_limits: true
max_velocity: 2.618
max_acceleration: 5.0
max_effort: 431.97
max_position: 3.054
min_position: -3.054
# ... 관절 2-5
예제: controllers.yaml
controller_manager:
ros__parameters:
update_rate: 500 # Hz
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
joint_trajectory_controller:
type: joint_trajectory_controller/JointTrajectoryController
free_drive_controller:
type: wim_controller/FreeDriveController
joint_state_broadcaster:
ros__parameters:
joints:
- joint0
- joint1
- joint2
- joint3
- joint4
- joint5
joint_trajectory_controller:
ros__parameters:
joints:
- joint0
- joint1
- joint2
- joint3
- joint4
- joint5
command_interfaces:
- position
- velocity
- acceleration
state_interfaces:
- position
- velocity
경로 해석
ConfigLoader는 여러 경로 형식을 지원합니다:
절대 경로:
load_config<int>("/etc/plem/config.yaml", "value");
상대 경로:
load_config<int>("config/robot.yaml", "value"); // 현재 디렉터리부터
ROS2 패키지 리소스:
// 형식: package_name/relative/path
load_config<int>("wim_description/urdf/config/indy7_v2/joint_limits.yaml",
"joint_limits.joint0.max_velocity");
로봇 프로파일
로봇 프로파일은 JSON 형식으로 인스턴스별 캘리브레이션 데이터를 저장합니다. 이는 로봇별 파라미터를 모델 전역 상수와 분리합니다.
프로파일 구조
위치: ~/.plem/profiles/<robot_serial>.json
형식:
{
"robot_serial": "PE07I7E0E001",
"joint_offsets": [-535768, 195386, 191634, 34542, 24035, 21232],
"profile_date": "2026-01-22T10:30:00Z"
}
필드:
robot_serial: 고유 식별자 (필수)joint_offsets: 6개의 int32 엔코더 오프셋 배열 (필수)profile_date: 생성/업데이트의 ISO 8601 타임스탬프 (선택사항)
프로파일 생성
프로그래밍 방식:
#include <plem/robot/robot_profile.hpp>
using namespace plem::robot;
// 프로파일 생성
RobotProfile profile;
profile.robot_serial = "PE07I7E0E001";
profile.joint_offsets = {-535768, 195386, 191634, 34542, 24035, 21232};
profile.profile_date = "2026-02-02T12:00:00Z";
// 검증
if (!profile.is_valid()) {
throw std::runtime_error("Invalid profile");
}
// 기본 위치에 저장 (~/.plem/profiles/PE07I7E0E001.json)
std::filesystem::create_directories(RobotProfile::default_directory());
profile.save(RobotProfile::default_path(profile.robot_serial));
수동으로:
# 프로파일 디렉터리 생성
mkdir -p ~/.plem/profiles
# 프로파일 파일 생성
cat > ~/.plem/profiles/PE07I7E0E001.json <<EOF
{
"robot_serial": "PE07I7E0E001",
"joint_offsets": [-535768, 195386, 191634, 34542, 24035, 21232],
"profile_date": "2026-02-02T12:00:00Z"
}
EOF
프로파일 로드
자동 로드 (권장):
// 설정에서 로봇 시리얼 설정
config.robot_serial = "PE07I7E0E001";
// 팩토리가 자동으로 프로파일 로드
auto robot = RobotFactory<Indy7>::create(config, bus);
// joint_offsets는 ~/.plem/profiles/PE07I7E0E001.json에서 채워짐
수동 로드:
// 기본 위치에서 로드
auto profile = RobotProfile::load_from_serial("PE07I7E0E001");
// 또는 커스텀 경로에서 로드
auto profile = RobotProfile::load("/custom/path/robot.json");
// 설정에 적용
config.joint_offsets = profile.joint_offsets;
프로파일 마이그레이션
하위 호환성: "calibration_date"를 사용하는 오래된 프로파일은 자동으로 "profile_date"로 마이그레이션됩니다:
// 이전 형식 (여전히 지원됨)
{
"robot_serial": "OLD001",
"joint_offsets": [1, 2, 3, 4, 5, 6],
"calibration_date": "2025-01-01T00:00:00Z"
}
// 다음으로 로드됨:
profile.profile_date == "2025-01-01T00:00:00Z" // 자동으로 마이그레이션됨
환경 변수
환경 변수는 시스템 전역 설정 기본값을 제공합니다.
ROS_DOMAIN_ID
목적: ROS2 DDS 도메인 격리
기본값: 0
유효 범위: 0-101 (215 피함, 예약됨)
사용:
export ROS_DOMAIN_ID=42
사용 시기:
- 동일 네트워크의 여러 로봇
- 개발/프로덕션 격리
- 다중 사용자 환경
참고: 통신하려면 모든 ROS2 노드가 동일한 도메인 ID를 사용해야 합니다.
RMW_IMPLEMENTATION
목적: ROS2 미들웨어 선택
기본값: rmw_cyclonedds_cpp (PLEM 프로젝트 기본값)
대안: rmw_fastrtps_cpp
사용:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
성능 참고사항:
- CycloneDDS: 더 나은 실시간 성능, 더 낮은 지연시간
- FastRTPS: 더 많은 기능, 더 넓은 채택
LD_LIBRARY_PATH
목적: 공유 라이브러리 검색 경로 필요한 경우: 설치 접두사 외부의 개발 빌드 사용:
export LD_LIBRARY_PATH=/workspace/install/plem_core/lib:$LD_LIBRARY_PATH
런치 파일이 자동으로 설정:
launch_utils.py의 build_plem_library_env()가 설치된 모든 plem_* 패키지를 자동 탐색하여 LD_LIBRARY_PATH를 구성합니다. 수동 설정은 불필요합니다.
RT 커널 변수
PREEMPT_RT 검증:
# 커널 타입 확인
uname -a | grep PREEMPT_RT
# RT 커널이 설치된 경우, 검증
cat /sys/kernel/realtime
# 출력: 1 (RT 커널 활성)
CPU 격리 (커널 파라미터):
# /etc/default/grub에 추가:
GRUB_CMDLINE_LINUX="isolcpus=7"
# grub 업데이트 및 재부팅
sudo update-grub
sudo reboot
런치 파일 파라미터
런치 파일은 시스템 설정을 위한 주요 인터페이스를 제공합니다. PLEM은 선언적 인수가 있는 Python 런치 파일을 사용합니다.
통합 런치 파라미터 (빠른 테스트용)
파일: plem_bringup/launch/plem_launch.py
용도: 프로토타이핑, 전체 스택 검증 (모든 PLEM 컴포넌트 시작)
프로덕션: 스캐폴드 런처 사용 권장 (ros2 run plem_bringup create_description로 launch 파일 포함 생성, --no-launch로 비활성화 가능)
eth_interface
타입: String
기본값: "eth0"
설명: EtherCAT 네트워크 인터페이스
사용:
ros2 launch plem_bringup plem_launch.py eth_interface:=enp3s0
robot_type
타입: String
기본값: "indy7_v2"
유효한 값: "indy7", "indy7_v2", "indy12", "indy12_v2", "indyrp2", 등
설명: 로봇 모델 선택 (description_package가 wim_description인 경우에만 사용)
사용:
ros2 launch plem_bringup plem_launch.py robot_type:=indy12_v2
효과:
wim_description에서 해당 URDF 로드- 관절 제한 설정 선택
- MoveIt 계획 그룹 설정
description_package
타입: String
기본값: "wim_description"
설명: URDF/SRDF/config를 포함하는 ROS2 패키지
사용:
# 외부 로봇 사용 (빠른 테스트)
ros2 launch plem_bringup plem_launch.py description_package:=my_robot_description
# 내장 로봇 사용 (기본값)
ros2 launch plem_bringup plem_launch.py description_package:=wim_description robot_type:=indy7_v2
# 프로덕션: 스캐폴드 런처 사용 (컴포넌트 선택 가능)
ros2 launch my_robot_description bringup.launch.py
효과:
wim_description: 타입 기반 선택 (robot_type 매개변수 사용)- 기타 패키지: 규약 기반 자동 검색 (robot_type 무시됨)
urdf_file
타입: String
기본값: "" (자동 검색)
설명: 자동 검색된 URDF 오버라이드 (description_package 공유에 상대적인 경로)
사용:
ros2 launch plem_bringup plem_launch.py \
description_package:=my_robot_description \
urdf_file:=urdf/my_robot_custom.urdf.xacro
사용 시기:
- 여러 URDF가 있는 패키지
- 테스트/디버깅
srdf_file
타입: String
기본값: "" (자동 검색)
설명: 자동 검색된 SRDF 오버라이드 (description_package 공유에 상대적인 경로)
사용:
ros2 launch plem_bringup plem_launch.py \
description_package:=my_robot_description \
srdf_file:=srdf/my_robot_custom.srdf.xacro
참고: SRDF가 없으면 MoveIt이 비활성화되고 ROS2 Control만 시작됨
robot_name
타입: String
기본값: "" (URDF에서 추출)
설명: 로봇 이름 오버라이드 (MoveIt 계획 그룹 접두사로 사용)
사용:
ros2 launch plem_bringup plem_launch.py \
description_package:=my_robot_description \
robot_name:=my_custom_robot
효과:
- MoveIt 계획 그룹:
{robot_name}_manipulator - 기본값: URDF
<robot name="...">속성에서 추출됨
record
타입: Boolean
기본값: true
설명: RT 모니터링 토픽의 rosbag2 기록 활성화
사용:
# 기록 비활성화
ros2 launch plem_bringup plem_launch.py record:=false
기록된 토픽:
/rt_raw(1kHz): 원시 RT 루프 데이터/rt_events: RT 이벤트 (폴트, 상태 변화)/rt_monitor_stats(10Hz): 큐 통계
bag_dir
타입: String
기본값: ~/bags/rt_monitoring
설명: rosbag 파일을 위한 디렉터리
사용:
ros2 launch plem_bringup plem_launch.py bag_dir:=/data/bags
retention_minutes
타입: Integer
기본값: 60
설명: bag 파일을 보관할 시간 (순환 버퍼)
사용:
# 2시간 데이터 보관
ros2 launch plem_bringup plem_launch.py retention_minutes:=120
disk_threshold
타입: Integer (백분율)
기본값: 70
설명: 디스크 사용량이 임계값을 초과하면 가장 오래된 파일 삭제
사용:
ros2 launch plem_bringup plem_launch.py disk_threshold:=80
robot_id
타입: String
기본값: "indy"
설명: 로봇 인스턴스 식별자. 모든 ROS2 토픽, 액션, IPC 경로에 /{robot_id}/ namespace prefix가 적용됩니다.
사용:
# 단일 로봇 (기본값)
ros2 launch plem_bringup plem_launch.py robot_id:=indy
# 다중 로봇 (각각 고유 ID)
ros2 launch plem_bringup multi_robot.launch.py
효과:
- IPC 공유 메모리 경로:
/{robot_id}_joint_actual_values - ROS2 토픽:
/{robot_id}/joint_states,/{robot_id}/rt_raw - ROS2 액션:
/{robot_id}/wim_rt_driver/set_mode - Python bridge:
PLEM_ROBOT_ID환경 변수로 namespace 설정 (plem_launch.py에서robot_id인수로 자동 전달)
다중 로봇 시나리오:
// C++ Builder API
auto left = Robot::builder<Indy7>(config_a).with_robot_id("arm_left").build();
auto right = Robot::builder<Indy7>(config_b).with_robot_id("arm_right").build();
robot_id 미지정 시 기본값 "indy"를 사용하여 기존 단일 로봇 설정과 하위 호환됩니다. C++ SerialArmConfig의 robot_id 기본값은 빈 문자열이며, 런치 파라미터 기본값이 "indy"입니다. plem_launch.py는 robot_id 인수를 PLEM_ROBOT_ID 환경변수로 Python bridge에 자동 전달합니다.
런치 파일 선언 패턴
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
# 인수 선언
robot_type_arg = DeclareLaunchArgument(
'robot_type',
default_value='indy7_v2',
description='로봇 타입 (indy7_v2, indy12_v2, 등)'
)
# 인수 사용
robot_type = LaunchConfiguration('robot_type')
return LaunchDescription([
robot_type_arg,
# ... robot_type을 사용하는 노드
])
설정 예제
개발 설정 (MockBus)
하드웨어 없이 유닛 테스트:
#include <plem/robot/models/indy7.hpp>
#include <plem/robot/specs/robot_factory.hpp>
#include <plem/fieldbus/mock_bus.hpp>
using namespace plem::robot;
using namespace plem::fieldbus;
// 설정 생성
models::Indy7 config;
config.rt_priority = 50; // 테스트를 위한 낮은 우선순위
// MockBus 주입
auto mock_bus = std::make_shared<MockBus>(6);
config.custom_bus = mock_bus;
// 로봇 생성 (EtherCAT 불필요, Builder 패턴 사용)
auto result = Robot::builder<models::Indy7>(config).build();
프로덕션 설정 (EtherCAT)
프로파일 기반 캘리브레이션이 있는 물리 로봇:
#include <plem/robot/robot_builder.hpp>
#include <plem/robot/models/indy7.hpp>
using namespace plem::robot;
// 설정 생성
models::Indy7 config;
config.network_interface = "eth0";
config.master_index = 0;
config.cycle_time = std::chrono::microseconds(1000); // 1kHz
config.rt_priority = 98;
config.robot_serial = "PE07I7E0E001"; // 자동으로 프로파일 로드
// 로봇 생성 (Builder 패턴 사용)
auto result = Robot::builder<models::Indy7>(config).build();
if (!result.is_ok()) { /* 에러 처리 */ }
auto robot = std::move(result).value();
런치 동등물:
ros2 launch plem_bringup plem_launch.py \
eth_interface:=eth0 \
robot_type:=indy7_v2 \
robot_id:=indy
일반적인 설정 오류
다음은 PLEM 설정 시 자주 발생하는 오류와 해결 방법입니다.
오류: "Failed to open profile file"
원인: 프로파일 파일을 찾을 수 없음 해결책:
# 파일 존재 확인
ls ~/.plem/profiles/PE07I7E0E001.json
# 누락된 경우 생성
mkdir -p ~/.plem/profiles
# ... 프로파일 파일 생성
오류: "EtherCAT master not found"
원인: EtherCAT 서비스가 실행되지 않거나 잘못된 인터페이스 해결책:
# 서비스 확인
sudo systemctl status ethercat
# 필요한 경우 재시작
sudo systemctl restart ethercat
# 장치 확인
ls /dev/EtherCAT*
오류: "joint_offsets must have exactly 6 elements"
원인: 프로파일에 잘못된 배열 크기 해결책: 프로파일 파일 편집, 6개 요소 보장:
{
"robot_serial": "PE07I7E0E001",
"joint_offsets": [-535768, 195386, 191634, 34542, 24035, 21232],
"profile_date": "2026-02-02T12:00:00Z"
}
오류: "rt_priority out of range"
원인: 유효하지 않은 RT 우선순위 값 해결책: 1-99 범위 사용, 99는 피함:
config.rt_priority = 98; // 권장 프로덕션 값
오류: "Network interface does not exist"
원인: 설정된 인터페이스를 사용할 수 없음 해결책:
# 사용 가능한 인터페이스 확인
ip link show
# EtherCAT 설정 확인
cat /opt/etherlab/etc/ethercat.conf | grep MASTER0_DEVICE
추가 리소스
새 로봇 모델의 런타임 설정은 로봇 통합 가이드 참조.
더 많은 설정 예제는 다음을 참조하십시오: