Files
fori_zltech_motor_test/doc/04-software-control.md
T
Dongubak 2f21d86193 Add ZLAC8015D driver docs, IMU integration plan, and reference resources
Documents the ZLAC8015D V4 dual-channel driver (hardware spec, RS485
Modbus register map, 4WD skid-steer system architecture, the
xbox_motor_control.cpp control loop, and error/troubleshooting) under
doc/, plus a phased plan for adding IMU-based heading/slip feedback
(doc/06) informed by Clearpath Husky's sensor fusion stack and
skid-steer ICR literature. QnA/ records the Q&A that refined that plan.
Also adds the underlying manual PDFs and reference links under
resource/ that the docs cite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 00:17:24 +09:00

10 KiB
Raw Blame History

04. 소프트웨어 제어 구조 (xbox_motor_control.cpp)

출처: xbox_motor_control.cpp 전체 (1382줄), Makefile, run_4wd.sh

4.1 빌드 & 실행

make                    # g++ -O3 -std=c++17 -Wall -Wextra ... -lSDL2 -lpthread -llivox_lidar_sdk_shared
./run_4wd.sh [PORT]     # USB latency_timer 1ms 튜닝 + 빌드(필요시) + 실행

run_4wd.sh는 실행 전 set_low_latency.sh/sys/bus/usb-serial/devices/<dev>/latency_timer를 16ms(리눅스 기본값) → 1ms로 낮춰, 100Hz 제어 루프에서 시리얼 read 지연이 병목이 되지 않게 한다.

4.2 클래스 구조

SerialPort          — 포트 open/close, Modbus RTU read/write (writeReg/writeRegs/readRegs)
MotorDriver          — SerialPort + slave_id를 감싸는 드라이버 1대(좌우 2채널) 추상화
                        initDriver / setBrakes / setRPMs / readFeedback /
                        readCurrentLimits / setMaxCurrent
LidarObstacleDetector — Livox Mid-360S SDK 콜백 기반 장애물 거리 추적 (전/후/좌/우 4방향)

드라이버 2대(driver_front, driver_rear)가 각각 MotorDriver 인스턴스로 생성되며, 브로드캐스트 모드가 아니면 후방 드라이버 포인터(driver_rear_ptr)가 유효해 개별 제어/피드백이 가능하다.

4.3 100Hz 메인 루프 개요

매 틱 (10ms):
  1. SDL 이벤트 처리 (버튼: A=트립리셋, X=라이다 토글, B/Back=종료)
  2. 조이스틱 입력 읽기 + 데드존 적용
  3. 라이다 장애물 회피 로직 (속도 스케일링 + 조향 오프셋)
  4. 스키드 스티어 운동학으로 target_fl/fr/rl/rr(RPM) 계산
  5. airborne(들뜸) 판정된 바퀴는 target를 0으로 강제
  6. 상태머신(STOPPED/RUNNING/STOPPING) + 저크 제한 S-curve로 cmd_* 갱신
  7. setRPMs()로 드라이버에 지령 전송
  8. readFeedback()으로 드라이버에서 피드백 수신
  9. 드라이버 알람(에러코드) 감지 및 콘솔 경고
  10. 휠 상태(정상/들뜸/걸림) 판정, 주행거리 갱신
  11. (옵션) CSV 로그 기록
  12. HUD 한 줄 출력 (\r로 갱신)

4.4 상태 머신

상태 전이 조건 동작
STOPPED 초기 상태 target_* 절대값이 0.1 RPM 초과하면 브레이크 해제(setBrakes(false)) 후 50ms 대기, RUNNING으로 전이
RUNNING STOPPED에서 전이 저크 제한 프로파일로 cmd_*target_* 추종. target_*cmd_*가 모두 임계값 이하면 STOPPING으로 전이
STOPPING RUNNING에서 전이 감속 프로파일로 0으로 수렴. cmd_fl/fr이 0.1 RPM 미만이면 즉시 정지 지령(setRPMs(0,0)) 후 50ms 뒤 브레이크 잠금, STOPPED로 복귀

브레이크 해제/잠금 시 50ms 대기를 두는 이유는 전자식 브레이크(솔레노이드)의 기계적 응답 시간을 확보하기 위함이다.

4.5 저크 제한(Jerk-Limited) S-curve 프로파일

