nnspike.utils

Utility functions and algorithms for robot control and image processing.

This module provides essential utility functions for image processing, control algorithms, and data recording used throughout the nnspike package for robot navigation and line following.

Modules:

control: Computer vision algorithms for line detection and navigation image: Image processing and visualization utilities pid: PID controller implementation for smooth robot movement recorder: Data recording utilities for training data collection

Example

Using image processing and control utilities:

from nnspike.utils import find_line_edges_at_y, PIDController

# Detect line edges in an image left_edge, right_edge = find_line_edges_at_y(image, y_position=100)

# Create PID controller for steering pid = PIDController(kp=1.0, ki=0.1, kd=0.05) steering = pid.update(error)

nnspike.utils.find_line_edges_at_y(image, roi, target_y, threshold_value=50)[source]

Get the left and right edge points of a black line at a specific Y coordinate.

Parameters:
  • image (np.ndarray) – Input image (BGR or grayscale).

  • roi (tuple[int, int, int, int]) – Tuple (x, y, width, height) defining the ROI.

  • target_y (float) – The Y coordinate where to detect line edges (in original image coordinates).

  • threshold_value (float, optional) – Threshold for binary conversion. Defaults to 50.

Returns:

Tuple containing:
  • left_x: X coordinate of left edge (None if not found)

  • right_x: X coordinate of right edge (None if not found)

Return type:

tuple[float | None, float | None]

nnspike.utils.find_gate_virtual_line(image, scan_x=320, from_y=0, to_y=480)[source]

Find the virtual line for gate detection based on gray color regions.

This function detects gray regions in an image and calculates the center point between the leftmost and rightmost gray pixels from a given scan position.

Parameters:
  • image (np.ndarray) – Input BGR image as numpy array.

  • scan_x (int, optional) – X-coordinate to start scanning from. Defaults to 320.

  • from_y (int, optional) – Starting Y-coordinate for scanning. Defaults to 0.

  • to_y (int, optional) – Ending Y-coordinate for scanning. Defaults to 480.

Returns:

Tuple containing:
  • virtual_line_coords: Virtual line coordinates (x, y) or None if no gate found

  • gray_mask: The processed gray mask for debugging

  • width: Width between left and right borders or None

Return type:

tuple[tuple[float, float] | None, np.ndarray | None, float | None]

nnspike.utils.find_bottle_center(image, color, min_area=500)[source]

Find the center coordinates and color pixel count of a colored object in an image using OpenCV.

This function detects objects of a specified color in an image and returns information about the largest detected object. It supports yellow, blue, and red color detection and can be used for various applications including object tracking, color-based navigation, and visual recognition.

This function is optimized for real-time applications with the following improvements: - Accepts numpy array input instead of file paths for real-time processing - Uses adaptive thresholding for better edge detection under various lighting conditions - Applies contour area filtering to reduce noise and false detections - Includes aspect ratio validation to filter out non-object-like shapes - Uses smaller morphological kernels for better performance - Removes debug print statements for cleaner real-time operation

Parameters:
  • image (np.ndarray) – Input image as numpy array (BGR format).

  • color (str) – Color to detect (‘yellow’, ‘blue’, or ‘red’).

  • min_area (int, optional) – Minimum contour area threshold for filtering noise. Defaults to 500.

Returns:

Tuple containing:
  • center: (x, y) center coordinates of the largest detected object

  • largest_contour: Contour of the largest detected object

  • color_pixel_count: Number of detected color pixels

Returns (None, None, 0) if not found.

Return type:

tuple[tuple[float, float] | None, np.ndarray | None, float | None]

Raises:

ValueError – If color parameter is not ‘yellow’, ‘blue’, or ‘red’.

nnspike.utils.find_bullseye(image, threshold=120)[source]

Find the center coordinates of a blue bullseye target in an image.

Uses blue color masking followed by circular shape detection for efficient bullseye detection. Prioritizes detections within a specified distance from the image center (x=320).

Parameters:
  • image (np.ndarray) – Input image as numpy array (BGR format).

  • threshold (float, optional) – Maximum allowed distance from image center (x=320). Bullseyes within range [320-threshold, 320+threshold] are prioritized. Defaults to 120.

Returns:

