Skip to content

2025W-HumanoidRoboticSystems/VerticalChess

Repository files navigation

Vertical Chess

A ROS2-based autonomous chess-playing robot using the Hiwonder AiNex Biped Humanoid Robot and a magnetic vertical chess board.

Setup

Git

1. Install Git LFS

# Linux
apt install git-lfs
git lfs install

# MacOS
brew install git-lfs
git lfs install

If LFS wasn’t installed or enabled when you cloned the repository, run git lfs pull inside the repository to download large files (e.g. images, videos, PDFs).

2. Clone Repository Recursively

git clone --recursive git@github.com:2025W-HumanoidRoboticSystems/VerticalChess.git

Recursive cloning is needed to download the submodules, e.g., Stockfish.

Forgot to Clone Recursively?

Run following commands inside the repository:

git submodule init
git submodule update

Docker

⚠️ If you get a ERROR: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/_ping": dial unix /var/run/docker.sock: connect: permission denied error, then you must execute the following:

# Contains shortcut commands
source .aliases

# Build image - takes a while
build-vertical-chess-docker-image

# Create container (only once or after a `docker rm`)
create-vertical-chess-docker-container

# Stop container
docker stop vertical-chess
# docker rm vertical-chess THIS DELETES THE CONTAINER AND ITS FILES!!!

# Resume container
docker start vertical-chess
docker attach vertical-chess

Python

Weird "chess not found" error bug. Solution:

python3 -m venv .venv
source .venv/bin/activate

pip install rospkg
pip install -U colcon-common-extensions
pip install chess numpy lark

# Build
python3 -m colcon build --symlink-install

Build

After a fresh clone of the repository, run setup.sh. You only need to do this once!

# Source the .aliases file
source .aliases

# Build
build

# Cleanup build files
clean

Running the System

Quick Start

# Launch all components with defaults
./launch_vertical_chess.sh

# With custom options
./launch_vertical_chess.sh --skill-level 5 --time-limit 1000

# Disable specific components
./launch_vertical_chess.sh --no-occupancy --no-manipulation

# See all options
./launch_vertical_chess.sh --help

ROS2 Launch File

ros2 launch vertical_chess vertical_chess.launch.py

# With parameters
ros2 launch vertical_chess vertical_chess.launch.py \
    enable_occupancy_detection:=true \
    stockfish_skill_level:=5 \
    enable_visualization:=true

Available launch parameters:

Parameter Default Description
enable_camera true System camera node
enable_board_detection true ChArUco board detection
enable_occupancy_detection true Color-based occupancy
enable_chess_engine true Chess logic + Stockfish
enable_manipulation true Pick-and-place FSM
enable_visualization true Debug windows
stockfish_skill_level 5 AI strength (0-20)
stockfish_depth 5 Search depth
stockfish_time_limit_ms 1000 Time per move

Architecture

System Overview

flowchart TB
    subgraph Hardware
        CAM[📷 Phone Camera<br/>/dev/video0]
        ROBOT[🤖 AiNex Robot<br/>24 Servos + Grippers]
        BOARD[♟️ Magnetic Vertical Board<br/>8x8 ChArUco]
    end

    subgraph Perception["Perception Pipeline"]
        SYS_CAM[system_camera_node]
        UNDIST[undistort_image_node]
        BOARD_DET[board_detection_node]
        OCC_DET[occupancy_detection_node]
    end

    subgraph Chess["Chess Engine"]
        ENGINE[chess_engine_node]
        STOCK[(Stockfish)]
    end

    subgraph Manipulation["Manipulation Pipeline"]
        MARKER[marker_locator_node]
        PICK[pick_and_place_node]
    end

    CAM --> SYS_CAM
    SYS_CAM --> UNDIST
    SYS_CAM --> BOARD_DET
    UNDIST --> OCC_DET
    BOARD_DET --> OCC_DET
    OCC_DET --> ENGINE
    ENGINE <--> STOCK
    ENGINE --> PICK
    MARKER --> PICK
    PICK --> ROBOT
    ROBOT --> BOARD
Loading

Node Communication

