From 9a97cf90228821c91d72ac5f62aa5a3c5e754e8e Mon Sep 17 00:00:00 2001 From: robin Date: Thu, 20 Aug 2026 13:20:43 +0900 Subject: [PATCH] Add IMU integration (Phases 1-5a) and RC/kinematics reliability fixes IMU integration (WitMotion HWT905-RS232, doc/06-imu-integration-plan.md): - Phase 1: raw accel/gyro/angle observation, power-on-relative yaw offset - Phase 2: x,y odometry + wheel-vs-IMU omega residual, using a self-fused heading (wheel encoder omega + raw gyro_z average) instead of the IMU's own onboard fused yaw, which field logs showed disagreeing with its own raw gyro sign ~30% of the time during turns - Phase 3: straight-line heading-hold PI trim, active only when steering is centered and no lidar dodge is in progress, capped and field-tuned - Phase 4: whole-body slip detection (commanded vs IMU-measured omega ratio) that temporarily scales down v_x/omega, independent of the per-wheel current-based diagnostics - Phase 5a: k_skid/effective_w replaced with values fitted from real wheel-vs-IMU logs (0.406->0.51, 0.684->0.87) instead of the geometric formula, which field data showed under-driving every turn RC receiver: switched from ttyUSB* guessing to udev-stable device names (ttyMOTOR/ttyRC/ttyIMU), wired through run_4wd.sh and set_low_latency.sh. Motor driver: read motor/driver temperature registers (0x20A4/0x20B0) for proactive overheat visibility in the HUD/CSV. Control fixes: - Airborne-wheel zeroing no longer applies during spin turns, where reaction-torque-driven diagonal load transfer was misclassified as wheels leaving the ground and cutting spin torque in half - jerkLimitedStep's instant-acceleration path now only fires for same-direction speed increases; sign reversals (RC noise/deadzone jitter near a stop, or a genuine direction change) go through the jerk-limited path instead of snapping across zero Auto CSV logging (logs/, gitignored) extended with encoder ticks, IMU, odometry, heading-hold, slip, and temperature columns for field analysis. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 1 + imu/imu_test | 236 ++++++++++++++ run_4wd.sh | 28 +- set_low_latency.sh | 5 +- xbox_motor_control.cpp | 687 ++++++++++++++++++++++++++++++++++++++--- xbox_motor_control_cpp | Bin 91368 -> 113624 bytes 6 files changed, 900 insertions(+), 57 deletions(-) create mode 100644 .gitignore create mode 100644 imu/imu_test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..333c1e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +logs/ diff --git a/imu/imu_test b/imu/imu_test new file mode 100644 index 0000000..bed989d --- /dev/null +++ b/imu/imu_test @@ -0,0 +1,236 @@ +// HWT905-RS232 (WitMotion) IMU reader — Phase 1: pure observation, no control loop. +// Reads the sensor's active-push protocol (0x55-prefixed packets) over a plain +// serial port (RS232-over-USB, e.g. /dev/ttyUSB0) and prints accel/gyro/angle to +// stdout, optionally logging to CSV. Not Modbus — the RS485 variant uses Modbus, +// this RS232 variant does not. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr uint8_t kFrameHeader = 0x55; +constexpr uint8_t kTypeAccel = 0x51; +constexpr uint8_t kTypeGyro = 0x52; +constexpr uint8_t kTypeAngle = 0x53; +constexpr uint8_t kTypeMag = 0x54; + +struct ImuState { + double accel[3] = {0, 0, 0}; // g + double gyro[3] = {0, 0, 0}; // deg/s + double angle[3] = {0, 0, 0}; // deg (roll, pitch, yaw) + double mag[3] = {0, 0, 0}; // raw counts + bool has_accel = false, has_gyro = false, has_angle = false, has_mag = false; +}; + +int16_t toInt16(uint8_t lo, uint8_t hi) { + return static_cast(static_cast(lo) | (static_cast(hi) << 8)); +} + +speed_t baudToSpeed(int baud) { + switch (baud) { + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; + default: + std::cerr << "[경고] 지원하지 않는 baud " << baud << ", 115200으로 대체\n"; + return B115200; + } +} + +int openSerialPort(const std::string &port, int baud) { + int fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) { + std::cerr << "[오류] 포트 열기 실패: " << port << " (" << std::strerror(errno) << ")\n"; + return -1; + } + fcntl(fd, F_SETFL, 0); // switch back to blocking reads + + struct termios options; + if (tcgetattr(fd, &options) != 0) { + std::cerr << "[오류] tcgetattr 실패\n"; + close(fd); + return -1; + } + + speed_t speed = baudToSpeed(baud); + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + + options.c_cflag |= (CLOCAL | CREAD); + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + options.c_cflag &= ~CRTSCTS; + + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_iflag &= ~(IXON | IXOFF | IXANY); + options.c_iflag &= ~(INLCR | ICRNL); + options.c_oflag &= ~OPOST; + + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 5; // 0.5s inter-byte timeout + + tcflush(fd, TCIFLUSH); + if (tcsetattr(fd, TCSANOW, &options) != 0) { + std::cerr << "[오류] tcsetattr 실패\n"; + close(fd); + return -1; + } + return fd; +} + +// Parses one validated 11-byte WitMotion frame (frame[0]==0x55, checksum ok) +// into the running ImuState. Returns true if this frame completed an +// accel+gyro+angle group worth printing (i.e. it was an angle frame). +bool applyFrame(const uint8_t *frame, ImuState &state) { + const uint8_t type = frame[1]; + switch (type) { + case kTypeAccel: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + state.accel[i] = raw / 32768.0 * 16.0; // g + } + state.has_accel = true; + return false; + case kTypeGyro: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + state.gyro[i] = raw / 32768.0 * 2000.0; // deg/s + } + state.has_gyro = true; + return false; + case kTypeAngle: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + state.angle[i] = raw / 32768.0 * 180.0; // deg + } + state.has_angle = true; + return true; + case kTypeMag: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + state.mag[i] = raw; + } + state.has_mag = true; + return false; + default: + return false; // time/quaternion/GPS frames etc. — ignored in Phase 1 + } +} + +} // namespace + +int main(int argc, char **argv) { + std::setvbuf(stdout, nullptr, _IOLBF, 4096); // line-buffer even when piped + + std::string port = "/dev/ttyUSB0"; + int baud = 9600; // HWT905-232 factory default (confirmed against this unit) + std::string log_path; + + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--port" && i + 1 < argc) { + port = argv[++i]; + } else if (arg == "--baud" && i + 1 < argc) { + baud = std::stoi(argv[++i]); + } else if (arg == "--log" && i + 1 < argc) { + log_path = argv[++i]; + } else if (arg == "--help") { + std::cout << "사용법: " << argv[0] + << " [--port /dev/ttyUSB0] [--baud 115200] [--log out.csv]\n"; + return 0; + } + } + + int fd = openSerialPort(port, baud); + if (fd < 0) return 1; + + std::ofstream log_file; + if (!log_path.empty()) { + log_file.open(log_path); + if (!log_file) { + std::cerr << "[오류] 로그 파일 열기 실패: " << log_path << "\n"; + return 1; + } + log_file << "t_s,ax_g,ay_g,az_g,gx_dps,gy_dps,gz_dps,roll_deg,pitch_deg,yaw_deg\n"; + } + + std::cout << "포트 " << port << " @ " << baud << "bps 에서 IMU 데이터 수신 시작 " + << "(Ctrl+C로 종료)\n"; + std::cout.flush(); + + const auto t_start = std::chrono::steady_clock::now(); + ImuState state; + uint8_t buf[11]; + size_t buf_len = 0; + + while (true) { + uint8_t byte; + ssize_t n = read(fd, &byte, 1); + if (n <= 0) { + if (n < 0 && errno != EAGAIN && errno != EINTR) { + std::cerr << "[오류] 시리얼 읽기 실패: " << std::strerror(errno) << "\n"; + break; + } + continue; + } + + if (buf_len == 0) { + if (byte != kFrameHeader) continue; // resync: wait for header + buf[buf_len++] = byte; + continue; + } + + buf[buf_len++] = byte; + if (buf_len < 11) continue; + + // Full 11-byte candidate frame collected — validate checksum. + uint8_t sum = 0; + for (int i = 0; i < 10; ++i) sum += buf[i]; + if (sum != buf[10]) { + // Checksum mismatch: resync by sliding one byte and rescanning for 0x55. + std::cerr << "[경고] 체크섬 불일치, 프레임 폐기\n"; + buf_len = 0; + continue; + } + + bool print_now = applyFrame(buf, state); + buf_len = 0; + + if (print_now && state.has_accel && state.has_gyro && state.has_angle) { + double t_s = std::chrono::duration(std::chrono::steady_clock::now() - t_start).count(); + std::printf( + "t=%7.3fs accel[g]=(%+.3f,%+.3f,%+.3f) gyro[dps]=(%+7.2f,%+7.2f,%+7.2f) " + "angle[deg]=(roll=%+7.2f,pitch=%+7.2f,yaw=%+7.2f)\n", + t_s, state.accel[0], state.accel[1], state.accel[2], state.gyro[0], state.gyro[1], + state.gyro[2], state.angle[0], state.angle[1], state.angle[2]); + + if (log_file) { + log_file << t_s << ',' << state.accel[0] << ',' << state.accel[1] << ',' + << state.accel[2] << ',' << state.gyro[0] << ',' << state.gyro[1] << ',' + << state.gyro[2] << ',' << state.angle[0] << ',' << state.angle[1] << ',' + << state.angle[2] << '\n'; + log_file.flush(); + } + } + } + + close(fd); + return 0; +} diff --git a/run_4wd.sh b/run_4wd.sh index a00b7bb..adadd53 100755 --- a/run_4wd.sh +++ b/run_4wd.sh @@ -1,9 +1,11 @@ #!/usr/bin/env bash # 1-Click Launch Script for C++ 4WD Motor Control with Mid-360S LiDAR Avoidance -# 현재 고정 배선: 모터 드라이버 = ttyUSB1, RC 수신기(XR1) = ttyUSB0 -PORT="${1:-/dev/ttyUSB1}" -RC_PORT="${RC_PORT:-/dev/ttyUSB0}" +# udev 규칙(/etc/udev/rules.d/99-fori-robot-serial.rules)으로 고정된 심볼릭 +# 링크 사용 — ttyUSB 번호는 꽂는 순서에 따라 바뀌지만 이 이름들은 고정이다. +PORT="${1:-/dev/ttyMOTOR}" +RC_PORT="${RC_PORT:-/dev/ttyRC}" +IMU_PORT="${IMU_PORT:-/dev/ttyIMU}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" || exit 1 @@ -12,11 +14,12 @@ echo "==================================================================" echo " 🚀 ZLAC8015D 4WD + Livox Mid-360S 라이다 원클릭 런치 시스템" echo "==================================================================" -# 1. USB Latency 1ms 단축 (모터 포트 + RC 수신기 포트 둘 다) +# 1. USB Latency 1ms 단축 (모터/RC/IMU 포트 전부) if [ -f "./set_low_latency.sh" ]; then - echo "[1/2] USB 시리얼 포트 지연 시간 1ms 최적화 적용 중... (모터: $PORT / RC: $RC_PORT)" + echo "[1/2] USB 시리얼 포트 지연 시간 1ms 최적화 적용 중... (모터: $PORT / RC: $RC_PORT / IMU: $IMU_PORT)" ./set_low_latency.sh "$PORT" ./set_low_latency.sh "$RC_PORT" + ./set_low_latency.sh "$IMU_PORT" fi # 2. C++ 바이너리 존재 여부 확인 및 컴파일 @@ -26,16 +29,17 @@ if [ ! -f "./xbox_motor_control_cpp" ]; then fi echo "==================================================================" -echo " [시작] C++ 100Hz 초저지연 4WD RC(RadioMaster Pocket + XR1) + 라이다 장애물 회피 시작" -echo " - 모터 포트: $PORT / RC 수신기 포트: $RC_PORT" -echo " - 라이다 기능 비활성화: --no_lidar 옵션" +echo " [시작] C++ 100Hz 초저지연 4WD RC(RadioMaster Pocket + XR1) + IMU(HWT905) + 라이다 장애물 회피 시작" +echo " - 모터 포트: $PORT / RC 수신기 포트: $RC_PORT / IMU 포트: $IMU_PORT" +echo " - 라이다 기능 비활성화: --no_lidar 옵션 / IMU 비활성화: --no_imu 옵션" +echo " - 매 주행마다 logs/에 CSV 로그 자동 저장 (끄려면 --no_log)" echo " - 비상 정지: CH5 브레이크 스위치 또는 Ctrl+C" echo "==================================================================" -# 3. C++ 4WD 프로그램 실행 (추가 인자 전달 가능. --rc_port를 다시 넘기면 -# 아래 기본값을 덮어쓸 수 있다 — 인자 파싱은 뒤에 온 값이 우선 적용됨) +# 3. C++ 4WD 프로그램 실행 (추가 인자 전달 가능. --rc_port/--imu_port를 다시 +# 넘기면 아래 기본값을 덮어쓸 수 있다 — 인자 파싱은 뒤에 온 값이 우선 적용됨) if [ $# -gt 1 ]; then - ./xbox_motor_control_cpp --port "$PORT" --rc_port "$RC_PORT" "${@:2}" + ./xbox_motor_control_cpp --port "$PORT" --rc_port "$RC_PORT" --imu_port "$IMU_PORT" "${@:2}" else - ./xbox_motor_control_cpp --port "$PORT" --rc_port "$RC_PORT" + ./xbox_motor_control_cpp --port "$PORT" --rc_port "$RC_PORT" --imu_port "$IMU_PORT" fi diff --git a/set_low_latency.sh b/set_low_latency.sh index d917b56..cea656d 100755 --- a/set_low_latency.sh +++ b/set_low_latency.sh @@ -8,7 +8,10 @@ if [ ! -e "$PORT" ]; then exit 1 fi -DEV_NAME=$(basename "$PORT") +# /dev/ttyMOTOR 같은 udev 고정 심볼릭 링크로 넘어올 수 있으므로, sysfs 조회에 +# 필요한 실제 장치명(ttyUSB0 등)으로 반드시 풀어준다 — 안 그러면 +# /sys/bus/usb-serial/devices/ttyMOTOR 경로가 존재하지 않아 항상 폴백으로 샌다. +DEV_NAME=$(basename "$(readlink -f "$PORT")") LATENCY_PATH="/sys/bus/usb-serial/devices/$DEV_NAME/latency_timer" if [ -f "$LATENCY_PATH" ]; then diff --git a/xbox_motor_control.cpp b/xbox_motor_control.cpp index 4af3b35..34b7a01 100644 --- a/xbox_motor_control.cpp +++ b/xbox_motor_control.cpp @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -315,33 +317,39 @@ public: bool readFeedback(float &l_fb, float &r_fb, int32_t &l_tick, int32_t &r_tick, float &l_torque_a, float &r_torque_a, - uint16_t &err_l, uint16_t &err_r) { + uint16_t &err_l, uint16_t &err_r, int &l_temp_c, + int &r_temp_c) { std::vector regs; - // 0x20A5~0x20AE 10레지스터 연속 읽기: 에러코드(0x20A5/0x20A6)부터 - // 포지션 틱, RPM 피드백, 실제 토크(전류)까지 한 번의 통신으로 확보. + // 0x20A4~0x20AE 11레지스터 연속 읽기: 모터온도(0x20A4)부터 에러코드 + // (0x20A5/0x20A6), 포지션 틱, RPM 피드백, 실제 토크(전류)까지 한 번의 + // 통신으로 확보. 0x20A4는 기존 읽기 범위 바로 앞이라 추가 통신 없이 + // 온도까지 같이 받아온다(과열 사전감지용, doc/02 §읽기전용상태 참고). // 무부하(공중에 뜬) 바퀴는 전류가 급격히 낮아지므로 슬립 진단에 사용, // 에러코드는 과전류/과부하 등 드라이버 알람 발생 시 원인 진단에 사용. - if (port_->readRegs(slave_id_, 0x20A5, 10, regs)) { - err_l = regs[0]; - err_r = regs[1]; + if (port_->readRegs(slave_id_, 0x20A4, 11, regs)) { + l_temp_c = static_cast((regs[0] >> 8) & 0xFF); + r_temp_c = static_cast(regs[0] & 0xFF); + + err_l = regs[1]; + err_r = regs[2]; // 수정: uint32_t -> int32_t 캐스팅은 2의 보수 표현에서 안전하게 부호가 // 재해석됨. 기존의 "val - 0x100000000ULL" 방식은 uint64_t 승격 후 // int32_t로 축소 캐스팅하는 구현정의 동작(현실적으로는 대부분 동작하지만 // 명확성/이식성이 떨어짐)이라 제거함. - uint32_t val_l = (static_cast(regs[2]) << 16) | regs[3]; + uint32_t val_l = (static_cast(regs[3]) << 16) | regs[4]; l_tick = static_cast(val_l); - uint32_t val_r = (static_cast(regs[4]) << 16) | regs[5]; + uint32_t val_r = (static_cast(regs[5]) << 16) | regs[6]; r_tick = static_cast(val_r); - int16_t vl = static_cast(regs[6]); - int16_t vr = static_cast(regs[7]); + int16_t vl = static_cast(regs[7]); + int16_t vr = static_cast(regs[8]); l_fb = static_cast(vl) * 0.1f; // 0.1RPM 단위 -> RPM r_fb = static_cast(vr) * 0.1f; - int16_t tl = static_cast(regs[8]); - int16_t tr = static_cast(regs[9]); + int16_t tl = static_cast(regs[9]); + int16_t tr = static_cast(regs[10]); l_torque_a = static_cast(tl) * 0.1f; // 0.1A 단위 -> A r_torque_a = static_cast(tr) * 0.1f; return true; @@ -349,6 +357,17 @@ public: return false; } + // 드라이버 자체 온도(0x20B0, 0.1℃). 열 시정수가 초 단위로 느려서 매 틱 + // 읽을 필요가 없어 별도 저빈도 폴링용으로 분리(readFeedback 연속범위와 + // 떨어져 있어 합치면 통신 1회가 늘어남). + bool readDriverTemp(float &temp_c) { + std::vector regs; + if (!port_->readRegs(slave_id_, 0x20B0, 1, regs)) + return false; + temp_c = static_cast(regs[0]) * 0.1f; + return true; + } + // 정격전류(0x2033/0x2063)·최대전류(0x2034/0x2064) 실측 조회. 채널당 // 2레지스터씩 떨어져 있어 L/R 두 번 읽음(설정값이라 시작 시 1회만 조회). bool readCurrentLimits(uint16_t &rated_l, uint16_t &max_l, uint16_t &rated_r, @@ -628,7 +647,9 @@ struct termios2 { // 의 커널 ABI와 동일 레이아웃 (glib }; struct RcConfig { - std::string port = "/dev/ttyUSB0"; // 현재 고정 배선: RC 수신기(XR1) = ttyUSB0 + // udev 규칙(/etc/udev/rules.d/99-fori-robot-serial.rules)으로 고정된 + // 심볼릭 링크. ttyUSB 번호는 꽂는 순서에 따라 바뀌지만 이 이름은 고정. + std::string port = "/dev/ttyRC"; int baud = 420000; // CRSF 표준 보드레이트 int ch_steer = 1; // CH1: 좌우 조향 @@ -914,20 +935,285 @@ private: } }; +// ----------------------------------------------------------------------------- +// WitMotion HWT905-RS232 IMU 리더 (doc/06-imu-integration-plan.md Phase 1) +// RS485/Modbus 변형과 달리 이 RS232 변형은 액티브 푸시 방식 바이너리 프로토콜을 +// 쓴다: [0]=0x55 헤더, [1]=타입(0x51 가속도/0x52 자이로/0x53 각도/0x54 지자기), +// [2..9]=int16×4(LE, XYZ+예약), [10]=체크섬(0~9바이트 합의 하위 8비트). +// imu/imu_test에서 실기로 검증된 프로토콜/스케일을 그대로 이식했다. +// Phase 1 범위: 순수 관측만 한다 — 제어 루프에는 절대 개입하지 않는다. +// ----------------------------------------------------------------------------- +struct ImuConfig { + // udev 규칙으로 고정된 심볼릭 링크 (모터=/dev/ttyMOTOR, RC=/dev/ttyRC와 별도) + std::string port = "/dev/ttyIMU"; + int baud = 9600; // HWT905-232 공장 출하 기본값(실기 확인됨) + int failsafe_timeout_ms = 500; // 이 시간 이상 미수신 시 미연결로 표시 +}; + +class ImuReader { +public: + struct Snapshot { + double accel[3] = {0, 0, 0}; // g (x,y,z) + double gyro[3] = {0, 0, 0}; // deg/s (x,y,z) + double angle[3] = {0, 0, 0}; // deg (roll, pitch, yaw) + bool valid = false; + }; + + explicit ImuReader(const ImuConfig &cfg) : cfg_(cfg) {} + ~ImuReader() { stop(); } + + void start() { + running_ = true; + thread_ = std::thread(&ImuReader::run, this); + } + + void stop() { + running_ = false; + if (thread_.joinable()) + thread_.join(); + closePort(); + } + + bool isConnected() { + std::lock_guard lock(mutex_); + if (!port_open_ || !has_angle_) + return false; + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast( + now - last_rx_time_) + .count() <= cfg_.failsafe_timeout_ms; + } + + Snapshot getSnapshot() { + std::lock_guard lock(mutex_); + Snapshot s; + for (int i = 0; i < 3; ++i) { + s.accel[i] = accel_[i]; + s.gyro[i] = gyro_[i]; + s.angle[i] = angle_[i]; + } + s.valid = isConnectedLocked(); + return s; + } + +private: + ImuConfig cfg_; + std::atomic running_{false}; + std::thread thread_; + int fd_ = -1; + + std::mutex mutex_; + double accel_[3] = {0, 0, 0}; + double gyro_[3] = {0, 0, 0}; + double angle_[3] = {0, 0, 0}; + bool has_accel_ = false, has_gyro_ = false, has_angle_ = false; + std::chrono::steady_clock::time_point last_rx_time_{}; + bool port_open_ = false; + + static constexpr uint8_t kFrameHeader = 0x55; + static constexpr uint8_t kTypeAccel = 0x51; + static constexpr uint8_t kTypeGyro = 0x52; + static constexpr uint8_t kTypeAngle = 0x53; + + bool isConnectedLocked() const { + if (!port_open_ || !has_angle_) + return false; + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast( + now - last_rx_time_) + .count() <= cfg_.failsafe_timeout_ms; + } + + static int16_t toInt16(uint8_t lo, uint8_t hi) { + return static_cast(static_cast(lo) | + (static_cast(hi) << 8)); + } + + static speed_t baudToSpeed(int baud) { + switch (baud) { + case 4800: + return B4800; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + default: + return B9600; + } + } + + bool openPort() { + fd_ = open(cfg_.port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if (fd_ < 0) + return false; + fcntl(fd_, F_SETFL, 0); // 블로킹 읽기로 전환 + + struct termios options; + if (tcgetattr(fd_, &options) != 0) { + close(fd_); + fd_ = -1; + return false; + } + + speed_t speed = baudToSpeed(cfg_.baud); + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + + options.c_cflag |= (CLOCAL | CREAD); + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + options.c_cflag &= ~CRTSCTS; + + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_iflag &= ~(IXON | IXOFF | IXANY); + options.c_iflag &= ~(INLCR | ICRNL); + options.c_oflag &= ~OPOST; + + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 5; // 0.5초 바이트 간 타임아웃 (종료 감지용) + + tcflush(fd_, TCIFLUSH); + if (tcsetattr(fd_, TCSANOW, &options) != 0) { + close(fd_); + fd_ = -1; + return false; + } + return true; + } + + void closePort() { + if (fd_ >= 0) { + close(fd_); + fd_ = -1; + } + std::lock_guard lock(mutex_); + port_open_ = false; + } + + // 검증된 11바이트 프레임 하나를 상태에 반영. 각도 프레임 완료 시 true. + bool applyFrame(const uint8_t *frame) { + const uint8_t type = frame[1]; + switch (type) { + case kTypeAccel: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + accel_[i] = raw / 32768.0 * 16.0; // g + } + has_accel_ = true; + return false; + case kTypeGyro: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + gyro_[i] = raw / 32768.0 * 2000.0; // deg/s + } + has_gyro_ = true; + return false; + case kTypeAngle: + for (int i = 0; i < 3; ++i) { + int16_t raw = toInt16(frame[2 + 2 * i], frame[3 + 2 * i]); + angle_[i] = raw / 32768.0 * 180.0; // deg + } + has_angle_ = true; + return true; + default: + return false; // 시간/쿼터니언/GPS 등: Phase 1에서는 무시 + } + } + + void run() { + auto last_open_attempt = + std::chrono::steady_clock::now() - std::chrono::milliseconds(1001); + uint8_t buf[11]; + size_t buf_len = 0; + + while (running_) { + if (fd_ < 0) { + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_open_attempt) + .count() > 1000) { + last_open_attempt = now; + if (openPort()) { + std::lock_guard lock(mutex_); + port_open_ = true; + std::cout << "\n[정보] IMU 포트(" << cfg_.port << ") 연결 성공.\n"; + } else { + std::cerr << "\n[경고] IMU 포트(" << cfg_.port + << ") 열기 실패. 1초 후 재시도.\n"; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + buf_len = 0; + continue; + } + + uint8_t byte; + ssize_t n = read(fd_, &byte, 1); + if (n <= 0) { + if (n < 0 && errno != EAGAIN && errno != EINTR) { + std::cerr << "\n[경고] IMU 포트 통신 오류. 재연결 시도...\n"; + closePort(); + buf_len = 0; + } + continue; // n==0: VTIME 타임아웃, running_ 재확인 후 계속 대기 + } + + if (buf_len == 0) { + if (byte != kFrameHeader) + continue; // 헤더 대기하며 재동기화 + buf[buf_len++] = byte; + continue; + } + + buf[buf_len++] = byte; + if (buf_len < 11) + continue; + + uint8_t sum = 0; + for (int i = 0; i < 10; ++i) + sum += buf[i]; + if (sum != buf[10]) { + buf_len = 0; // 체크섬 불일치: 프레임 폐기 후 재동기화 + continue; + } + + bool angle_done; + { + std::lock_guard lock(mutex_); + angle_done = applyFrame(buf); + if (angle_done) + last_rx_time_ = std::chrono::steady_clock::now(); + } + buf_len = 0; + (void)angle_done; + } + } +}; + int main(int argc, char **argv) { std::signal(SIGINT, signalHandler); std::signal(SIGTERM, signalHandler); - std::string port1 = "/dev/ttyUSB1"; // 현재 고정 배선: 모터 드라이버 = ttyUSB1 + // udev 규칙(/etc/udev/rules.d/99-fori-robot-serial.rules)으로 고정된 + // 심볼릭 링크. ttyUSB 번호는 꽂는 순서에 따라 바뀌지만 이 이름은 고정. + std::string port1 = "/dev/ttyMOTOR"; std::string port2 = ""; uint8_t id1 = 1; uint8_t id2 = 2; bool bcast_mode = false; RcConfig rc_cfg; float max_spin_v = 0.30f; // m/s - float track_width = 0.576f; // 좌우 바퀴 중심 거리 576mm - float wheelbase = 0.3684f; // 전후 바퀴 중심 거리 368.4mm float wheel_radius = 0.131517f; // 사용자 실측 정밀 10.35인치 휠 반지름 + float k_skid_override = -1.0f; // 0 이상이면 Phase 5a 기본 캘리브레이션값을 대체 + float effective_w_override = -1.0f; // 0 이상이면 Phase 5a 기본 캘리브레이션값을 대체 // 가속은 역기전력 보호회로가 하드웨어로 구성되어 목표값에 1:1로 즉시 // 추종하므로 별도의 가속 한계값이 없다. decel_rate/jerk_rate(및 // spin_jerk_rate)는 감속에만 적용된다. @@ -939,6 +1225,27 @@ int main(int argc, char **argv) { // 살짝 벗어나게 해 스크럽 마찰 저항을 줄임) float bumper_offset = 0.045f; // 바퀴 축에서 로봇 맨 앞 범퍼까지의 거리 + // Phase 3 — 직진 헤딩 홀드 PI 트림 (doc/06-imu-integration-plan.md §Phase 3). + // 조향 중립 + 라이다 회피 미개입 + 실제 주행 중일 때만 개입해, 진입 순간의 + // fused_yaw_deg를 목표로 고정하고 그로부터 벗어난 만큼을 PI로 보정한다. + bool heading_hold_enabled = true; + float heading_kp = 0.6f; // rad/s per rad 오차 + float heading_ki = 0.15f; // rad/s per (rad·s) 적분 오차 + float heading_max_trim = 0.15f; // rad/s (트림 상한 — 의도적 조향을 압도하지 않도록) + + // Phase 4 — IMU 기반 전신(whole-body) 슬립 감지 (doc §Phase 4). 지령 + // omega 대비 바퀴 피드백과 무관한 IMU 원시 gyro_z 비율이 임계값 밑으로 + // 지속되면 "전신 슬립"(빙판/젖은 잔디 등 4륜 동시 헛돎)으로 판정해 라이다 + // 회피의 speed_scale과 같은 패턴으로 속도를 일시적으로 낮춘다. 제자리 + // 회전은 반력 토크로 인한 정상적인 스크럽 손실(지령 대비 40~60% 미달도 + // 흔함)이 있어 문턱값을 낮게(=크게 미달일 때만) 잡아 정상 회전을 오탐하지 + // 않게 한다. + bool whole_slip_enabled = true; + float whole_slip_min_omega = 0.15f; // rad/s (이 미만 지령은 판정 보류) + float whole_slip_ratio_threshold = 0.15f; // |imu_omega|/|omega| 가 이 밑이면 슬립 후보 + int whole_slip_debounce_ticks = 15; // 틱 (100Hz 기준 150ms) 연속돼야 확정 + float whole_slip_speed_scale = 0.5f; // 슬립 확정 시 v_x/omega에 곱하는 배율 + // 바퀴별 이상(들뜸/걸림) 판정 파라미터: 속도 폐루프 특성상 "지령 대비 실제 // 속도" 하나만으로는 무부하(들뜸) 판정이 안 되므로, 동료 바퀴 대비 전류 // 편차와 함께 두 축으로 판정한다. @@ -951,13 +1258,21 @@ int main(int argc, char **argv) { // 지연이 동반되면 걸림/과부하로 판정 int airborne_debounce_ticks = 5; // 틱 (100Hz 기준 50ms) 연속 들뜸 판정 // 시에만 실제로 목표속도를 낮춤 - std::string log_path = ""; // 비어있으면 로깅 비활성화. --log <파일경로>로 지정 + // 비어있으면 프로그램 시작 시 logs/drive_YYYYmmdd_HHMMSS.csv로 자동 생성된다 + // (매 주행마다 RC/IMU/모터 로깅을 자동으로 별도 파일에 남기기 위함). + // --log <파일경로>로 직접 지정하거나 --no_log로 완전히 끌 수 있다. + std::string log_path = ""; + bool no_log = false; // 실측 결과 정격15A/최대30A(공장 기본값) 그대로였음. 정격 위로 5A 여유는 // 남기되, 걸림/과부하 상황에서 30A까지 밀어붙이며 3초씩 버티다 과부하 // 알람이 터지는 걸 막기 위해 기본값을 20A로 낮춤. --max_current_a로 조정 가능. // 음수를 주면 미변경(공장/기존 설정 유지). float max_current_a = 20.0f; + // IMU 파라미터 (doc/06-imu-integration-plan.md Phase 1: 순수 관측) + bool use_imu = true; + ImuConfig imu_cfg; + // LiDAR Parameters & Robot Physical Specs (User Specification) bool use_lidar = true; std::string lidar_config = "mid360s_config.json"; @@ -987,10 +1302,10 @@ int main(int argc, char **argv) { id1 = static_cast(std::stoi(argv[++i])); else if (arg == "--id2" && i + 1 < argc) id2 = static_cast(std::stoi(argv[++i])); - else if (arg == "--track_width" && i + 1 < argc) - track_width = std::stof(argv[++i]); - else if (arg == "--wheelbase" && i + 1 < argc) - wheelbase = std::stof(argv[++i]); + else if (arg == "--k_skid" && i + 1 < argc) + k_skid_override = std::stof(argv[++i]); + else if (arg == "--effective_w" && i + 1 < argc) + effective_w_override = std::stof(argv[++i]); else if (arg == "--radius" && i + 1 < argc) wheel_radius = std::stof(argv[++i]); else if (arg == "--bumper" && i + 1 < argc) @@ -1015,6 +1330,8 @@ int main(int argc, char **argv) { airborne_debounce_ticks = std::stoi(argv[++i]); else if (arg == "--log" && i + 1 < argc) log_path = argv[++i]; + else if (arg == "--no_log") + no_log = true; else if (arg == "--max_current_a" && i + 1 < argc) max_current_a = std::stof(argv[++i]); else if (arg == "--bcast" || arg == "--broadcast") @@ -1041,6 +1358,12 @@ int main(int argc, char **argv) { max_z = std::stof(argv[++i]); else if (arg == "--no_lidar") use_lidar = false; + else if (arg == "--imu_port" && i + 1 < argc) + imu_cfg.port = argv[++i]; + else if (arg == "--imu_baud" && i + 1 < argc) + imu_cfg.baud = std::stoi(argv[++i]); + else if (arg == "--no_imu") + use_imu = false; else if (arg == "--rc_port" && i + 1 < argc) rc_cfg.port = argv[++i]; else if (arg == "--rc_baud" && i + 1 < argc) @@ -1085,15 +1408,55 @@ int main(int argc, char **argv) { rc_cfg.failsafe_timeout_ms = std::stoi(argv[++i]); else if (arg == "--max_spin_v" && i + 1 < argc) max_spin_v = std::stof(argv[++i]); + else if (arg == "--no_heading_hold") + heading_hold_enabled = false; + else if (arg == "--heading_kp" && i + 1 < argc) + heading_kp = std::stof(argv[++i]); + else if (arg == "--heading_ki" && i + 1 < argc) + heading_ki = std::stof(argv[++i]); + else if (arg == "--heading_max_trim" && i + 1 < argc) + heading_max_trim = std::stof(argv[++i]); + else if (arg == "--no_whole_slip") + whole_slip_enabled = false; + else if (arg == "--whole_slip_min_omega" && i + 1 < argc) + whole_slip_min_omega = std::stof(argv[++i]); + else if (arg == "--whole_slip_ratio" && i + 1 < argc) + whole_slip_ratio_threshold = std::stof(argv[++i]); + else if (arg == "--whole_slip_debounce_ticks" && i + 1 < argc) + whole_slip_debounce_ticks = std::stoi(argv[++i]); + else if (arg == "--whole_slip_speed_scale" && i + 1 < argc) + whole_slip_speed_scale = std::stof(argv[++i]); + } + + // --log를 직접 지정하지 않았으면 이 저장소(프로젝트 폴더) 안의 logs/에 + // 타임스탬프 파일명으로 자동 생성한다 — 매 주행마다 RC/IMU/모터 로깅을 + // 빠짐없이 남기기 위함. --no_log로 완전히 끌 수 있다. + if (log_path.empty() && !no_log) { + std::filesystem::create_directories("logs"); + std::time_t now_c = std::time(nullptr); + char ts_buf[32]; + std::strftime(ts_buf, sizeof(ts_buf), "%Y%m%d_%H%M%S", std::localtime(&now_c)); + log_path = std::string("logs/drive_") + ts_buf + ".csv"; } constexpr float PI_VAL = 3.14159265358979323846f; float rpm_per_ms = 60.0f / (2.0f * PI_VAL * wheel_radius); - // 4WD 유효 선회 계수 - float effective_w = - std::sqrt(track_width * track_width + wheelbase * wheelbase); - float k_skid = (track_width + (wheelbase * wheelbase) / track_width) / 2.0f; + // effective_w — Phase 5a 정적 캘리브레이션. 기하학적 공식값(대각선 길이, + // 0.684)은 제자리 회전 실측 대비 작았다. 전용 회전 로그 2개(무결점 + // 405틱, 두 로그 개별 중앙값 0.855/0.904로 서로 일치)에서 역산한 + // 중앙값 0.87을 대신 사용한다. --effective_w로 재조정 가능. + float effective_w = (effective_w_override >= 0.0f) ? effective_w_override : 0.87f; + // k_skid — Phase 5a 정적 캘리브레이션(doc/06-imu-integration-plan.md §5a): + // 기하학적 공식값(트랙폭/휠베이스로만 계산, 0.406)은 실측 대비 계속 + // 작게 나왔다(회전이 지령보다 항상 덜 도는 원인 중 하나). 두 차례 + // 실주행 로그(커브 선회 중 무결점 총 5,150틱)에서 바퀴 실측 속도차 대 + // IMU 실측 회전율로 역산한 중앙값이 0.51 적용 전 0.510, 적용 후에도 + // 0.517로 수렴해 그대로 0.51을 유지한다. 회전 반경에 따라 + // 0.44(완만한 커브)~0.53(급한 커브)로 흔들리는 편이라 0.51은 그 + // 절충값 — 반경별로 정밀하게 맞추려면 Phase 5b(온라인 ICR 추정)가 + // 필요하다. --k_skid로 재조정 가능. + float k_skid = (k_skid_override >= 0.0f) ? k_skid_override : 0.51f; std::cout << "\n=============================================================" "=====\n"; @@ -1129,20 +1492,27 @@ int main(int argc, char **argv) { std::cout << "==================================================================\n"; - // CSV 로깅: --log <경로> 지정 시 매 틱 지령/피드백/전류/휠상태를 파일로 기록. - // 실외에서 로봇을 조종하며 화면을 동시에 읽기 어려우므로, 문제가 된 - // 구간(턱 넘는 지점 등)을 나중에 잘라서 분석하기 위한 용도. + // CSV 로깅: 기본적으로 매 주행마다 logs/에 자동 저장된다(위 auto-path 로직). + // RC 채널, IMU(자이로/각도), 모터 지령/피드백/전류/휠상태/엔코더 raw tick까지 + // 매 틱 전부 기록한다. 실외에서 로봇을 조종하며 화면을 동시에 읽기 어려우므로, + // 문제가 된 구간(턱 넘는 지점 등)이나 IMU-엔코더 융합용 데이터를 나중에 잘라서 + // 분석하기 위한 용도. std::ofstream log_stream; if (!log_path.empty()) { log_stream.open(log_path); if (log_stream.is_open()) { log_stream << "t_ms,state,rc_steer,rc_throttle,rc_ok,brake,vmax,vx,omega," "ch1,ch2,ch3,ch4,ch5,ch6,ch7,ch8," + "imu_ok,imu_gyro_z,imu_roll,imu_pitch,imu_yaw,imu_yaw_rel," "cmd_fl,cmd_fr,cmd_rl,cmd_rr," "fb_fl,fb_fr,fb_rl,fb_rr," "amp_fl,amp_fr,amp_rl,amp_rr," + "temp_fl,temp_fr,temp_rl,temp_rr,temp_drv_front,temp_drv_rear," "stat_fl,stat_fr,stat_rl,stat_rr," "err_fl,err_fr,err_rl,err_rr," + "tick_fl,tick_fr,tick_rl,tick_rr," + "wheel_omega,imu_omega,omega_residual,fused_yaw,odom_x,odom_y," + "heading_hold,heading_target,heading_trim,whole_slip," "dist_axle,comm_ms\n"; std::cout << " - CSV 로그 기록: " << log_path << "\n"; } else { @@ -1174,6 +1544,11 @@ int main(int argc, char **argv) { "동시에 읽으면 RC 데이터 파싱이 깨질 수 있으니 서로 다른 " "포트로 지정하세요.\n\n"; } + if (use_imu && (imu_cfg.port == port1 || imu_cfg.port == rc_cfg.port)) { + std::cout << "\n[경고] --imu_port(" << imu_cfg.port + << ")가 모터 또는 RC 수신기 포트와 동일합니다. 서로 다른 " + "포트로 지정하세요.\n\n"; + } RcReceiver rc(rc_cfg); rc.start(); @@ -1181,6 +1556,14 @@ int main(int argc, char **argv) { << rc_cfg.port << "). 신호 수신 전까지는 안전을 위해 브레이크가 " "걸린 상태로 대기합니다.\n"; + ImuReader imu(imu_cfg); + if (use_imu) { + imu.start(); + std::cout << "[정보] IMU(HWT905-RS232) 리더 시작 (" << imu_cfg.port << " @" + << imu_cfg.baud << "bps). Phase 1: 관측 전용, 제어에는 개입하지 " + "않습니다.\n"; + } + SerialPort sp1, sp2; if (!sp1.openPort(port1)) return 1; @@ -1268,7 +1651,16 @@ int main(int argc, char **argv) { return target_vel; } - bool is_accelerating = std::abs(target_vel) > std::abs(current_vel); + // 같은 방향으로 더 빨라지는 경우만 즉시 스냅한다. 부호가 바뀌는 역전은 + // (현재속도가 0이 아닌데 목표가 반대부호로 바뀌는 경우) 저크 제한 감속 + // 경로로 보낸다 — 정지 직전 저속 구간에서 RC/조향 노이즈만으로 목표가 + // 순간 반대부호로 흔들려도 그대로 즉시 스냅되면 "전진→후진→전진"처럼 + // 튀는 현상이 생기고, 실제 반전 요청이라도 0을 관통하는 순간 스냅은 + // 역기전력 스파이크가 오히려 일반 가속보다 더 크다. + bool same_direction = + (current_vel == 0.0f) || (target_vel * current_vel > 0.0f); + bool is_accelerating = + same_direction && (std::abs(target_vel) > std::abs(current_vel)); if (is_accelerating) { current_accel = 0.0f; return target_vel; @@ -1307,6 +1699,14 @@ int main(int argc, char **argv) { bool just_landed_rl = false, just_landed_rr = false; char wheel_status_str[5] = "OOOO"; bool fault_active = false; // 드라이버 알람(과전류/과부하 등) 발생 여부 + // 드라이버 온도(0x20B0)는 열 시정수가 초 단위로 느려 매 틱 통신할 + // 필요가 없다 — 30틱(실측 ~27Hz 기준 약 1.1초)마다만 갱신. + float front_driver_temp_c = 0.0f, rear_driver_temp_c = 0.0f; + int driver_temp_poll_counter = 0; + constexpr int kDriverTempPollTicks = 30; + // 하드웨어 스펙(doc/01) 동작온도 상한 50°C, 드라이버 자체 과열 보호 + // 임계값 기본 80°C 대비 여유를 두고 조기 경보를 띄우는 기준. + constexpr int kOverheatWarnC = 60; bool prev_brake_on = false; // 브레이크(CH5/Fail-safe) 직전 틱 상태 bool prev_rc_ok = false; // RC 연결 직전 틱 상태 (Fail-safe 로그용) float omega_max = (effective_w > 0.0f) ? (max_spin_v / (effective_w / 2.0f)) @@ -1329,6 +1729,40 @@ int main(int argc, char **argv) { bool steer_initialized = false, throttle_initialized = false; int steer_reject_streak = 0, throttle_reject_streak = 0; + // IMU Phase 1: 순수 관측용 표시 오프셋. 로봇 전원이 켜질 때(또는 이 + // 프로그램이 시작될 때) IMU가 처음 보고하는 절대 yaw를 "정면(0°)" + // 기준으로 저장해, 화면에는 그 시점 대비 상대 yaw를 보여준다. 이건 + // 순수 표시용 계산일 뿐 제어 로직에는 전혀 관여하지 않는다 — 헤딩을 + // 실제로 잠그거나 보정하는 건 Phase 3의 몫이다. + double imu_yaw_offset = 0.0; + bool imu_yaw_offset_set = false; + + // Phase 2 — 헤딩 적분 + x,y 오도메트리 (여전히 순수 관측, 제어 미개입). + // 처음엔 IMU 내장 AHRS가 계산해 주는 imu_yaw_rel을 θ로 그대로 썼으나, + // 실측 루프백(한 바퀴 돌아 제자리 복귀) 주행 로그로 검증한 결과 그 값이 + // 같은 장치의 원시 자이로(gyro_z)와도 1초 평균 구간 기준 부호 일치율이 + // 72%에 불과함이 드러났다(회전 중 모터 전류로 인한 지자기 간섭 등으로 + // 추정 — 저가 AHRS의 흔한 증상). 실제로 그 루프백 로그에서 odom이 시작 + // 위치로 전혀 돌아오지 못했다. 반면 바퀴 인코더 기반 wheel_omega와 원시 + // gyro_z는 부호 일치율 99.3%로 서로 강하게 신뢰할 수 있음을 같은 로그에서 + // 확인했다. 그래서 θ는 IMU 융합 yaw 대신 wheel_omega와 원시 gyro_z의 + // 평균을 자체 적분한 fused_yaw_deg를 쓴다(IMU 미연결 시엔 wheel_omega만 + // 사용). IMU 융합 yaw(imu_yaw_rel)는 비교용으로 HUD/CSV에 계속 남긴다. + double odom_x = 0.0, odom_y = 0.0; // m, Phase 1 오프셋 설정 시점 기준 + double fused_yaw_deg = 0.0; // deg, wheel_omega+gyro_z 자체 적분 헤딩 + auto last_odom_time = std::chrono::steady_clock::now(); + + // Phase 3 — 헤딩 홀드 PI 상태. 적분 windup 방지용 상한은 트림 상한을 + // Ki로 나눠 "적분항 단독으로도 트림 상한을 넘지 않는" 값으로 고정한다. + bool heading_hold_active_prev = false; + double heading_target_deg = 0.0; + double heading_error_integral = 0.0; + const float heading_integral_limit = + (heading_ki > 1e-6f) ? (heading_max_trim / heading_ki) : 0.0f; + + // Phase 4 — 전신 슬립 디바운스 카운터 + int whole_slip_count = 0; + while (g_running) { // --------------------------------------------------------------------- // RC 수신기 채널 읽기 (doc/joystick.md §2, §3 기능 명세서 기준) @@ -1388,6 +1822,25 @@ int main(int argc, char **argv) { raw_ch[ch] = 0; // 아직 수신 못한 채널은 0으로 표시 } + // IMU Phase 1: 순수 관측. 제어 로직에는 관여하지 않는다. + ImuReader::Snapshot imu_snap = use_imu ? imu.getSnapshot() : ImuReader::Snapshot{}; + if (imu_snap.valid && !imu_yaw_offset_set) { + imu_yaw_offset = imu_snap.angle[2]; + imu_yaw_offset_set = true; + std::cout << "\n[정보] IMU 정면 기준 설정: 시작 시점 yaw " << imu_yaw_offset + << "도를 0도(정면)로 저장했습니다.\n"; + } + double imu_yaw_rel = 0.0; + if (imu_yaw_offset_set) { + imu_yaw_rel = imu_snap.angle[2] - imu_yaw_offset; + // [-180, 180) 범위로 정규화 + while (imu_yaw_rel > 180.0) imu_yaw_rel -= 360.0; + while (imu_yaw_rel < -180.0) imu_yaw_rel += 360.0; + } + // 바퀴 피드백과 무관한 IMU 원시 요레이트. Phase 2(휠-IMU 융합 헤딩)와 + // Phase 4(지령-IMU 전신 슬립 감지, 명령 결정 이전에 필요)가 공유한다. + float imu_omega_rad = imu_snap.gyro[2] * (PI_VAL / 180.0f); // rad/s + if (!rc_ok && prev_rc_ok) { std::cout << "\n[경고] RC 신호 Fail-safe 발동 (" << rc_cfg.failsafe_timeout_ms << "ms 이상 미수신) -> 비상 정지.\n"; @@ -1404,6 +1857,7 @@ int main(int argc, char **argv) { float v_x = 0.0f; // 선속도 m/s float omega = 0.0f; // 각속도 rad/s + bool user_steer_centered = false; // Phase 3 헤딩 홀드 진입 조건용 if (!brake_on) { // 3.3 전후진 속도 계산 (CH2) — 실측 결과 raw 값이 높을수록 전진(+), @@ -1438,6 +1892,7 @@ int main(int argc, char **argv) { static_cast(rc_cfg.steer_max - rc_cfg.steer_center_hi)) * omega_max; } + user_steer_centered = (omega == 0.0f); } // --------------------------------------------------------------------- @@ -1445,11 +1900,12 @@ int main(int argc, char **argv) { // (요청에 따라 후진 시 측면 회피는 미적용 - 전진 시에만 좌우 회피 동작) // --------------------------------------------------------------------- std::string lidar_telemetry = "LIDAR:OFF"; + float avoid_omega_offset = 0.0f; // Phase 3 헤딩 홀드가 라이다 회피와 + // 충돌하지 않도록 블록 밖에서도 확인 if (use_lidar) { ObstacleStatus obs = lidar_detector.getStatus(); if (obs.connected) { float speed_scale = 1.0f; - float avoid_omega_offset = 0.0f; // 1. Forward / Backward Automatic Stopping if (v_x > 0.01f) { @@ -1509,6 +1965,68 @@ int main(int argc, char **argv) { } } + // --------------------------------------------------------------------- + // Phase 3 — 직진 헤딩 홀드 (doc/06-imu-integration-plan.md §Phase 3). + // 조향 중립 + 라이다 회피 미개입 + 실제 주행 중일 때만 개입한다. 진입 + // 순간의 fused_yaw_deg를 목표 헤딩으로 고정(lock)하고, 이후 그로부터 + // 벗어난 만큼(오차)을 작은 PI로 보정해 omega에 트림을 더한다. 사용자 + // 조향/정지/라이다 회피가 개입하는 즉시 해제되고 적분항도 리셋된다. + // Phase 2 검증(실측 루프백 로그)에서 wheel_omega/gyro_z 부호 일치율이 + // 99.3%로 확인된 fused_yaw_deg를 피드백으로 쓴다 — IMU 단독 융합 yaw는 + // 회전 중 신뢰도가 낮아 제어 피드백에서 제외했다(imu_yaw_rel은 HUD/CSV + // 비교용으로만 남김). + bool heading_hold_condition = heading_hold_enabled && !brake_on && + user_steer_centered && + avoid_omega_offset == 0.0f && v_x != 0.0f; + float heading_trim_omega = 0.0f; // HUD/CSV 노출용 + if (heading_hold_condition) { + if (!heading_hold_active_prev) { + heading_target_deg = fused_yaw_deg; + heading_error_integral = 0.0; + } + double heading_error_deg = heading_target_deg - fused_yaw_deg; + while (heading_error_deg > 180.0) heading_error_deg -= 360.0; + while (heading_error_deg < -180.0) heading_error_deg += 360.0; + double heading_error_rad = heading_error_deg * (PI_VAL / 180.0); + + heading_error_integral += heading_error_rad * dt; + heading_error_integral = std::clamp( + heading_error_integral, + static_cast(-heading_integral_limit), + static_cast(heading_integral_limit)); + + heading_trim_omega = static_cast( + heading_kp * heading_error_rad + heading_ki * heading_error_integral); + heading_trim_omega = + std::clamp(heading_trim_omega, -heading_max_trim, heading_max_trim); + omega += heading_trim_omega; + } else { + heading_error_integral = 0.0; + } + heading_hold_active_prev = heading_hold_condition; + + // --------------------------------------------------------------------- + // Phase 4 — IMU 기반 전신(whole-body) 슬립 감지 (doc §Phase 4). 지령 + // omega(라이다 회피/헤딩 홀드 트림까지 반영된 최종값) 대비 바퀴 피드백과 + // 완전히 독립적인 IMU 원시 gyro_z의 비율이 문턱값 밑으로 + // whole_slip_debounce_ticks 틱 이상 지속되면 "전신 슬립"으로 확정하고, + // 라이다 회피의 speed_scale과 같은 패턴으로 v_x/omega를 일시적으로 + // 낮춘다. 문턱값을 낮게(0.15) 잡아 제자리 회전의 정상적인 스크럽 손실 + // (지령 대비 40~60% 미달도 흔함)과 혼동하지 않고 사실상 전혀 못 도는 + // 극단적 슬립만 잡아낸다. + bool whole_slip_now = false; + if (whole_slip_enabled && imu_snap.valid && + std::abs(omega) > whole_slip_min_omega) { + float slip_ratio = std::abs(imu_omega_rad) / std::abs(omega); + whole_slip_now = slip_ratio < whole_slip_ratio_threshold; + } + whole_slip_count = whole_slip_now ? whole_slip_count + 1 : 0; + bool whole_slip_active = whole_slip_count >= whole_slip_debounce_ticks; + if (whole_slip_active) { + v_x *= whole_slip_speed_scale; + omega *= whole_slip_speed_scale; + } + // 제자리 회전 여부: CH2(전후진) 입력이 완전히 중립(v_x == 0.0f)이면서 // CH1(조향) 입력이 있을 때만 진입한다. v_x는 데드존 처리 시 정확히 // 0.0f로 설정되므로 등호 비교가 안전하다. @@ -1541,14 +2059,22 @@ int main(int argc, char **argv) { // 직전 틱 피드백에서 무부하(들뜸)로 판정된 바퀴는 목표 속도를 0으로 낮춰 // 헛돌이를 억제한다. 접지력이 회복되어 전류가 정상으로 돌아오면 다음 // 판정 틱에서 자동으로 해제되어 원래 지령으로 복귀한다. - if (airborne_fl) - target_fl = 0.0f; - if (airborne_fr) - target_fr = 0.0f; - if (airborne_rl) - target_rl = 0.0f; - if (airborne_rr) - target_rr = 0.0f; + // 단, 제자리 회전 중에는 반력 토크로 인한 대각선 하중 이동(FL+RR 또는 + // FR+RL 쌍이 동시에 가벼워짐)이 정상적인 현상인데 이걸 "들뜸"으로 오判定해 + // 목표 속도를 꺼버리면 4륜 중 사실상 2륜만 구동되어 회전력이 반토막 + // 난다(실측 로그에서 전체 회전 틱의 38%가 대각선 쌍 동시 들뜸 판정이었고, + // 그 결과 실제 회전율이 지령 대비 44~58%에 그쳤음). 그래서 제자리 회전 + // 중에는 판정(HUD 표시용)은 유지하되 속도를 꺾는 개입만 끈다. + if (!is_spin_turn) { + if (airborne_fl) + target_fl = 0.0f; + if (airborne_fr) + target_fr = 0.0f; + if (airborne_rl) + target_rl = 0.0f; + if (airborne_rr) + target_rr = 0.0f; + } // 3.1 브레이크 최우선 로직 (CH5/Fail-safe): 저크 램프를 건너뛰고 즉시 // 0으로 스냅 후 브레이크를 잠근다. 해제되면 STOPPED 상태에서 아래 @@ -1647,12 +2173,21 @@ int main(int argc, char **argv) { float fl_amp = 0, fr_amp = 0, rl_amp = 0, rr_amp = 0; int32_t fl_tick = 0, fr_tick = 0, rl_tick = 0, rr_tick = 0; uint16_t err_f_l = 0, err_f_r = 0, err_r_l = 0, err_r_r = 0; + int fl_temp = 0, fr_temp = 0, rl_temp = 0, rr_temp = 0; driver_front.readFeedback(fl_fb, fr_fb, fl_tick, fr_tick, fl_amp, fr_amp, - err_f_l, err_f_r); + err_f_l, err_f_r, fl_temp, fr_temp); if (driver_rear_ptr) driver_rear_ptr->readFeedback(rl_fb, rr_fb, rl_tick, rr_tick, rl_amp, - rr_amp, err_r_l, err_r_r); + rr_amp, err_r_l, err_r_r, rl_temp, rr_temp); + + // 드라이버(기판) 온도는 저빈도로만 갱신(위 kDriverTempPollTicks 주석 참고). + if (++driver_temp_poll_counter >= kDriverTempPollTicks) { + driver_temp_poll_counter = 0; + driver_front.readDriverTemp(front_driver_temp_c); + if (driver_rear_ptr) + driver_rear_ptr->readDriverTemp(rear_driver_temp_c); + } // 드라이버 알람(과전류/과부하 등) 감지: 새로 발생한 알람만 콘솔에 // 한 번 크게 출력한다 (매 틱 갱신되는 HUD 줄과 별개의 고정 줄). @@ -1776,6 +2311,40 @@ int main(int argc, char **argv) { float dist_bumper = dist_axle + (dist_axle > 0.001f ? bumper_offset : 0.0f); (void)dist_bumper; + // Phase 2 — 휠 기반 ω(엔코더 피드백) vs IMU 기반 ω(자이로) 비교 + + // x,y 오도메트리 적분. 실측 검증(실기 로그) 결과 부호 규약이 일치함: + // 우회전(+omega)일 때 wheel_omega/imu_gyro_z 둘 다 양수로 나온다. + // 이 둘의 차이(잔차)가 §6.3-4의 슬립 지표다. 여전히 제어에는 관여하지 + // 않는다 — HUD/CSV 노출까지만. + auto odom_now = std::chrono::steady_clock::now(); + float odom_dt = std::chrono::duration(odom_now - last_odom_time).count(); + last_odom_time = odom_now; + + float v_l_meas = ((fl_fb + rl_fb) / 2.0f) / rpm_per_ms; // m/s + float v_r_meas = -((fr_fb + rr_fb) / 2.0f) / rpm_per_ms; // m/s (부호 규약: fr/rr는 -v_r로 지령됨) + float v_x_meas = (v_l_meas + v_r_meas) / 2.0f; + float wheel_omega = (k_skid > 0.0f) + ? (v_r_meas - v_l_meas) / (2.0f * k_skid) + : 0.0f; // rad/s + float omega_residual = wheel_omega - imu_omega_rad; + + // θ 적분원: wheel_omega와 원시 gyro_z의 평균(둘의 부호/크기 일치율이 + // 실측상 99.3%로 높음). IMU가 끊겼을 땐 wheel_omega만으로 대체한다. + float fused_omega_rad = + imu_snap.valid ? (wheel_omega + imu_omega_rad) / 2.0f : wheel_omega; + + if (imu_yaw_offset_set && odom_dt > 0.0f && odom_dt < 0.5f) { + // odom_dt>=0.5s: Fail-safe 재연결 등으로 틱이 크게 벌어진 비정상 + // 구간은 위치 적분에서 제외해 순간 도약을 막는다. + fused_yaw_deg += fused_omega_rad * (180.0 / PI_VAL) * odom_dt; + while (fused_yaw_deg > 180.0) fused_yaw_deg -= 360.0; + while (fused_yaw_deg < -180.0) fused_yaw_deg += 360.0; + + double theta_rad = fused_yaw_deg * (PI_VAL / 180.0); + odom_x += v_x_meas * odom_dt * std::cos(theta_rad); + odom_y += v_x_meas * odom_dt * std::sin(theta_rad); + } + if (log_stream.is_open()) { float t_ms = std::chrono::duration( std::chrono::steady_clock::now() - log_start_time) @@ -1786,13 +2355,26 @@ int main(int argc, char **argv) { << omega << ',' << raw_ch[1] << ',' << raw_ch[2] << ',' << raw_ch[3] << ',' << raw_ch[4] << ',' << raw_ch[5] << ',' << raw_ch[6] << ',' << raw_ch[7] << ',' << raw_ch[8] << ',' + << (imu_snap.valid ? 1 : 0) << ',' << imu_snap.gyro[2] << ',' + << imu_snap.angle[0] << ',' << imu_snap.angle[1] << ',' + << imu_snap.angle[2] << ',' << imu_yaw_rel << ',' << cmd_fl << ',' << cmd_fr << ',' << cmd_rl << ',' << cmd_rr << ',' << fl_fb << ',' << fr_fb << ',' << rl_fb << ',' << rr_fb << ',' << fl_amp << ',' << fr_amp - << ',' << rl_amp << ',' << rr_amp << ',' << wheel_status_str[0] + << ',' << rl_amp << ',' << rr_amp + << ',' << fl_temp << ',' << fr_temp << ',' << rl_temp << ',' + << rr_temp << ',' << front_driver_temp_c << ',' + << rear_driver_temp_c << ',' << wheel_status_str[0] << ',' << wheel_status_str[1] << ',' << wheel_status_str[2] << ',' << wheel_status_str[3] << ',' << err_f_l << ',' << err_f_r << ',' << err_r_l << ',' << err_r_r << ',' + << fl_tick << ',' << fr_tick << ',' << rl_tick << ',' << rr_tick + << ',' << wheel_omega << ',' << imu_omega_rad << ',' + << omega_residual << ',' << fused_yaw_deg << ',' + << odom_x << ',' << odom_y << ',' + << (heading_hold_condition ? 1 : 0) << ',' << heading_target_deg + << ',' << heading_trim_omega << ',' + << (whole_slip_active ? 1 : 0) << ',' << dist_axle << ',' << comm_ms << '\n'; } @@ -1803,15 +2385,30 @@ int main(int argc, char **argv) { (v_max_now <= rc_cfg.v_max_low + 0.001f) ? "LOW " : "HIGH"; printf("\r\033[K[%-8s] RC:%s%s " "C1:%4d C2:%4d C3:%4d C4:%4d C5:%4d C6:%4d C7:%4d C8:%4d " - "SPD:%s VX:%+4.2f OMG:%+4.2f | AXLE:%5.3fm | %-28s | " + "SPD:%s VX:%+4.2f OMG:%+4.2f | IMU:%s GZ:%+6.2f YAW(imu):%+6.1f | " + "WO:%+5.2f IO:%+5.2f YAW(fus):%+6.1f ODO:(%+5.2f,%+5.2f) | " + "HH:%s TRG:%+6.1f TRM:%+5.2f%s | " + "AXLE:%5.3fm | %-28s | " "I(A):FL%+4.1f FR%+4.1f RL%+4.1f RR%+4.1f | " + "T(C):FL%3d FR%3d RL%3d RR%3d DRV%4.1f/%4.1f%s | " "WHL(FL/FR/RL/RR):%s%s | %4.1fms", state.c_str(), rc_ok ? "OK" : "LOST", brake_on ? "[BRK]" : " ", raw_ch[1], raw_ch[2], raw_ch[3], raw_ch[4], raw_ch[5], raw_ch[6], raw_ch[7], raw_ch[8], speed_mode_str, v_x, omega, + imu_snap.valid ? "OK " : "LOST", imu_snap.gyro[2], imu_yaw_rel, + wheel_omega, imu_omega_rad, fused_yaw_deg, odom_x, odom_y, + heading_hold_condition ? "ON " : "off", heading_target_deg, + heading_trim_omega, whole_slip_active ? " [!!전신슬립!!]" : "", dist_axle, lidar_telemetry.c_str(), fl_amp, fr_amp, - rl_amp, rr_amp, wheel_status_str, - fault_active ? " [!!알람!!]" : "", comm_ms); + rl_amp, rr_amp, fl_temp, fr_temp, rl_temp, rr_temp, + front_driver_temp_c, rear_driver_temp_c, + (fl_temp >= kOverheatWarnC || fr_temp >= kOverheatWarnC || + rl_temp >= kOverheatWarnC || rr_temp >= kOverheatWarnC || + front_driver_temp_c >= kOverheatWarnC || + rear_driver_temp_c >= kOverheatWarnC) + ? " [!!고온!!]" + : "", + wheel_status_str, fault_active ? " [!!알람!!]" : "", comm_ms); fflush(stdout); std::this_thread::sleep_for( @@ -1837,6 +2434,8 @@ int main(int argc, char **argv) { } rc.stop(); + if (use_imu) + imu.stop(); std::cout << "[정보] C++ 4WD 제어 프로그램이 성공적으로 종료되었습니다.\n"; return 0; diff --git a/xbox_motor_control_cpp b/xbox_motor_control_cpp index 1d5e7e7b928bb898a714d5ef892e58d751fa0b5d..d06e5122fb48c59f9d77a97cda40002dcc70b6d9 100755 GIT binary patch literal 113624 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
N5=-ayrbbp0@F$`T7DbQyr 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 ay`V?QUqN9T=#c4a7xS3eVF|*5(*FUCYB(eS