# CRAIC — Camera-Robot AI Control System

> 本仓库为 **[中国机器人及人工智能大赛](https://www.caairobot.com)**（CRAIC）**机器人任务挑战赛（小型桌面级）** 参赛代码。

ESP32-S3 双核摄像头 + ROS 2 机械臂控制 + 视觉抓取的一体化机器人系统。

## 赛事信息

**中国机器人及人工智能大赛**（**C**hina **R**obot and **A**rtificial **I**ntelligence **C**ompetition，简称 **CRAIC**）是由 [中国人工智能学会](https://www.caai.cn)（CAAI）主办的全国性学科竞赛，始于 1999 年，已列入**全国普通高等学校学科竞赛排行榜**及**教育部 A 类竞赛名单**。

本仓库参加赛项：**机器人任务挑战赛（小型桌面级）** — Robot Task Challenge (Small Desktop Level)

| 项目 | 信息 |
|------|------|
| 大赛名称 | 中国机器人及人工智能大赛 |
| 英文名称 | China Robot and Artificial Intelligence Competition (CRAIC) |
| 主办单位 | 中国人工智能学会 (CAAI) |
| 赛事级别 | 全国普通高等学校学科竞赛排行榜 / 教育部 A 类 |
| 参赛赛项 | 机器人任务挑战赛（小型桌面级） |
| 官方网站 | [www.caairobot.com](https://www.caairobot.com) |

## 项目结构

```
craic/
├── jxbeye/                    # ESP32-S3 固件 (PlatformIO)
│   ├── src/main.cpp           # 双核摄像头推流 + UDP 控制
│   └── platformio.ini         # ESP32-S3-WROOM-1-N16R8 配置
├── ros2/                      # ROS 2 控制系统
│   ├── src/arm_control_msgs/  # 消息和服务定义
│   └── src/udp_teleop/        # 机械臂控制和视觉抓取节点
├── tools/                     # 独立工具脚本
│   ├── udp_control.py         # 命令行机械臂控制（带逆运动学）
│   ├── camera_to_base.py      # 相机坐标到基坐标变换
│   └── camera_capture.py      # MJPEG 流采集
└── docs/                      # 技术文档
    └── arm.md                 # 机械臂运动学推导
```

## 硬件

| 组件 | 型号 |
|------|------|
| 主控 | ESP32-S3-WROOM-1-N16R8 (16MB Flash, 8MB PSRAM) |
| 摄像头 | OV2640 (XGA 1024×768, JPEG) |
| 机械臂 | 6-DOF，飞特 SCS/STS 串行舵机 |

## 核心功能

### 1. ESP32-S3 固件 (`jxbeye/`)

- **双核架构**：Core 0 采集，Core 1 推流
- **MJPEG 推流**：`http://<IP>` 实时查看
- **UDP 控制**：端口 8888，非阻塞异步处理
- **WiFi 配置**：串口发送 `WIFI:SSID:PASSWORD` 配置

### 2. ROS 2 机械臂控制 (`ros2/`)

**arm_control 节点**：
- 关节空间和笛卡尔空间运动控制
- 完整逆运动学和正运动学
- 服务接口：`move_joints`, `move_pose`, `get_pose`, `set_gripper`
- 状态发布（10Hz）

**vision_grasp 节点**：
- 相机坐标到基坐标系自动转换
- 自动抓取：释放 → 移动 → 抓取 → 回收
- 自动释放：移动 → 释放 → 回收

### 3. 工具脚本 (`tools/`)

- `udp_control.py` - 命令行机械臂控制（支持逆运动学）
- `camera_to_base.py` - 坐标变换工具
- `camera_capture.py` - 相机帧采集（支持自动扫描）

## 快速开始

### ESP32 固件

```bash
cd jxbeye
pio run -t upload
pio device monitor
```

首次启动创建热点 `ESP32-S3-Camera`（密码 `12345678`），访问 `http://192.168.4.1`。

### ROS 2 控制系统

```bash
# 环境准备（首次）
conda create -n ros2_humble -c robostack-staging -c conda-forge ros-humble-desktop
conda activate ros2_humble

# 编译
cd ros2
export PYTHON_EXECUTABLE=$CONDA_PREFIX/bin/python
export PYTHON_INCLUDE_DIR=$CONDA_PREFIX/include/python3.12
export PYTHON_LIBRARY=$CONDA_PREFIX/lib/libpython3.12.so

colcon build --packages-select arm_control_msgs \
  --cmake-args \
    -DPython_EXECUTABLE=$PYTHON_EXECUTABLE \
    -DPython_INCLUDE_DIR=$PYTHON_INCLUDE_DIR \
    -DPython_LIBRARY=$PYTHON_LIBRARY

colcon build --packages-select udp_teleop
source install/setup.bash

# 运行机械臂控制
ros2 run udp_teleop arm_control \
    --ros-args --params-file src/udp_teleop/config/arm_control.yaml

# 运行视觉抓取（新终端）
ros2 run udp_teleop vision_grasp \
    --ros-args --params-file src/udp_teleop/config/vision_grasp.yaml
```

### 命令行工具

```bash
# 机械臂控制（关节空间）
python tools/udp_control.py joints \
    --height -100 --j2 10 --j3 20 --j4 30 --duration 2.0

# 机械臂控制（笛卡尔空间）
python tools/udp_control.py pose \
    --x 200 --y 100 --z -100 --phi 45 --duration 2.0

# 相机采集
python tools/camera_capture.py --ip 192.168.4.1
python tools/camera_capture.py --scan  # 自动扫描
```

## UDP 协议

所有指令通过 UDP 端口 `8888` 发送：

```bash
# 底盘控制（麦克纳姆轮）
XYW:<X>:<Y>:<W>:XZHY\n

# 机械臂控制（6 轴）
JXB:<height>:<J2>:<J3>:<J4>:<J5>:<J6>:0:0:EZHY\n

# 激光控制
LASERON / LASEROFF
```

## 使用示例

### ROS 服务调用

```bash
# 查询位姿
ros2 service call /arm_control/get_pose arm_control_msgs/srv/GetPose

# 移动到目标位置
ros2 service call /arm_control/move_pose arm_control_msgs/srv/MovePose \
    "{x: 200.0, y: 100.0, z: -100.0, phi: 45.0, duration: 2.0}"

# 视觉抓取（发布相机坐标）
ros2 topic pub --once /vision_grasp/grasp_target geometry_msgs/Point \
    "{x: 10.0, y: 5.0, z: 250.0}"
```

### Python 集成

```python
import rclpy
from geometry_msgs.msg import Point

class VisionDetector:
    def __init__(self):
        self.grasp_pub = self.create_publisher(Point, 'vision_grasp/grasp_target', 10)
    
    def on_detection(self, camera_x, camera_y, camera_z):
        msg = Point()
        msg.x = camera_x
        msg.y = camera_y
        msg.z = camera_z
        self.grasp_pub.publish(msg)
```

## 文档

- **ROS 2 系统**：[ros2/README.md](ros2/README.md) - 完整的 ROS 节点文档
- **运动学推导**：[docs/arm.md](docs/arm.md) - 机械臂逆运动学数学推导
- **视觉标定**：[docs/vision_calibration_horizontal.md](docs/vision_calibration_horizontal.md) - 相机标定指南

## 坐标系说明

**机械臂基坐标系**（Z 轴朝上）：
- 原点：J1 线性滑轨底部
- X 轴：基座正前方
- Y 轴：基座左侧
- Z 轴：竖直向上（高度）

**相机坐标系**（水平安装）：
- X 轴：右侧
- Y 轴：向下
- Z 轴：正前方（光轴）

变换由 `camera_to_base.py` 和 `vision_grasp` 节点自动处理。

## 依赖

| 环境 | 依赖 |
|------|------|
| ESP32 | PlatformIO, Arduino framework, esp32-camera |
| ROS 2 | ROS 2 Humble (robostack), Python 3.12 |
| 工具 | NumPy, OpenCV (可选) |

## 许可

MIT License