Tuple containing:
  • center: (x, y) center coordinates of the detected bullseye

  • contour: Detected bullseye contour

  • blue_pixel_count: Number of blue pixels

Returns (None, None, None) if not found.

Return type:

tuple[tuple[float, float] | None, np.ndarray | None, float | None]

nnspike.utils.calculate_attitude_angle(offset_pixels, roi_bottom_y, camera_height=0.2, focal_length_pixels=640)[source]

Calculate attitude angle (theta) from pixel offset using camera geometry.

This function converts the pixel-based offset detected in the camera image to a real-world attitude angle that represents the robot’s deviation from the desired path. This provides more physically meaningful control compared to simple pixel-based normalization.

Parameters:
  • offset_pixels (float) – Lateral offset in pixels from image center

  • roi_bottom_y (int) – Bottom y-coordinate of ROI (closer to robot)

  • camera_height (float, optional) – Camera height above ground in meters. Defaults to 0.20.

  • focal_length_pixels (float, optional) – Camera focal length in pixels. Defaults to 640.

Returns:

Attitude angle (theta) in radians. Positive values indicate rightward deviation,

negative values indicate leftward deviation.

Return type:

float

Note

The camera parameters (height and focal length) should be calibrated for your specific robot setup to ensure accurate angle calculations.

nnspike.utils.normalize_image(image)[source]

Normalize an input image by converting its color space, applying Gaussian blur, resizing, and scaling pixel values.

This function performs the following steps: 1. Converts the image from RGB to YUV color space. 2. Applies a Gaussian blur with a kernel size of 5x5. 3. Resizes the image to dimensions 200x66. 4. Scales the pixel values to the range [0, 1].

Parameters:

image (np.ndarray) – Input image in RGB format as a NumPy array.

Returns:

Normalized image as a NumPy array.

Return type:

np.ndarray

nnspike.utils.draw_driving_info(image, info, roi)[source]

Draws driving information on an image.

This function overlays driving-related information onto a given image. It draws a tracing point, a region of interest (ROI) rectangle, and various text annotations based on the provided info dictionary.

Parameters:
  • image (np.ndarray) – The input image on which to draw the information.

  • info (dict) –

    A dictionary containing the driving information to be displayed. Expected keys are:

    • ”trace_x” (int or str): The x-coordinate for the tracing point.

    • ”trace_y” (int or str): The y-coordinate for the tracing point.

    • ”text” (dict): A dictionary of text annotations where keys are the labels and values are the corresponding data.

  • roi (tuple[int, int, int, int]) – A tuple defining the region of interest in the format (x1, y1, x2, y2).

Returns:

The image with the overlaid driving information.

Return type:

np.ndarray

class nnspike.utils.PIDController(kp, ki, kd, setpoint, output_limits=(None, None))[source]

A PID (Proportional-Integral-Derivative) controller for closed-loop control systems.

This class implements a discrete-time PID controller that continuously calculates an error value as the difference between a desired setpoint and a measured process variable. The controller applies proportional, integral, and derivative corrections to minimize this error over time, making it suitable for robotic control systems, motor speed regulation, and other feedback control applications.

Methodology: The PID controller uses three distinct parameters: - Proportional (P): Provides output proportional to the current error - Integral (I): Accumulates past errors to eliminate steady-state error - Derivative (D): Predicts future error based on the rate of change

The control output is calculated as: output = Kp * error + Ki * integral + Kd * derivative

where error = setpoint - measured_value

Results: The controller produces a continuous control signal that drives the system towards the desired setpoint. Output can be constrained within specified limits to prevent actuator saturation or system damage.

Conclusion: This implementation provides time-aware PID control with integral windup prevention through output limiting. The controller maintains internal state between updates, making it suitable for real-time control applications. Note that proper tuning of Kp, Ki, and Kd parameters is crucial for optimal performance and system stability.

kp

Proportional gain coefficient.

Type:

float

ki

Integral gain coefficient.

Type:

float

kd

Derivative gain coefficient.

Type:

float

setpoint

Target value that the system should achieve.

Type:

float

output_limits

Min/max output constraints.

Type:

tuple[float | None, float | None]

Example

