# CLAUDE.md

## 项目概述

这是一个 ROS 2 Humble 工作空间，用于 Ackermann 转向小车的 Gazebo 仿真、SLAM 建图和 Nav2 导航。同时支持实车部署（无 Gazebo）。

## 工作空间结构

```
test_ws/
├── src/
│   ├── gc_navigation2_slamtoolbox/   # 仿真版：导航+SLAM 启动文件和配置
│   │   ├── launch/                   # Python launch 文件
│   │   ├── config/                   # slam_toolbox 参数
│   │   ├── params/                   # Nav2 参数
│   │   ├── maps/                     # 地图文件 (.pgm, .yaml, .posegraph)
│   │   └── world/                    # Gazebo 模型 (zhihui/)
│   ├── gc_navigation2_real/          # 实车版：导航+SLAM 启动文件和配置（无 Gazebo）
│   │   ├── launch/                   # Python launch 文件
│   │   ├── config/                   # slam_toolbox 参数（实车调优）
│   │   ├── params/                   # Nav2 参数（use_sim_time=false）
│   │   ├── maps/                     # 实车地图存放目录
│   │   └── behavior_tree/            # 阿克曼自定义行为树
│   ├── origincar_description/        # 机器人 URDF 和 Gazebo world
│   │   ├── urdf/                     # origincar.urdf
│   │   ├── world/                    # .world 文件
│   │   ├── meshes/                   # 3D 模型
│   │   ├── launch/                   # display.launch.py
│   │   └── rviz/                     # RViz 配置
│   ├── origincar_base/               # 实车底盘驱动（串口通信+EKF+IMU融合）
│   │   ├── src/                      # C++ 源码（origincar_base.cpp）
│   │   ├── scripts/                  # cmd_vel_to_ackermann_drive.py
│   │   ├── launch/                   # origincar_bringup / base_serial / ekf
│   │   └── config/                   # ekf.yaml / imu.yaml
│   ├── origincar_msg/                # 自定义 ROS 2 消息
│   │   └── msg/                      # Data.msg / Sign.msg
│   └── LSLIDAR_X_ROS2-20240228/      # 镭神激光雷达 ROS 2 驱动
│       └── src/
│           ├── lslidar_driver/       # 雷达驱动核心
│           └── lslidar_msgs/         # 雷达消息定义
├── build/                            # colcon build 输出
├── install/                          # colcon install 输出 (含符号链接)
└── log/                              # 构建日志
```

## 构建命令

```bash
cd /home/guoch/test_ws
source /opt/ros/humble/setup.bash
colcon build --symlink-install
source install/setup.bash
```

## 常用 Launch 文件

### 仿真版 (gc_navigation2_slamtoolbox)

| 文件 | 功能 |
|------|------|
| `gc_slam_mapping.launch.py` | Gazebo + SLAM 建图模式 |
| `gc_nav2_with_slam_online.launch.py` | 已知地图 + SLAM 在线更新 + Nav2 导航 |
| `gc_nav2_with_slam.launch.py` | 已知地图 + SLAM 定位 + Nav2 导航 |
| `gc_nav2_with_amcl.launch.py` | AMCL 定位 + Nav2 导航 |

### 实车版 (gc_navigation2_real)

| 文件 | 功能 |
|------|------|
| `real_bringup.launch.py` | 基础 bringup（底盘驱动+雷达+TF，无导航） |
| `real_slam_mapping.launch.py` | 实车 SLAM 建图模式 |
| `real_nav2_slam.launch.py` | 已知地图 + SLAM 定位 + Nav2 导航 |
| `real_nav2_slam_online.launch.py` | 已知地图 + SLAM 在线更新 + Nav2 导航 |

## 机器人参数 (origincar)

- **类型**: Ackermann 转向小车
- **尺寸**: 0.276 × 0.214 × 0.211 m (长×宽×高)
- **轴距**: 0.143 m（URDF 中前轮 x=0.0715，后轮 x=-0.0715），**轮距**: 0.189 m
- **轮子**: 半径 0.03 m，厚 0.025 m
- **激光雷达**: 360°, 0.12~3.5 m, 5 Hz（仿真）; 镭神 N10 0.15~12m（实车）
- **IMU**: 100 Hz, 含高斯噪声
- **Gazebo 插件**: `gazebo_ros_ackermann_drive`, `gazebo_ros_ray_sensor`, `gazebo_ros_imu_sensor`

## 实车 TF 树

