Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 48 additions & 11 deletions camera_ros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,59 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
# Find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclpy REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(std_srvs REQUIRED)
find_package(std_msgs REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(rosidl_default_generators REQUIRED)

# Generate Python classes for the custom service
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/GetInt.srv"
DEPENDENCIES std_msgs
ADD_LINTER_TESTS
)

# Install Python scripts
install(PROGRAMS
scripts/camera_publisher.py
DESTINATION lib/${PROJECT_NAME}
)

# Install launch files
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)

# Install launch files
install(FILES
launch/camera.launch.py
DESTINATION share/${PROJECT_NAME}/launch
)

# Install package.xml
install(FILES
package.xml
DESTINATION share/${PROJECT_NAME}/
)

# Install README
install(FILES
README.md
DESTINATION share/${PROJECT_NAME}/
)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

# Export the generated interfaces
ament_export_dependencies(rosidl_default_runtime)

ament_package()
207 changes: 207 additions & 0 deletions camera_ros/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Zero-Copy MJPEG Camera Publisher for ROS2

A high-performance camera publisher optimized for NVIDIA Jetson AGX Orin that provides zero-copy MJPEG streaming with minimal CPU overhead.

## Features

- 🚀 **Zero-copy MJPEG streaming** - No CPU transcoding overhead
- ⚡ **High performance** - Optimized for NVIDIA Jetson AGX Orin
- 🎯 **Smart frame rate control** - Dynamic FPS adjustment
- 🔄 **Automatic client management** - Activates when clients detected, pauses when none
- 📡 **Client count monitoring** - Listens to `/client_count` topic for automatic control
- 🛠️ **GStreamer pipeline** - Hardware-accelerated video processing

## Requirements

- NVIDIA Jetson AGX Orin (tested on 64GB model)
- ROS2 Humble
- Python 3.10+
- OpenCV with GStreamer support
- GStreamer 1.0+

## Usage

### Prerequisites

**Source the ROS environment:**
```bash
# Navigate to your workspace
cd /home/dev/lucy_ws

# Source ROS Humble
source /opt/ros/humble/setup.zsh

# Source your workspace
source install/setup.zsh
```

### Launch Methods

**Basic launch (camera activates automatically when clients connect):**
```bash
ros2 launch camera_ros camera.launch.py
```

**Launch with custom parameters:**
```bash
ros2 launch camera_ros camera.launch.py fps:=20.0 device:=/dev/video0
```

### Automatic Client Management

The camera automatically activates and deactivates based on client count:

**Client count monitoring:**
- Camera **activates** when `client_count > 0`
- Camera **pauses** when `client_count = 0`
- Monitors `/client_count` topic for automatic control

### Topics

**Published topics:**
- `/camera/mobius/jpg` - CompressedImage (JPEG format, published only when clients are present)

**Subscribed topics:**
- `/client_count` - Int32 (monitors client count for automatic activation/deactivation)

## Configuration

### Camera Settings

Edit `src/camera_publisher.py` to modify:

```python
# Frame rate (1.0 - 30.0 FPS)
FPS = 10.0

# GStreamer pipeline
GST_PIPELINE = (
"v4l2src device=/dev/video0 ! "
"image/jpeg,width=1280,height=720,framerate=30/1 ! "
"jpegparse ! "
"appsink drop=true emit-signals=true sync=false"
)
```

### Resolution Options

For different resolutions, modify the GStreamer pipeline:

```python
# 640x480 (lower CPU usage)
GST_PIPELINE = "v4l2src device=/dev/video0 ! image/jpeg,width=640,height=480,framerate=30/1 ! jpegparse ! appsink drop=true emit-signals=true sync=false"

# 1920x1080 (higher quality, more CPU)
GST_PIPELINE = "v4l2src device=/dev/video0 ! image/jpeg,width=1920,height=1080,framerate=30/1 ! jpegparse ! appsink drop=true emit-signals=true sync=false"
```

## Performance Optimization

### System-Level Optimizations

1. **Set CPU governor to performance mode:**
```bash
sudo jetson_clocks
```

2. **Check camera device:**
```bash
ls /dev/video*
v4l2-ctl --device=/dev/video0 --list-formats-ext
```

3. **Monitor performance:**
```bash
# Monitor CPU usage
htop

# Monitor Jetson stats
tegrastats

# Check frame rate
ros2 topic hz /camera/mobius/jpg
```

### Troubleshooting

#### Common Issues

**Camera not found:**
```bash
# Check USB devices
lsusb

# Check video devices
ls /dev/video*

# Check camera capabilities
v4l2-ctl --list-devices
v4l2-ctl --device=/dev/video0 --list-formats-ext

# Reload camera driver
sudo modprobe uvcvideo
```

**High CPU usage:**
- Reduce frame rate to 10-15 FPS
- Use lower resolution (640x480)
- Check for background processes
- Ensure CPU is in performance mode
- Use `jetson_clocks` for maximum performance

**ExternalShutdownException error:**
This usually occurs when the node is terminated externally. To prevent this:
```bash
# Use Ctrl+C to stop the node gracefully
# Or use the service to disable camera first
ros2 service call /camera/mobius/enable std_srvs/srv/SetBool "{data: false}"
```

## Architecture

```
Camera (/dev/video0)
GStreamer Pipeline (v4l2src → jpegparse → appsink)
OpenCV VideoCapture (zero-copy)
ROS2 CompressedImage Publisher
Client Applications
```

## API Reference

### CameraPublisher Class

**Methods:**
- `set_fps(fps)` - Set frame rate (1.0-30.0)
- `set_active(active)` - Enable/disable camera

**Parameters:**
- `fps` - Target frame rate
- `device` - Camera device path

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test on Jetson AGX Orin
5. Submit a pull request

## License

This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

## Authors

- **Sentience Robotics Team** - [[email protected]](mailto:[email protected])

## Support

For issues and questions:
- Check the troubleshooting section
- Open an issue on GitHub
- Contact the development team
21 changes: 21 additions & 0 deletions camera_ros/camera_ros/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2024 Sentience Robotics Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Camera ROS Package.

Zero-copy MJPEG camera publisher optimized for NVIDIA Jetson AGX Orin."""

__version__ = "1.0.0"
58 changes: 58 additions & 0 deletions camera_ros/launch/camera.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
# Copyright 2024 Sentience Robotics Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Launch file for zero-copy MJPEG camera publisher.

This launch file starts the camera node with optimized settings for NVIDIA Jetson AGX Orin."""

from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument, LogInfo
from launch.substitutions import LaunchConfiguration

def generate_launch_description():
# Launch arguments
fps_arg = DeclareLaunchArgument(
'fps',
default_value='15.0',
description='Camera frame rate (1.0-30.0 FPS)'
)

device_arg = DeclareLaunchArgument(
'device',
default_value='/dev/video0',
description='Camera device path'
)

# Camera node
camera_node = Node(
package='camera_ros',
executable='camera_publisher.py',
name='camera_publisher',
output='screen',
parameters=[{
'fps': LaunchConfiguration('fps'),
'device': LaunchConfiguration('device'),
}],
)

return LaunchDescription([
fps_arg,
device_arg,
LogInfo(msg='Starting zero-copy MJPEG camera publisher with automatic client-based activation'),
camera_node,
])
Loading