>>> # Create PID controller for steering control
>>> pid = PIDController(kp=1.0, ki=0.1, kd=0.05, setpoint=0.0)
>>> pid.set_output_limits((-1.0, 1.0))
>>>
>>> # Update control loop
>>> measured_position = get_sensor_reading()
>>> control_output = pid.update(measured_position)
>>> apply_control_signal(control_output)
__init__(kp, ki, kd, setpoint, output_limits=(None, None))[source]

Initializes the PIDController with specified parameters.

Sets up the PID controller with the given gain values, target setpoint, and optional output constraints. Initializes internal state variables for time tracking and error accumulation.

Parameters:
  • kp (float) – Proportional gain coefficient. Higher values increase responsiveness but may cause overshoot.

  • ki (float) – Integral gain coefficient. Eliminates steady-state error but may cause instability if too high.

  • kd (float) – Derivative gain coefficient. Reduces overshoot and improves stability but sensitive to noise.

  • setpoint (float) – Target value that the controller should achieve.

  • output_limits (tuple[float | None, float | None], optional) – Minimum and maximum output bounds. Use None for unbounded limits. Defaults to (None, None).

Example

>>> # Create PID for temperature control
>>> pid = PIDController(
...     kp=2.0, ki=0.5, kd=0.1, setpoint=25.0, output_limits=(-100, 100)
... )
set_output_limits(new_output_limits)[source]

Updates the output limits for the PID controller.

Modifies the minimum and maximum bounds for the controller output. This helps prevent actuator saturation and integral windup.

Parameters:

new_output_limits (tuple[float, float]) – New minimum and maximum output limits as (min_limit, max_limit).

Return type:

None

Example

>>> pid.set_output_limits((-50.0, 50.0))  # Limit output to ±50
update(measured_value)[source]

Computes the PID control output based on current measurement.

Calculates the control signal by combining proportional, integral, and derivative terms. Updates internal state for the next iteration and applies output limits if specified.

Parameters:

measured_value (float) – Current value of the process variable being controlled (e.g., current position, temperature, speed).

Returns:

Control output signal, typically used for actuator control (e.g., motor commands, valve positions). Value is constrained within the specified output limits.

Return type:

float

Note

The first call may have reduced accuracy for the derivative term due to lack of previous time reference.

Example

>>> current_temp = thermometer.read()
>>> heater_power = pid.update(current_temp)
>>> heater.set_power(heater_power)
class nnspike.utils.SensorRecorder(output_dir='storage/sensor_data', timestamp=None)[source]
High-performance CSV recorder for ETRobot sensor data and control information.

This class manages CSV file creation, writing, and cleanup with optimizations for

real-time robot operation at high frame rates (30fps+).

Features: - Single file open/close for entire session - Buffered writing for performance - Periodic flushing for data safety - Automatic cleanup on program exit - Comprehensive sensor and control data logging

__enter__()[source]

Context manager entry.

Return type:

SensorRecorder

__exit__(exc_type, exc_val, exc_tb)[source]

Context manager exit.

Return type:

None

__init__(output_dir='storage/sensor_data', timestamp=None)[source]

Initialize the sensor recorder.

Parameters:
  • output_dir (str) – Directory to store CSV files

  • timestamp (str | None) – Custom timestamp for filename, auto-generated if None

get_filename()[source]

Get the current CSV filename.

Returns:

Full path to the CSV file

Return type:

str

get_frame_count()[source]

Get the current frame count.

Returns:

Number of frames recorded

Return type:

int

log_frame_data(spike_status, mode=None)[source]

Log sensor data for a single frame.

Parameters:
  • spike_status (SpikeStatus) – SpikeStatus object with sensor data

  • mode (Mode | None) – Current behavior mode (e.g., Mode.LEFT_EDGE_FOLLOWING)

Return type:

None

start_recording()[source]

Start CSV recording session.

Creates output directory, opens CSV file, writes headers, and sets up cleanup.

Return type:

None

stop_recording()[source]

Stop CSV recording session and close file properly.

Return type:

None

Modules

control

Computer vision control utilities for robot navigation and object detection.

image

This module provides utility functions for image and video processing using OpenCV and NumPy.

pid

PID (Proportional-Integral-Derivative) controller implementation for control systems.

recorder

Sensor Data Recorder Module