jerkLimitedStep(current_vel, target_vel, current_accel&, max_accel, max_decel, jerk_limit)
  • 목표 속도까지 가속도 자체를 매 틱 jerk_limit × dt만큼만 변화시켜 부드럽게 목표 가속도에 도달시킨다. 정지 상태에서 출발할 때 첫 틱부터 풀가속도가 걸려 정지마찰이 급격히 파괴되는 문제(예: 저마찰 노면에서 순간 슬립)를 막기 위한 구조.
  • 오버슈트 방지: 다음 틱 속도가 목표를 넘어서면 그 자리에서 목표값으로 스냅하고 가속도를 0으로 리셋한다.
  • 가속/감속/저크 한계값은 CLI 인자로 조정 가능 (§4.8).
  • 제자리 회전은 spin_jerk_rate(기본 220 RPM/s²)로 직진/커브 선회(jerk_rate, 기본 600 RPM/s²) 보다 훨씬 완만하게 적용된다 — 고마찰 노면에서 제자리 선회 시 정지마찰이 급격히 깨지지 않도록 가속에 도달하는 시간을 더 길게 늘린 것.

4.6 착지(Landing) 스냅 처리

들뜸(airborne) 상태에서 해제된(착지) 직후 한 틱은, 저크 제한 프로파일을 건너뛰고 cmd_*target_*로 즉시 스냅한다(just_landed_* 플래그). 차체는 이미 그 속도로 이동 중인데 지령이 정지출발용 완만한 가속 램프를 다시 타면, 그 사이 바퀴가 지면 이동 속도를 못 따라가며 끌리는 현상을 없애기 위함.

4.7 라이다(Livox Mid-360S) 장애물 회피

LidarObstacleDetector가 포인트클라우드 콜백에서 좌표를 17° 하향 피치 보정 후 4개 구역 (전방/후방/좌측/우측)의 최소 거리를 EMA(지수이동평균, 접근 시 즉각 반응·회복 시 완만)로 추적한다.

파라미터 기본값 의미
front_stop_dist 0.35 m 전방 완전 정지
front_warn_dist 0.50 m 전방 감속 시작 (선형 스케일)
side_dodge_dist 0.50 m 측면 회피 조향 시작
side_stop_dist 0.20 m 측면 회피 강도 100% 도달 거리
max_dodge_omega 0.35 rad/s 자동 회피 최대 조향 각속도
  • 전진 중 전방 장애물 → 속도 스케일링(0~1) 또는 완전 정지.
  • 후진 중 후방 장애물 → 완전 정지만 적용(감속 스케일링 없음).
  • 전진 중이면서 사용자가 좌우 조향(rx) 입력이 없을 때만 측면 회피 조향 개입 (요청 사양: 후진 시 측면 회피 미적용).
  • 라이다 데이터 800ms 이상 미수신 시 connected=false로 판단, HUD에 LIDAR:WAITING 표시.
  • X 버튼으로 라이다 회피 기능 자체를 런타임에 토글 가능.

4.8 휠 이상(들뜸/걸림) 진단

속도 폐루프 특성상(부하 무관하게 지령 RPM을 추종하려 함) "지령 대비 실제속도" 하나만으로는 무부하(들뜸)를 판정할 수 없다. 두 개의 독립된 판정 축을 사용한다:

걸림/과부하 판정 (classifyStall)

vel_ratio = |실제RPM| / |지령RPM|
stall = (vel_ratio < stall_vel_ratio) && (|전류| >= stall_current_a)

지령속도가 min_active_rpm(기본 3 RPM) 미만이면 판정 보류. 기본값: stall_vel_ratio=0.5, stall_current_a=12.0A(정격 15A 근접).

무부하/들뜸 판정 (classifyAirPair)

같은 지령속도를 받는 같은쪽 앞뒤 페어(FL↔RL, FR↔RR)의 전류를 비교한다. 좌/우를 통째로 비교하면 정상 커브 선회에서 좌우 지령속도가 원래 다르기 때문에 오탐이 발생하므로, 반드시 target_fl==target_rl, target_fr==target_rr가 항상 성립하는 앞뒤 페어끼리만 비교한다.

hi = max(|amp_a|, |amp_b|)
if (hi < 0.5A) return;  // 둘 다 무전류(관성 주행)면 판정 보류
if (a < hi * airborne_current_ratio) air_a = true;   // 기본 0.35
if (b < hi * airborne_current_ratio) air_b = true;