flowchart LR
    subgraph Topics["Published Topics"]
        T1[/"camera/system/compressed"/]
        T2[/"camera/system/camera_info"/]
        T3[/"/camera_image/undistorted"/]
        T4[/"/chessboard_homography"/]
        T5[/"/chessboard_pose_base"/]
        T6[/"~/occupancy_state"/]
        T7[/"~/board_state"/]
        T8[/"~/execute_move"/]
        T9[/"~/game_status"/]
    end

    SYS[system_camera_node] --> T1
    SYS --> T2
    UND[undistort_image_node] --> T3
    BD[board_detection_node] --> T4
    BD --> T5
    OCC[occupancy_detection_node] --> T6
    CHE[chess_engine_node] --> T7
    CHE --> T8
    CHE --> T9

    T1 --> BD
    T1 --> OCC
    T2 --> BD
    T3 --> OCC
    T4 --> OCC
    T6 --> CHE
    T8 --> PP[pick_and_place_node]
Loading

Service Architecture

flowchart TB
    subgraph chess_engine_node
        S1([get_board_state])
        S2([make_move])
        S3([get_best_move])
        S4([get_legal_moves])
        S5([validate_move])
        S6([reset_board])
    end

    subgraph marker_locator_node
        S7([get_square_position])
    end

    subgraph occupancy_detection_node
        S8([set_enabled])
    end

    CLI[External Clients] -.-> S1
    CLI -.-> S2
    CLI -.-> S3
    CLI -.-> S6

    PP[pick_and_place_node] -.->|"Get 3D position"| S7
    PP -.->|"Pause during manipulation"| S8
Loading

Pick-and-Place State Machine

stateDiagram-v2
    [*] --> WAITING_FOR_MOVE

    WAITING_FOR_MOVE --> IDLE: ExecuteChessMove received
    IDLE --> ADJUST_KNEE_PICK: Start pick sequence

    state "Pick Sequence" as pick {
        ADJUST_KNEE_PICK --> OPEN_GRIPPER
        OPEN_GRIPPER --> MOVE_TO_PICK
        MOVE_TO_PICK --> GRASP
        GRASP --> RETRACT_FROM_PICK
        RETRACT_FROM_PICK --> RETRACT_SHOULDER_1
        RETRACT_SHOULDER_1 --> MOVE_TO_HOME_1
    }

    MOVE_TO_HOME_1 --> ADJUST_KNEE_PLACE: Start place sequence

    state "Place Sequence" as place {
        ADJUST_KNEE_PLACE --> MOVE_TO_PLACE
        MOVE_TO_PLACE --> RELEASE
        RELEASE --> RETRACT_FROM_PLACE
        RETRACT_FROM_PLACE --> RETRACT_SHOULDER_2
        RETRACT_SHOULDER_2 --> MOVE_TO_HOME_2
    }

    MOVE_TO_HOME_2 --> CROUCH
    CROUCH --> DONE
    DONE --> WAITING_FOR_MOVE: Ready for next move
Loading

Coordinate Frames

flowchart TB
    BF[base_footprint<br/>Ground origin 0,0,0]
    BL[base_link<br/>z = 0.245m]
    CAM[system_camera_optical_link<br/>Camera frame]
    CB[chessboard_frame<br/>Origin at a1]
    SQ[square_a1 ... square_h8<br/>64 virtual links]

    BF --> BL
    BF --> CAM
    CAM -.->|"Detected pose"| CB
    CB --> SQ

    style CB stroke-dasharray: 5 5
Loading

Data Flow Summary

sequenceDiagram
    participant Cam as Camera
    participant Per as Perception
    participant Occ as Occupancy
    participant Eng as Chess Engine
    participant Stock as Stockfish
    participant Man as Manipulation
    participant Robot as Robot

    Note over Cam,Robot: Human makes a move on the board

    Cam->>Per: Compressed image
    Per->>Occ: Homography + undistorted image
    Occ->>Eng: Occupancy state (64 bools)

    Note over Eng: Infer move from occupancy change
    Eng->>Eng: Apply human's move

    Eng->>Stock: Get best response
    Stock->>Eng: Best move (UCI)

    Eng->>Man: ExecuteChessMove
    Man->>Man: Get square 3D position
    Man->>Robot: Joint commands

    Note over Robot: Robot executes pick-and-place
Loading

Nodes

