Search code examples
pythonflaskraspberry-pi4

flask server crashing on Raspberry pi vid stream


I have built a pi car using my RPI4 and it vides streams from a webcam to my pc using a flask server and it also is controlled bu my pc. But sometimes when i am going fast on the car the cam feed sometimes freezes and when i try to connect to the vid feed again ir gives me an error called "Internal server error. this might be caused because of an internal error or the server is overloaded". and ther is the serverside script which is running on the pi

from gpiozero import Motor, Servo, Device
from gpiozero.pins.pigpio import PiGPIOFactory
import socket
from flask import Flask, Response
import cv2
from imutils.video import VideoStream
import threading

# Flask app for streaming video
app = Flask(__name__)

# Setup for GPIO
Device.pin_factory = PiGPIOFactory()

# Initialize the motors and servo
motor_left = Motor(forward=6, backward=13)
motor_right = Motor(forward=19, backward=26)
servo = Servo(18)

# Video stream initialization
vs = VideoStream(src=0).start()

def generate():
    """Video streaming generator function with optimizations."""
    while True:
        frame = vs.read()
        # Resize the frame to reduce size
        frame = cv2.resize(frame, (540, 380))  # Example resize, adjust as needed
        
        # Convert the frame to grayscale to reduce size further (optional)
        # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # Adjust JPEG quality to reduce size
        ret, jpeg = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), 50])
        
        if not ret:
            continue
        
        yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n')

@app.route('/video_feed')
def video_feed():
    """Video streaming route."""
    return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')

# Thread to run the Flask app
def run_flask_app():
    app.run(host='0.0.0.0', port=5000, debug=False, threaded=True, use_reloader=False)

flask_thread = threading.Thread(target=run_flask_app)
flask_thread.start()

# Rest of your GPIO control code
# Motor control functions
def forward():
    motor_left.forward()
    motor_right.forward()

def backward():
    motor_left.backward()
    motor_right.backward()

def right():
    motor_left.backward()
    motor_right.forward()

def left():
    motor_left.forward()
    motor_right.backward()

def stop():
    motor_left.stop()
    motor_right.stop()

# Updated servo control functions
def servo_move_left():
    servo.max()

def servo_move_right():
    servo.min()

def servo_stop():
    # Updated to explicitly set servo to neutral position
    servo.value = 0  # Neutral position

# Command mapping with added 'servo_neutral' command
commands = {
    "forward": forward,
    "backward": backward,
    "left": left,
    "right": right,
    "stop": stop,
    "servo_move_left": servo_move_left,
    "servo_move_right": servo_move_right,
    "servo_neutral": servo_stop,  # Use "servo_neutral" to center the servo
}

def execute_command(command):
    action = commands.get(command)
    if action:
        action()
    else:
        print(f"Unknown command: {command}")

def main():
    host = '0.0.0.0'
    port = 12345

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((host, port))
        s.listen()
        print("Server listening on port", port)

        while True:
            conn, addr = s.accept()
            with conn:
                print(f"Connected by {addr}")
                while True:
                    data = conn.recv(1024)
                    if not data:
                        break
                    command = data.decode('utf-8').strip().lower()
                    execute_command(command)


if __name__ == "__main__":
    main()

and the pc side code used to control it

import socket
import tkinter as tk
import webbrowser


def send_command(command):
    """Send command to the Raspberry Pi server."""
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect(("192.168.1.44", 12345))  # Update with your Raspberry Pi's IP address
            s.sendall(command.encode())
    except Exception as e:
        print(f"Error sending command: {e}")

def open_video_stream():
    """Open the video stream in the default web browser."""
    stream_url = "http://192.168.1.44:5000/video_feed"  # Update with your Raspberry Pi's IP address
    webbrowser.open(stream_url)

def on_key_press(event):
    """Map key presses to movement commands."""
    commands = {
        'w': 'forward',
        's': 'backward',
        'a': 'left',
        'd': 'right',
        'Left': 'servo_move_left',  # Arrow keys for servo movement
        'Right': 'servo_move_right',
        'Up': 'servo_neutral',  # Additional key for setting servo to neutral
    }
    command = commands.get(event.keysym) or commands.get(event.char)  # Handle both special keys and char keys
    if command:
        send_command(command)

def on_key_release(event):
    """Handle key release events to stop the car and potentially the servo."""
    control_keys = ['w', 's', 'a', 'd', 'Left', 'Right']
    if event.keysym in control_keys:
        send_command('stop')  # Stops the car and servo movement on release of movement keys

root = tk.Tk()
root.title("Raspberry Pi Car and Servo Control")

# Add a button to open the video stream
stream_button = tk.Button(root, text="Open Video Stream", command=open_video_stream)
stream_button.pack(pady=20)

# Bind key press and release events
root.bind("<KeyPress>", on_key_press)
root.bind("<KeyRelease>", on_key_release)

# Instructions for the user
instructions = tk.Label(root, text="Use WASD for car control, Arrow keys for servo movement, and 'Up' for servo neutral.\nReleasing control keys stops movement.\nPress 'Open Video Stream' to view the camera.")
instructions.pack(pady=20)

root.mainloop()

Pls can anyone help to solve this issue?

I tried to reduce the load by going slower and reducing the quality of the stream but it did not solve the issue.


Solution

  • It's hard to predict the behavior and identify defective parts when hardware involved in the picture.

    You need to start with basic troubleshooting steps like checking hardware connections and monitoring system resources. Based on your code try working on below parts ..

    Limit Frame Rate

    vs = VideoStream(src=0, framerate=20).start()
    

    You still feels feed is chunkier set even lower frame rates

    Reduce Flask Thread Pool Size

    You can try something like

    app.run(host='0.0.0.0', port=5000, debug=False, threaded=True, use_reloader=False)
    

    You can also try closing the video stream. this will ensure that the video stream is properly stopped and cleaned up when the Flask server is stopped

    @app.route('/video_feed')
    def video_feed():
        """Video streaming route."""
        try:
            return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')
        except Exception as e:
            print(f"Error streaming video: {e}")
            return Response(status=500)
    
    # Close Video Stream on Flask Shutdown
    @app.teardown_appcontext
    def close_video_stream(exception=None):
        vs.stop()