Skip to content

Commit 2c062b9

Browse files
Initial commit
0 parents  commit 2c062b9

25 files changed

+5258
-0
lines changed

.github/workflows/publish.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Trigger on version tags like v0.0.1
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Set up Python
16+
uses: actions/setup-python@v4
17+
with:
18+
python-version: '3.9'
19+
20+
- name: Install build dependencies
21+
run: |
22+
python -m pip install --upgrade pip
23+
pip install build twine
24+
25+
- name: Build package
26+
run: python -m build
27+
28+
- name: Check package
29+
run: twine check dist/*
30+
31+
- name: Publish to PyPI
32+
env:
33+
TWINE_USERNAME: __token__
34+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
35+
run: twine upload dist/*

.gitignore

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
dist/
13+
downloads/
14+
eggs/
15+
.eggs/
16+
lib/
17+
lib64/
18+
parts/
19+
sdist/
20+
var/
21+
wheels/
22+
*.egg-info/
23+
.installed.cfg
24+
*.egg
25+
26+
# PyInstaller
27+
# Usually these files are written by a python script from a template
28+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
29+
*.manifest
30+
*.spec
31+
32+
# Installer logs
33+
pip-log.txt
34+
pip-delete-this-directory.txt
35+
36+
# Unit test / coverage reports
37+
htmlcov/
38+
.tox/
39+
.nox/
40+
.coverage
41+
.coverage.*
42+
.cache
43+
.pytest_cache/
44+
.hypothesis/
45+
46+
# Translations
47+
*.mo
48+
*.pot
49+
50+
# Django stuff:
51+
*.log
52+
local_settings.py
53+
db.sqlite3
54+
55+
# Flask stuff:
56+
instance/
57+
.webassets-cache
58+
59+
# Scrapy stuff:
60+
.scrapy
61+
62+
# Sphinx documentation
63+
docs/_build/
64+
65+
# PyBuilder
66+
target/
67+
68+
# Jupyter Notebook
69+
.ipynb_checkpoints
70+
71+
# Environments
72+
.env
73+
.venv
74+
env/
75+
venv/
76+
ENV/
77+
env.bak/
78+
venv.bak/

.vscode/launch.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Debug: ros2_calib",
6+
"type": "python",
7+
"request": "launch",
8+
"module": "ros2_calib.main",
9+
"justMyCode": true
10+
}
11+
]
12+
}

CLAUDE.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is a manual LiDAR-Camera calibration tool for ROS 2 that provides a graphical interface for performing extrinsic calibration between LiDAR sensors and cameras. The application is built with PySide6 and operates on recorded rosbag data (.mcap files) without requiring a live ROS 2 environment.
8+
9+
## Development Commands
10+
11+
### Installation and Setup
12+
```bash
13+
# Install the project in development mode
14+
python -m pip install .
15+
```
16+
17+
### Running the Application
18+
```bash
19+
# Run the calibration tool
20+
ros2_calib
21+
```
22+
23+
### Code Quality
24+
```bash
25+
# Run linter (configured in pyproject.toml)
26+
ruff check
27+
28+
# Format code
29+
ruff format
30+
```
31+
32+
## Architecture Overview
33+
34+
The application follows a modular GUI-based architecture:
35+
36+
**Core Workflow:**
37+
1. User loads a rosbag file (.mcap format)
38+
2. Application displays available topics for selection
39+
3. User selects image, point cloud, and camera info topics
40+
4. Interactive calibration view allows manual 2D-3D correspondences
41+
5. RANSAC-based PnP solver provides initial estimate
42+
6. Scipy least-squares optimization refines the transformation
43+
44+
**Key Components:**
45+
46+
- **main.py**: Application entry point with PySide6 QApplication setup
47+
- **main_window.py**: Primary GUI window handling rosbag loading and topic selection
48+
- **calibration_widget.py**: Interactive widget for 2D/3D point selection and visualization
49+
- **calibration.py**: Core mathematical calibration logic using OpenCV and Scipy
50+
- **bag_handler.py**: Rosbag file processing and message extraction utilities
51+
- **ros_utils.py**: Mock ROS 2 message dataclasses (PointCloud2, Image, CameraInfo) and conversion utilities
52+
53+
**Key Components (Continued):**
54+
55+
- **transformation_widget.py**: Node graph visualization for TF trees using NodeGraphQt
56+
- **lidar_cleaner.py**: Point cloud processing based on RePLAy ECCV 2024 paper for removing occluded points
57+
- **tf_transformations.py**: Transform utilities for coordinate frame conversions
58+
59+
**Application Flow:** The main application uses a QStackedWidget to manage multiple views:
60+
1. Initial view for rosbag loading and topic selection (main_window.py)
61+
2. Interactive calibration view with 2D/3D visualization (calibration_widget.py)
62+
3. Transform tree visualization and management (transformation_widget.py)
63+
64+
**Dependencies:** The project uses `rosbags` library for ROS bag processing, avoiding dependency on live ROS 2 installation. All ROS message types are mocked as dataclasses in `ros_utils.py`. NodeGraphQt provides the graph visualization for TF trees.
65+
66+
**Calibration Algorithm:** Two-stage approach using OpenCV's `solvePnPRansac` for robust initial pose estimation followed by Scipy's `least_squares` optimization for refinement. The objective function minimizes reprojection error between 3D LiDAR points and 2D image correspondences. Point cloud cleaning uses algorithms from the RePLAy paper to remove occluded points.
67+
68+
## Configuration
69+
70+
- **Linting**: Configured in `pyproject.toml` with ruff (line length: 100, select: E, F, W, I)
71+
- **Entry Point**: Defined in `pyproject.toml` as `ros2_calib = "ros2_calib.main:main"`
72+
- **Dependencies**: PySide6, rosbags, numpy, opencv-python-headless, scipy, ruff, NodeGraphQt, transforms3d, setuptools
73+
74+
## Development Notes
75+
76+
- The application is designed to work offline with recorded rosbag data
77+
- GUI framework: PySide6 for cross-platform compatibility
78+
- No test suite is currently present in the codebase
79+
- Code uses modern Python with type hints and dataclasses
80+
- The LiDAR cleaning implementation is based on the RePLAy ECCV 2024 paper for removing projective artifacts
81+
- Transform visualization uses NodeGraphQt for interactive graph-based TF tree management

GEMINI.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Gemini Code Assistant Context
2+
3+
This file provides context for the Gemini code assistant to understand the project structure, purpose, and conventions.
4+
5+
## Project Overview
6+
7+
This project is a graphical tool for performing manual extrinsic calibration between a LiDAR sensor and a camera for ROS 2. It is a standalone Python application that uses PySide6 for its user interface.
8+
9+
The tool's workflow is as follows:
10+
1. The user loads a rosbag (`.mcap` file).
11+
2. The application reads the bag and displays the available topics.
12+
3. The user selects the appropriate topics for the image, point cloud, and camera information.
13+
4. The application then proceeds to a calibration view where the user can manually create 2D-3D correspondences between the image and the point cloud.
14+
5. The core calibration logic uses these correspondences to calculate the rigid transformation (rotation and translation) between the camera and LiDAR frames. It employs OpenCV's RANSAC-based PnP solver for a robust initial estimate and Scipy's least-squares optimization for refinement.
15+
16+
The project is structured to be independent of a live ROS 2 environment for its core functionality, instead relying on the `rosbags` library to process recorded data. It uses dataclasses to mock the necessary ROS 2 message structures.
17+
18+
## Key Files
19+
20+
- `pyproject.toml`: Defines the project metadata, dependencies (`PySide6`, `rosbags`, `numpy`, `opencv-python-headless`, `scipy`), and the main entry point script.
21+
- `ros2_calib/main.py`: The main entry point for the application.
22+
- `ros2_calib/main_window.py`: Implements the main GUI window, handling rosbag loading and topic selection.
23+
- `ros2_calib/calibration_widget.py`: (Inferred) The widget that handles the interactive 2D/3D point selection and visualization for the calibration process.
24+
- `ros2_calib/calibration.py`: Contains the core mathematical logic for performing the calibration using OpenCV and Scipy.
25+
- `ros2_calib/bag_handler.py`: Provides functions for reading topic information and messages from rosbag files.
26+
- `ros2_calib/ros_utils.py`: Defines mock dataclasses for ROS 2 message types (`PointCloud2`, `Image`, `CameraInfo`) and includes utility functions for converting message data into NumPy arrays.
27+
28+
## Building and Running
29+
30+
### Installation
31+
32+
To install the project and its dependencies, run the following command from the project root directory:
33+
34+
```bash
35+
python -m pip install .
36+
```
37+
38+
### Running the Application
39+
40+
Once installed, the application can be run using the command defined in `pyproject.toml`:
41+
42+
```bash
43+
ros2_calib
44+
```
45+
46+
## Development Conventions
47+
48+
- **GUI:** The user interface is built with PySide6.
49+
- **ROS 2 Data:** The application interacts with ROS 2 data via `.mcap` rosbag files. It does not require a running ROS 2 daemon.
50+
- **Calibration Logic:** The calibration algorithm is implemented in the `calibrate` function in `ros2_calib/calibration.py`. It uses a robust RANSAC approach followed by a least-squares refinement.
51+
- **Code Style:** The code is written in modern Python, using type hints and dataclasses. There is no specific linter configuration file present.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Institute for Automotive Engineering (ika), RWTH Aachen University
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MANIFEST.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include README.md
2+
include LICENSE
3+
include CLAUDE.md
4+
recursive-include assets *.png *.jpg *.jpeg

0 commit comments

Comments
 (0)