```
map ──→ odom_combined ──→ base_footprint ──→ base_link ──→ laser
 ↑          ↑                ↑                  ↑
slam_toolbox  EKF            static TF          URDF/static
(定位)      (融合odom+IMU)   (origincar_bringup)
```

- slam_toolbox 发布 `map → odom_combined`
- EKF 发布 `odom_combined → base_footprint`
- origincar_bringup 发布 `base_footprint → base_link`（z=0）、`base_link → laser`
- robot_state_publisher 发布 URDF 各连杆 TF

### 仿真 vs 实车关键差异

| 方面 | 仿真版 | 实车版 |
|------|--------|--------|
| use_sim_time | `true` | `false` |
| odom_frame | `odom` | `odom_combined`（EKF 融合后） |
| 底盘驱动 | Gazebo plugin | origincar_base 串口驱动 |
| 激光雷达 | Gazebo ray plugin | lslidar_driver（镭神 N10） |
| robot_state_publisher | launch 文件内启动 | origincar_bringup 已包含，勿重复启动 |
| joint_state_publisher | launch 文件内启动 | origincar_bringup 已包含，勿重复启动 |

## 阿克曼底盘模式

通过 `akmcar` 参数控制，链路如下：

```
origincar_bringup.launch.py
  └─ akmcar=LaunchConfiguration('akmcar', default='true')
       └─ 传入 base_serial.launch.py
            ├─ IfCondition(true)  → origincar_base_node (akm_cmd_vel='ackermann_cmd')
            │                     + cmd_vel_to_ackermann_drive.py (Twist→Ackermann)
            └─ UnlessCondition(false) → 差速模式（跳过）
```

数据流：`MPPI cmd_vel(Twist) → cmd_vel_to_ackermann_drive.py → ackermann_cmd(AckermannDriveStamped) → STM32`

关键参数：
- `cmd_vel_to_ackermann_drive.py`: `wheelbase = 0.143`（与 URDF 一致）
- `akmcar=true` 时 STM32 接收 `speed + steering_angle`
- `akmcar=false` 时 STM32 接收 `vx + vy + wz`（固件自行转换）

## 关键设计

1. **map_server** 提供全量静态底图（`/map`）
2. **slam_toolbox** 负责定位和增量建图，重映射 `/map` → `/slam_map` 避免冲突
3. **Nav2** 使用 `global_costmap/static_layer ← /map` + `local_costmap/obstacle_layer ← /scan`
4. 保存地图: `ros2 service call /slam_toolbox/serialize_map ...`
5. 实车版 launch 文件**不应**自行启动 `robot_state_publisher` 和 `joint_state_publisher`，`origincar_bringup` 已包含

## 地图文件

地图放在 `src/gc_navigation2_slamtoolbox/maps/`（仿真）和 `src/gc_navigation2_real/maps/`（实车），包含:
- `xxx.pgm` — 地图图像
- `xxx.yaml` — 地图元数据 (resolution, origin, thresholds)
- `xxx.posegraph` — 序列化的位姿图

## Gazebo World

World 文件在 `src/origincar_description/world/`:
- `zhihui.world` — 智慧楼墙体环境（内联模型）
- `test.world` — 测试环境
- `fishbot.world` / `gc_world.world` — 其他场景

## 实车底盘驱动包 (origincar_base)

- `origincar_base_node`（C++）: 串口读写（/dev/ttyACM0, 115200bps）、航迹推算、四元数姿态解算（Mahony AHRS）
- `cmd_vel_to_ackermann_drive.py`（Python）: Twist → AckermannDriveStamped 转换，wheelbase=0.143
- 帧协议: 24 字节收（帧头0x7B/帧尾0x7D）/ 11 字节发
- EKF 融合: `/odom` + IMU → `/odom_combined`，`two_d_mode=true`

## 注意事项

- 使用 `--symlink-install` 构建，Python launch 文件修改后无需重新编译
- `.world` 文件不应使用 `model://` 外部引用，应将模型内联定义
- `robot_base_frame` 在 ackermann plugin 中应设为 `base_footprint`
- 实车 `gc_navigation2_real` 的 launch 文件调用 `origincar_bringup.launch.py` 时，不再重复启动 `robot_state_publisher` 和 `joint_state_publisher`
- 轴距参数在三处需保持一致：URDF（0.143m）、`cmd_vel_to_ackermann_drive.py`（0.143m）、Nav2 planner `minimum_turning_radius`（0.40m）