Node Package Purpose
system_camera_node vertical_chess Capture from external camera (1280x720, 30fps)
undistort_image_node vertical_chess Fisheye lens distortion correction
board_detection_node vertical_chess ChArUco detection, homography computation
occupancy_detection_node vertical_chess Color-based piece occupancy tracking
chess_engine_node vertical_chess Chess logic, move inference, Stockfish interface
pick_and_place_node vertical_chess 17-state FSM for piece manipulation
marker_locator_node vertical_chess 3D square position lookup via URDF geometry

Key Topics

Topic Type Description
camera/system/compressed CompressedImage Raw camera feed (BEST_EFFORT QoS)
camera/system/camera_info CameraInfo Camera calibration parameters
/camera_image/undistorted CompressedImage Fisheye-corrected image (PNG)
/chessboard_homography Float64MultiArray 3x3 image-to-board transform
/chessboard_pose_base PoseStamped Board pose in robot base frame
/occupancy_detection_node/occupancy_state OccupancyState 64-bool occupancy array
/chess_engine_node/board_state BoardState FEN, turn, game status
/chess_engine_node/execute_move ExecuteChessMove Move command for robot
/chess_engine_node/game_status GameStatus Check/checkmate/stalemate events

Services

Service Type Description
/chess_engine_node/get_board_state GetBoardState Current FEN and legal moves
/chess_engine_node/make_move MakeMove Apply a move to the board
/chess_engine_node/get_best_move GetBestMove Query Stockfish for best move
/chess_engine_node/get_legal_moves GetLegalMoves All legal moves from position
/chess_engine_node/validate_move ValidateMove Check if move is legal
/chess_engine_node/reset_board ResetBoard Reset to start or custom FEN
/marker_locator_node/get_square_position GetSquarePosition 3D position of any square
/occupancy_detection_node/set_enabled SetBool Pause occupancy during manipulation

Manual Control

Chess Engine

# Make a move
ros2 service call /chess_engine_node/make_move \
  vertical_chess_interfaces/srv/MakeMove "{move: 'e4', validate: true}"

# Get best move
ros2 service call /chess_engine_node/get_best_move \
  vertical_chess_interfaces/srv/GetBestMove "{fen: '', skill_level: -1, depth: -1, time_limit_ms: -1}"

# Reset board
ros2 service call /chess_engine_node/reset_board \
  vertical_chess_interfaces/srv/ResetBoard "{fen: ''}"

Monitoring

# Board state
ros2 topic echo /chess_engine_node/board_state

# Occupancy
ros2 topic echo /occupancy_detection_node/occupancy_state

# Game events
ros2 topic echo /chess_engine_node/game_status

Square Positions

# Get 3D position of any square
ros2 service call /marker_locator_node/get_square_position \
  vertical_chess_interfaces/srv/GetSquarePosition "{square: 'e4'}"

Parameters

# List all parameters
ros2 param list

# Get/set Stockfish skill
ros2 param get /chess_engine_node stockfish_skill_level
ros2 param set /chess_engine_node stockfish_skill_level 10

RViz Visualization

# Launch RViz with robot model
ros2 launch ainex_description display.launch.py

Configure RViz:

  1. Set Fixed Frame to base_link
  2. Add MarkerArray display for /marker_locator_node/square_position_viz
  3. Add TF display for coordinate frames

Testing Occupancy

Publish test occupancy states:

# Starting position
ros2 topic pub --once /occupancy_detection_node/occupancy_state \
  vertical_chess_interfaces/OccupancyState \
  '{occupancy: [true,true,true,true,true,true,true,true,
                true,true,true,true,true,true,true,true,
                false,false,false,false,false,false,false,false,
                false,false,false,false,false,false,false,false,
                false,false,false,false,false,false,false,false,
                false,false,false,false,false,false,false,false,
                true,true,true,true,true,true,true,true,
                true,true,true,true,true,true,true,true], changed: false}'

Physical Setup

  • Robot: AiNex biped, fixed position 22cm from board
  • Board: Vertical magnetic, 32cm x 32cm, 40mm squares
  • Camera: Phone on tripod, 53cm from board, behind robot
  • Pieces: Magnetic, white at top, black at bottom

Documentation

  • CLAUDE.md - AI assistant context and architecture details
  • docs/COMMANDS.md - ROS2 command reference
  • docs/INTERFACES.md - Topic/service specifications

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages