Close

Gesture based Navigation

A project log for GestureBot - Computer Vision Robot

A mobile robot that responds to human gestures, facial expressions using real-time pose estimation and gesture recognition & intuitive HRI

vipin-mVipin M 08/14/2025 at 15:540 Comments

Gesture-controlled robotics represents a compelling intersection of computer vision, human-robot interaction, and real-time motion control. I developed GestureBot as a comprehensive system that translates hand gestures into precise robot movements, addressing the unique challenges of responsive detection, mechanical stability, and modular architecture design.

The project tackles several technical challenges inherent in gesture-controlled navigation: achieving sub-second response times while maintaining detection stability, preventing mechanical instability in tall robot form factors through acceleration limiting, and creating a modular architecture that supports future multi-modal integration. My implementation demonstrates how MediaPipe's gesture recognition capabilities can be effectively integrated with ROS2 navigation systems to create a responsive, stable, and extensible robot control platform.

System Architecture

I designed GestureBot with a modular architecture that separates gesture detection from motion control, enabling flexible deployment and future expansion. The system consists of two primary components connected through ROS2 topics:

Core Components

Gesture Recognition Module: Handles camera input and MediaPipe-based gesture detection, publishing stable gesture results to /vision/gestures. This module operates independently and can function without the motion control system for testing and development.

Navigation Bridge Module: Subscribes to gesture detection results and converts them into smooth robot motion commands published to /cmd_vel. This separation allows the navigation bridge to potentially receive input from multiple detection sources in future implementations.

Data Flow Architecture

Camera Input → MediaPipe Processing → Gesture Stability Filtering → /vision/gestures
                                                                           ↓
/cmd_vel ← Acceleration Limiting ← Velocity Smoothing ← Motion Mapping ←──┘

The modular design enables independent operation of components. I can run gesture detection without motion control for development, or use external gesture sources with the navigation bridge. This architecture prepares the system for Phase 4 multi-modal integration where object detection and pose estimation will feed into the same navigation bridge.

Launch File Structure

I implemented separate launch files for each component:

This separation provides deployment flexibility and simplifies parameter management for different robot configurations.

Technical Implementation

MediaPipe Integration for Hand Gesture Detection

I integrated MediaPipe's gesture recognition model using a controller-based architecture that handles the MediaPipe lifecycle independently from ROS2 infrastructure. The implementation uses MediaPipe's LIVE_STREAM mode with asynchronous processing for optimal performance:

class GestureRecognitionController:
    def __init__(self, model_path: str, confidence_threshold: float, max_hands: int, result_callback):
        self.model_path = model_path
        self.confidence_threshold = confidence_threshold
        self.max_hands = max_hands
        self.result_callback = result_callback
        
        # Initialize MediaPipe gesture recognizer
        base_options = python.BaseOptions(model_asset_path=self.model_path)
        options = vision.GestureRecognizerOptions(
            base_options=base_options,
            running_mode=vision.RunningMode.LIVE_STREAM,
            result_callback=self._mediapipe_callback,
            min_hand_detection_confidence=self.confidence_threshold,
            min_hand_presence_confidence=self.confidence_threshold,
            min_tracking_confidence=self.confidence_threshold,
            num_hands=self.max_hands
        )
        self.recognizer = vision.GestureRecognizer.create_from_options(options)

The controller processes camera frames asynchronously and extracts gesture classifications, hand landmarks, and handedness information. I implemented robust handedness extraction that handles MediaPipe's data structure variations: 

def extract_handedness(self, handedness_list, hand_index: int) -> str:
    """Extract handedness from MediaPipe results using standard category_name format."""
    if not handedness_list or hand_index >= len(handedness_list):
        return 'Unknown'
    
    try:
        handedness_data = handedness_list[hand_index]
        if hasattr(handedness_data, '__len__') and len(handedness_data) > 0:
            if hasattr(handedness_data[0], 'category_name'):
                return handedness_data[0].category_name
    except (IndexError, AttributeError):
        pass
    
    return 'Unknown'

Gesture-to-Motion Mapping System

I implemented a comprehensive mapping system that translates 8 distinct hand gestures into specific robot movements:

GESTURE_MOTION_MAP = {
    'Thumb_Up': {'linear_x': 0.3, 'angular_z': 0.0},      # Move forward
    'Thumb_Down': {'linear_x': -0.2, 'angular_z': 0.0},   # Move backward  
    'Open_Palm': {'linear_x': 0.0, 'angular_z': 0.0},     # Emergency stop
    'Pointing_Up': {'linear_x': 0.3, 'angular_z': 0.0},   # Move forward (alternative)
    'Victory': {'linear_x': 0.0, 'angular_z': 0.8},       # Turn left
    'ILoveYou': {'linear_x': 0.0, 'angular_z': -0.8},     # Turn right
    'Closed_Fist': {'linear_x': 0.0, 'angular_z': 0.0},   # Emergency stop
    'None': {'linear_x': 0.0, 'angular_z': 0.0}           # No gesture detected
}

The mapping system includes safety considerations with multiple emergency stop gestures (Open_Palm and Closed_Fist) that bypass acceleration limiting for immediate response. Forward and backward movements use different maximum velocities, with backward motion limited to 0.2 m/s for safety.

Acceleration Limiting for Mechanical Stability

Tall robots with high centers of mass require careful acceleration management to prevent wobbling and tipping. I implemented a comprehensive acceleration limiting system that operates at 25 Hz to provide smooth velocity transitions:

def apply_acceleration_limit(self, current_vel: float, target_vel: float, max_accel: float, dt: float) -> float:
    """Apply acceleration limiting to prevent abrupt velocity changes."""
    velocity_diff = target_vel - current_vel
    max_change = max_accel * dt
    
    if abs(velocity_diff) <= max_change:
        return target_vel  # Can reach target this step
    else:
        # Limit the change to maximum allowed acceleration
        return current_vel + (max_change if velocity_diff > 0 else -max_change)

The system uses conservative acceleration limits tuned for tall robot stability:

High-Frequency Velocity Smoothing

I implemented a 25 Hz velocity smoothing loop that continuously interpolates between current and target velocities. This high-frequency control prevents the jerky motion that can cause mechanical instability:

def update_smoothed_velocity(self) -> None:
    """High-frequency velocity smoothing with acceleration limiting."""
    current_time = time.time()
    dt = current_time - self.last_velocity_update
    self.last_velocity_update = current_time
    
    # Skip if dt is too large (system lag) or too small
    if dt > 0.1 or dt < 0.001:
        return
    
    # Apply acceleration limiting to linear velocity
    self.current_velocity['linear_x'] = self.apply_acceleration_limit(
        self.current_velocity['linear_x'],
        self.target_velocity['linear_x'],
        max_linear_accel,
        dt
    )
    
    # Apply acceleration limiting to angular velocity
    self.current_velocity['angular_z'] = self.apply_acceleration_limit(
        self.current_velocity['angular_z'],
        self.target_velocity['angular_z'],
        max_angular_accel,
        dt
    )
    
    # Create and publish smoothed Twist message
    twist = Twist()
    twist.linear.x = self.current_velocity['linear_x']
    twist.angular.z = self.current_velocity['angular_z']
    self.cmd_vel_publisher.publish(twist)

Performance Optimizations

Gesture Stability Filtering

I developed a multi-layered stability filtering system that balances responsiveness with detection reliability. The system combines three filtering mechanisms:

Time-based stability: Requires gestures to be detected consistently for a minimum duration (0.1 seconds for maximum responsiveness).

Consistency checking: Validates that the same gesture appears in consecutive detections (single detection sufficient for immediate response).

Transition delay: Enforces minimum time between different gesture changes (0.05 seconds for fastest viable switching).

def check_gesture_stability(self, gesture_name: str, confidence: float, timestamp: float) -> bool:
    """Enhanced stability checking with consistency and transition delay."""
    # Add current detection to history
    self.gesture_detection_history.append({
        'gesture': gesture_name,
        'confidence': confidence,
        'timestamp': timestamp
    })
    
    # Check consistency - same gesture detected N consecutive times
    if not self._check_gesture_consistency(gesture_name):
        return False
    
    # Check transition delay - minimum time between different gestures
    if not self._check_transition_delay(gesture_name, timestamp):
        return False
    
    # Check time-based stability - existing method
    if not self.is_gesture_stable(gesture_name, timestamp):
        return False
    
    return True

These parameters were optimized through testing to achieve sub-second response times while maintaining detection stability. The system achieves gesture-to-motion latency of 0.3-0.5 seconds under optimal conditions.

Smart Logging System

I implemented intelligent logging that reduces noise while preserving essential debugging information. The system only logs when velocity commands change significantly or represent meaningful motion:

def log_velocity_change(self, twist: Twist) -> None:
    """Smart logging that only logs when velocity actually changes or is significant."""
    current_linear = twist.linear.x
    current_angular = twist.angular.z
    last_linear = self.last_published_velocity['linear_x']
    last_angular = self.last_published_velocity['angular_z']
    
    # Check if velocity is zero
    is_zero_velocity = abs(current_linear) < 0.001 and abs(current_angular) < 0.001
    was_zero_velocity = abs(last_linear) < 0.001 and abs(last_angular) < 0.001
    
    # Check if velocity has changed significantly
    velocity_changed = (abs(current_linear - last_linear) > 0.01 or 
                       abs(current_angular - last_angular) > 0.01)
    
    # Log conditions: non-zero velocities, significant changes, or transitions to stop
    should_log = (not is_zero_velocity or 
                 (velocity_changed and not was_zero_velocity) or
                 (velocity_changed and not self.zero_velocity_logged))
    
    if should_log:
        self.get_logger().info(f'Velocity: linear: {current_linear:.3f}, angular: {current_angular:.3f}')

This approach reduces log volume by approximately 80% while maintaining visibility into actual motion commands and system state changes.

Cross-Workspace ROS2 Integration

I designed the system to support cross-workspace integration, enabling gesture control of robots with existing navigation stacks. The modular architecture publishes standard /cmd_vel messages that any ROS2 navigation system can consume:

# GestureBot workspace (publishes /cmd_vel)
cd ~/GestureBot/gesturebot_ws
source ~/GestureBot/gesturebot_env/bin/activate
source install/setup.bash
ros2 launch gesturebot gesture_recognition.launch.py
ros2 launch gesturebot gesture_navigation_bridge.launch.py

# Robot base workspace (subscribes to /cmd_vel)
cd ~/Robot/robot_ws
source ~/Robot/robot_env/bin/activate
source install/setup.bash
ros2 run robot_base base_controller_node

This architecture enables gesture control of any ROS2-compatible robot without modifying existing navigation code.

Results and Performance Metrics

Responsiveness Improvements

Through systematic optimization, I achieved significant improvements in gesture-to-motion response times:

Before optimization:

After optimization:

Stability Achievements

The acceleration limiting system successfully prevents mechanical instability in tall robot configurations:

Acceleration compliance:

Motion characteristics:

Detection Performance

The gesture recognition system demonstrates robust performance across various conditions:

Detection accuracy:

System resource usage:

Discussions