디바운스: 노이즈성 순간 전류 편차로 즉시 개입하지 않도록, airborne_debounce_ticks (기본 5틱=50ms) 연속으로 조건이 유지될 때만 실제 airborne_* 상태가 확정되고 해당 바퀴 target이 0으로 강제된다. 접지력이 회복돼 전류가 정상화되면 다음 판정 틱에서 자동 해제되고, §4.6의 착지 스냅 로직으로 즉시 원래 지령을 재개한다.

HUD의 WHL(FL/FR/RL/RR) 4글자는 각 바퀴 상태를 O(정상)/A(들뜸,Airborne)/S(걸림,Stall)로 표시.

4.9 드라이버 알람 감지

readFeedback()로 받은 err_f_l/err_f_r/err_r_l/err_r_r 중 하나라도 0이 아니면 알람 발생으로 간주, 상태 전이 시(0→알람) 1회만 콘솔에 decodeDriverError() 결과와 함께 큰 경고를 출력한다 (매 틱 갱신되는 HUD 줄과 별개 라인). 알람 발생 시 드라이버가 명령을 무시하는 잠금 상태가 되어 소프트웨어 재시작만으로는 해제되지 않는 경우가 많음을 코드 주석이 명시 — 하드웨어 알람 클리어 또는 전원 재투입이 필요할 수 있다. 상세 대응은 05-error-troubleshooting.md 참고.

4.10 CSV 로깅 (--log <경로>)

매 틱 아래 컬럼을 기록한다:

t_ms,state,ly,lx,rx,
cmd_fl,cmd_fr,cmd_rl,cmd_rr,
fb_fl,fb_fr,fb_rl,fb_rr,
amp_fl,amp_fr,amp_rl,amp_rr,
stat_fl,stat_fr,stat_rl,stat_rr,
err_fl,err_fr,err_rl,err_rr,
dist_axle,comm_ms

실외에서 로봇을 조종하며 화면을 동시에 읽기 어려우므로, 문제가 된 구간(턱 넘는 지점 등)을 나중에 잘라서 분석하기 위한 용도.

4.11 CLI 인자 전체 목록

인자 기본값 설명
--port, --port1 /dev/ttyUSB0 전방 드라이버(및 기본 후방) 시리얼 포트
--port2 (없음, port1 재사용) 후방 드라이버 전용 포트 지정 시
--id1 1 전방 드라이버 Modbus 슬레이브 주소
--id2 2 후방 드라이버 Modbus 슬레이브 주소
--bcast / --broadcast off 두 드라이버 모두 주소 0(브로드캐스트)으로 동시 제어
--track_width 0.576 m 좌우 바퀴 중심거리
--wheelbase 0.3684 m 전후 바퀴 중심거리
--radius 0.131517 m 휠 유효 반경
--bumper 0.045 m 휠 축→전면 범퍼 거리
--accel 120 RPM/s 최대 가속도
--decel 180 RPM/s 최대 감속도
--jerk 600 RPM/s² 직진/커브 저크 한계
--spin_jerk 220 RPM/s² 제자리 회전 저크 한계
--spin_arc_bias 0.12 제자리 회전 시 혼합할 미세 전진 비율 (0~1)
--min_active_rpm 3.0 RPM 이상판정 보류 임계 지령속도
--airborne_current_ratio 0.35 무부하(들뜸) 판정 전류비
--stall_vel_ratio 0.5 걸림 판정 속도비
--stall_current_a 12.0 A 걸림 판정 전류 임계값
--airborne_debounce_ticks 5 (50ms) 들뜸 판정 디바운스
--log <path> (비활성) CSV 로그 파일 경로
--max_current_a 20.0 A 4바퀴 최대전류 일괄 설정 (음수=미변경)
--no_lidar (라이다 사용) 라이다 비활성화
--lidar_config mid360s_config.json Livox SDK 설정 파일
--front_stop 0.35 m 전방 정지거리
--front_warn 0.50 m 전방 경고(감속)거리
--side_dodge 0.50 m 측면 회피 감지거리
--robot_width 0.410 m 라이다 회피 폭 계산용 로봇 전폭
--lidar_height 0.460 m 라이다 설치 높이
--lidar_pitch 17.0 deg 라이다 하향 피치각
--lidar_x_offset 0.250 m 라이다 전방 오프셋
--min_z / --max_z -0.40 / 0.80 m 라이다 필터링 Z축 범위