Vista de cámara HAR

Los reguladores gubernamentales implementan varios requisitos para garantizar que la visibilidad trasera indirecta proporcione suficiente información para maniobrar el vehículo de manera precisa y oportuna. Esto influye en la percepción del conductor sobre el entorno.

En el caso de los sistemas de visibilidad trasera basados en el Sistema de Monitoreo de Cámara (CMS), la Administración Nacional de Seguridad del Tráfico en Rutas (NHTSA) exige que cumplas con estos requisitos (S6.6.2.3 al que se hace referencia en UNECE46):

  • S5.5.3 Tiempo de respuesta. La imagen del espejo retrovisor que cumple con los requisitos de S5.5.1 (campo de visión) y S5.5.2 (tamaño), cuando se prueba de conformidad con S14.2, se muestra en un plazo de 2.0 segundos después del inicio de un evento de marcha atrás.

  • S5.5.4 Tiempo de permanencia. La imagen del retrovisor que cumple con los requisitos de S5.5.1 y S5.5.2 no se muestra después de que finaliza el evento de marcha atrás.

  • S5.5.5 Desactivación. La imagen del espejo retrovisor que cumple con los requisitos de los puntos S5.5.1 y S5.5.2 permanece visible durante el evento de marcha atrás hasta que el conductor modifica la vista o el selector de dirección del vehículo se mueve de la posición de marcha atrás.

  • S6.6.2.3.3.5 Artefactos. El manual del operador debe hacer referencia a los posibles artefactos y sus impactos en la oclusión parcial del campo visual y de los objetos, lo que podría requerir que el conductor esté particularmente alerta y atento.

  • S6.2.2.3.4.1 Velocidad de fotogramas. Los movimientos de los objetos frente a la cámara se renderizan de forma fluida y suave. La velocidad de fotogramas mínima del sistema es de, al menos, 30 Hz (equivalente a 30 FPS). En condiciones de poca luz o mientras se maniobra a baja velocidad, la velocidad de fotogramas mínima del sistema es de al menos 15 Hz.

  • S6.2.2.3.4.2 Tiempo de formación de la imagen. El tiempo de formación de la imagen del monitor es inferior a 55 ms a una temperatura de 22 grados Celsius ± 5 grados Celsius.

  • S6.2.2.3.4.3 Latencia del sistema. Un sistema de monitoreo de cámara (CMS) tiene una latencia lo suficientemente corta como para renderizar el paisaje casi al mismo tiempo. La latencia es inferior a 200 ms a una temperatura de 22 grados Celsius ±5 grados Celsius.

Presentamos el Sistema de vista extendida (EVS) del SO Android Automotive (AAOS) para cumplir con estos requisitos en el AAOS bare metal. Presentamos un servicio similar para la virtualización en dispositivos AAOS con el renderizador de alta disponibilidad (HAR), que también demuestra el cumplimiento de estos requisitos.

Canalización de vista previa de la cámara

Estas cinco etapas conforman la canalización de vista previa de la cámara:

Etapas de la canalización de vista previa de la cámara

Figura 1. Etapas de la canalización de vista previa de la cámara.

El bloque de servicio de la cámara hace referencia a la plataforma de Camera Service y su capa de abstracción que permite que las apps accedan a las cámaras disponibles y las usen. La función Display Service visualiza los datos de imágenes para los usuarios. La app implementa los recorridos del usuario objetivo con Camera Service y Display Service.

El recorrido del usuario principal para la visibilidad trasera es el siguiente:

  1. El conductor coloca el selector de dirección (la palanca de cambios) en reversa para activar un evento de marcha atrás.

  2. El sistema emite el evento de respaldo. La app recibe la transmisión y, luego, inicializa el bloque de entrada de la cámara (servicio de cámara) y el renderizador (servicio de pantalla).

  3. El bloque de entrada de la cámara inicializa la plataforma del servicio de cámara y devuelve el identificador del servicio a la app.

  4. El renderizador inicializa la ventana de visualización para la entrada de cámara del paso 4.

  5. La app solicita el bloque de entrada de cámara para comenzar a enviar búferes de fotogramas y eventos.

  6. La app pone en cola los búferes de fotogramas entregados a través de las devoluciones de llamada (asíncronas). Los búferes de fotogramas son propiedad del bloque de entrada de la cámara, por lo que la app no puede modificarlos.

  7. La app quita de la cola un búfer de fotogramas (si la cola no está vacía) y compone la vista del usuario. Los usuarios pueden hacer una copia para modificar el contenido.

  8. La app envía un búfer al renderizador.

  9. El renderizador dibuja el contenido de un búfer recibido en la pantalla.

  10. Si el evento de respaldo sigue en curso, ve al paso 7. Cuando se completa el evento de respaldo, la app solicita que el bloque de entrada de la cámara deje de enviar búferes de fotogramas y eventos después de ocultar la vista al usuario.

  11. De manera opcional, la app cierra una cámara y libera el renderizador.

En la figura 1, se ilustra el flujo. Esta imagen usa elementos de la API de la biblioteca de cámaras de QNX para usar la plataforma del servicio de cámara.

Recorrido principal del usuario de HAR

Figura 2: Es el recorrido principal del usuario de HAR.

El bloque de entrada de cámara declara tres interfaces:

  • CameraManager, declara métodos para administrar dispositivos de cámara; por ejemplo, la app usa esta interfaz para abrir (reservar) un dispositivo de cámara objetivo.

  • CameraDevice declara métodos para controlar un dispositivo de cámara, por ejemplo, iniciar o detener un flujo de datos.

  • CameraStreamListener declara un solo método para recibir varios eventos de una cámara de destino.

Diseño

En esta sección, se detalla el diseño del sistema.

Experiencia del usuario

El conductor puede obtener una vista previa de la cámara posterior en la pantalla del panel de instrumentos cuando coloca la palanca de cambios en reversa. La pantalla deja de mostrar la vista previa de la cámara cuando el conductor saca la palanca de cambios de la marcha atrás.

Se pueden habilitar recorridos del usuario adicionales. Por ejemplo, el conductor puede obtener una vista previa del área que no se ve en los espejos cuando se activa la luz de giro.

Iniciar la vista previa de la cámara

Cuando se usan cámaras, la app enumera y evalúa las cámaras disponibles para encontrar la mejor cámara para el propósito previsto. Por ejemplo, para la visibilidad trasera, la app busca la cámara que muestra la parte trasera del vehículo en la lista de cámaras disponibles.

La app evalúa esto examinando las características de cada cámara, por ejemplo, la ubicación, la dirección de la lente, la velocidad de fotogramas, la resolución de salida y el formato de salida. Si varias cámaras tienen las mismas características requeridas, es posible que la app examine características adicionales, como el campo de visión y la distancia focal.

En esta imagen, se muestra una secuencia para iniciar una vista previa de la cámara con una configuración estática de la cámara:

Inicia la vista previa de la cámara con una configuración estática

Figura 3: Inicia la vista previa de la cámara con la configuración estática de la cámara.

Detener la vista previa de la cámara

La app deja de proporcionar visibilidad trasera cuando finaliza el evento de marcha atrás. Para evitar mostrar una pantalla en blanco o una imagen estática, la app primero oculta la vista al usuario y, luego, solicita que el bloque de entrada de la cámara deje de enviar eventos.

En esta imagen, se muestra una secuencia para detener un flujo de datos desde una cámara objetivo:

Detener el flujo de datos del dispositivo de cámara de destino

Figura 4: Detener el flujo de datos del dispositivo de cámara de destino

Errores

Un dispositivo de cámara puede dejar de enviar un nuevo búfer de fotogramas de forma inesperada. Para detectar este tipo de incidentes, el bloque de entrada de la cámara podría implementar un temporizador que vence cuando llega un nuevo fotograma y enviar una notificación cuando vence este temporizador.

Cuando la app recibe una notificación, le informa al usuario que la vista previa de la cámara ya no está activa y trata de restablecerla cerrando el dispositivo de cámara y volviéndolo a abrir. En la figura 5, se muestra cómo la app controla un tiempo de espera:

Cómo controlar un tiempo de espera

Figura 5: Cómo controlar un tiempo de espera agotado (flujo de datos que se detiene)

El bloque de entrada de la cámara puede informar incidentes que no sean una transmisión de datos suspendida y puede incorporar más detalles en los búferes. Los OEM pueden usar estos metadatos de eventos para controlar incidentes en su plataforma.

Actividades

Las apps que se ejecutan en el host y administran la pantalla del panel de instrumentos a través del HAR (bloques azules en el siguiente diagrama) usan la API.

En la figura 5, se ilustra un diagrama del sistema:

Diagrama del sistema

Figura 6: Diagrama del sistema.

Servicios

Se espera que las llamadas a la API se ejecuten en el contexto del proceso de llamada.

API

La nueva API solo está diseñada para las apps que administran las vistas previas de la cámara en la pantalla del panel de instrumentos a través del HAR. La API está disponible a través de la capa de abstracción de la plataforma y se vincula de forma dinámica.

La interfaz CameraInputBlock declara métodos para inicializar la funcionalidad de la cámara y obtener el administrador de bloques de entrada. La app usa una instancia de CameraManager que se devolvió para administrar los dispositivos de cámara.

// This class represents a camera input block for the application that manages the
// instrument cluster display with Harry.
public class CameraInputBlock : public InputBlock {
    public:
        // Clean up the resources.
        virtual ~CameraInputBlock();

        // A method inherited from InputBlock class. This method initializes
        // CameraInputBlock instance; e.g. checking the platform camera service
        // is live.
        //
        // @return CAMERA_EPERM if the platform camera service is not
        //                      available.
        //         CAMERA_OK otherwise.
        virtual CameraError init() override;

        // A method inherited from InputBlock class. This method release all
        // resources held by this CameraInputBlock instance.
        virtual void release() override;

        // This method returns a CameraManager instance. The caller uses
        // this instance to manage camera devices.
        //
        // @param out If this method is successful, this points to a valid
        //            CameraManager instance.
        // @return CAMERA_EACCESS when we fail to create CameraManager instance
        //         to return.
        //         CAMERA_OK otherwise.
        virtual CameraError getCameraManager(
            std::shared_ptr<CameraManager>* out) = 0;

    private:
        // Handle to manage camera devices.
        std::shared_ptr<CameraManager> mMgr;

        // Handle to manage camera devices that have been opened by clients.
        std::set<CameraDevice> mCameras;
};

La clase CameraManager declara métodos para abrir (o poseer) cámaras y liberarlas cuando la app termina de usar esa cámara. La app puede abrir más de una cámara y consumir sus transmisiones para crear una experiencia de campo de visión más amplio o de varias vistas.

// This pure virtual class declares methods to manage camera devices.
public class CameraManager {
    public:
        // This method returns a list of CameraDescriptor objects representing
        // available cameras.
        //
        // @param out A list of CameraDescriptor instances. This list may be
        //            empty if the platform camera service does not list any
        //            camera.
        // @return CAMERA_EACCESS if we failed to build a camera list.
        //         CAMERA_OK otherwise.
        virtual CameraError getCameraList(
            std::vector<CameraDescriptor>* out) = 0;

        // Open a camera device associated with a given string identifier.
        //
        // @param ID A string identifier of a camera device to request.
        // @param out A pointer to CameraDevice shared pointer object. This
        //            is null when we fail to open a target device.
        // @return CAMERA_ENODEV if no camera is associated with a given id.
        //         CAMERA_EACCESS if it fails to open a target device.
        //         CAMERA_OK otherwise.
        virtual CameraError open(
            std::string ID, std::shared_ptr<CameraDevice>* out) = 0;

        // Close a camera device associated with a given string identifier.
        // This method is assumed to be always successful.
        //
        // @param id A string identifier of a camera device to close.
        virtual void close(std::string id) = 0;
};

Si las apps no pueden detectar qué cámaras usar, pueden elegir la que mejor funcione en el contexto. CameraManager::getCameraList() devuelve una lista de instancias de CameraDescriptor, que proporciona las características de cada cámara.

La clase CameraDevice representa un solo dispositivo de cámara y declara métodos para iniciar y detener su flujo de datos. Si las características de la cámara no se conocen de forma estática, los clientes las obtienen de su descriptor y las analizan.

Por ejemplo, un cliente puede obtener una lista de las configuraciones de transmisión que ofrece un dispositivo de cámara objetivo a partir de sus metadatos y elegir la que tenga los mejores atributos (por ejemplo, frecuencias de fotogramas, resoluciones y formato de salida).

// This class represents a single camera device.
public class CameraDevice {
    public:
        // Start a data stream that attributes are matching to given
        // configuration best.
        // If a selected configuration is not given (null), a data stream is
        // initiated in its default configuration and return.
        //
        // @param configuration Selected attributes of the imagery data stream.
        // @param listener An object to listen to an active data stream.
        // @param effective Actual attributes of started data stream.
        // @return CAMERA_EINVAL if a listener object is invalid.
        //         CAMERA_EIO if we failed to start a video stream.
        //         CAMERA_OK otherwise.
        virtual CameraError start(
                std::shared_ptr<CameraStreamConfiguration>& configuration,
                std::shared_ptr<CameraStreamListener>& listener,
                std::shared_ptr<CameraStreamConfiguration>* effective) = 0;

        // Stop a data stream.
        virtual void stop() = 0;

        // Get a camera descriptor.
        //
        // @param desc A set of attributes that defines this camera device.
        // @return CAMERA_ENODATA if a descriptor is not available.
        //         CAMERA_OK otherwise.
        CameraError getDescriptor(std::shared_ptr<CameraDescriptor>* desc) = 0;

        // Return a consumed buffer to the camera device. A client of active
        // stream must return a frame buffer explicitly by calling this method.
        virtual void doneWithFrame(std::shared_ptr<FrameBuffer>& buffer) = 0;

    private:
        // Describe this camera device.
        CameraDescriptor mDescriptor;

        // A weak reference to a listening client.
        std::weak_ptr<CameraStreamListener> mClient;
};

// This class declares attributes that characterize a camera device.
public class CameraDescriptor {
    public:
        // Unique std::string object to identify a single camera device.
        std::string mId;

        // A set of stream configurations this camera device is capable of. A
        // camera must have at least one stream configuration.
        std::set<CameraStreamConfiguration> mConfigurations;

        // Are more attributes needed to exist, such as locations, lens
        // facing directions, and intrinsic/extrinsic parameters?
};

// This class declares attributes that characterize an imagery data stream.
public class CameraStreamConfiguration {
    public:
        // Width of output of this stream in pixels.
        unsigned int mWidthInPixels;

        // Height of output of this stream in pixels.
        unsigned int mHeightInPixels;

        // An average number of frames per second.
        double mFrameRate;

        // A format of this stream's output. A client could calculate a
        // byte-per-pixel (bpp) from this.
        CameraColorFormat mFormat;
};

// This class represents a listener/callback object to listen to frames and
// events.
public class CameraStreamListener {
    public:
        // A listener method to receive various stream events including a new
        // frame buffer.
        //
        // @param event CameraStreamEvent object that represents a single event
        //              such as an arrival of a new frame buffer, camera stream
        //              is terminated, and so forth.
        virtual void onEvent(std::shared_ptr<CameraStreamEvent>* event) = 0;
};

CameraDevice::start() toma tres argumentos:

  • Es la configuración de transmisión elegida por el llamador.

  • Objeto de escucha para recibir eventos de transmisión.

  • Es un puntero a una configuración de transmisión efectiva. Recomendamos que el llamador examine este valor para controlar los búferes de fotogramas entrantes según lo previsto.

Cuando CameraDevice::start() inicia un flujo de datos con la plataforma de Camera Service, mantiene una referencia débil al objeto de escucha del llamador para detectar la finalización inesperada del llamador.

Cuando un cliente termina de usar un búfer de fotogramas, debe notificar a un dispositivo de cámara que ya no lo necesita llamando al método CameraDevice::doneWithFrame().

Cuando se inicia una transmisión, un cliente recibe mensajes de eventos. Un mensaje común es un búfer de fotogramas nuevo. A través de una función de devolución de llamada registrada, un cliente recibe un evento kNewFrameBuffer que contiene los datos de la imágenes junto con los metadatos del búfer de fotogramas. StreamEventType declara más tipos para controlar otros eventos de transmisión. Por ejemplo, un flujo de datos detenido o bloqueado.

// This class lists events possibly occurring while a data stream is active.
enum class CameraStreamEventType {
    // A delivery of a new frame buffer.
    kNewFrameBuffer,
    // A data stream has been stopped.
    kStreamStopped,
    // No new frame buffer arrives for a while.
    kStreamHang,
    // Add more.
    ...
};

// This class represents a single instance of StreamEventType.
public class CameraStreamEvent {
    public:
        // Return a type of this event.
        //
        // @return CameraStreamEventType enum value.
        CameraStreamEventType getType() { return mType; }

        // Return a pointer to data associated with this event.
        //
        // @return A shared pointer object of the buffer that contains data for
        //         this event.
        std::shared_ptr<void> getData() { return mData; }

    private:
        // Describe a type of this event.
        CameraStreamEventType mType;

        // A pointer to the data buffer.
        std::shared_ptr<void> mData;
};

// This class inherits StreamEvent class and has additional fields to represent
// the frame buffer.
public class FrameBufferEvent : public CameraStreamEvent {
    public:
        // Return an identifier of this frame buffer.
        //
        // @return A unique integer value to identify this frame buffer.
        int getBufferID() { return mBufferID; }

        // Give access to frame buffer metadata.
        //
        // @return A shared pointer to the buffer that contains data besides
        //         the imagery data.
        std::shared_ptr<void> getMetadata() { return mMetadata; }

    private:
        // Unique integer to identify this buffer.
        int mBufferID;

        // A pointer to metadata of this frame buffer.
        std::shared_ptr<void> mMetadata;
};

En este ejemplo, se muestra una implementación de la interfaz CameraInputBlock y su app:

CameraError getCameraManager(std::shared_ptr<CameraManager>* out) {
    // During an instantiation, CameraManager will retrieve a list of camera
    // devices from the platform camera service and identify their attributes.
    *out = std::make_shared<CameraManager>();
    return CAMERA_OK;
}

// This method returns a list of CameraDescriptor objects representing available
// cameras.
CameraError CameraManager::getCameraList(std::vector<CameraDescriptor>* out) {
    if (mCameraList.size() < 1) {
        // Query a list of cameras and get their attributes.
    }
    *out = mCameraList;
    return CAMERA_OK;
}

// Open a camera device associated with a given string identifier.
CameraError CameraManager::open(std::string id, std::shared_ptr<CameraDevice>* out) {
    if (!mCameraList.contains(id)) {
        // We cannot identify any camera with a given value.
        return CAMERA_NODEV;
    }

    // During a construction, CameraDevice will obtain a handle of a target
    // camera device from the platform camera service.
    std::shared_ptr<CameraDevice> h = std::make_shared<CameraDevice>(id);
    if (!h) {
        // We fail to open a camera device.
        return CAMERA_EACCESS;
    }

    *out = h;
    return CAMERA_OK;
}

// Close a camera device associated with a given string identifier. This method
// is assumed to be always successful.
void CameraManager::close(std::string id) {
    if (!mCameraList.contains(id)) {
        // We ignore calls with unknown identifiers.
        return;
    }

    // mCameraList.remove() returns an object removed from the list.
    std::shared_ptr<CameraDevice> device = mCameraList.remove(id);

    // Ensure a device stops streaming.
    device->stop();
}

// Start a data stream that attributes are matching to given configuration
// best.
// If a selected configuration is not given (null), a data stream will be
// initiated in its default configuration and return.
CameraError CameraDevice::start(
        std::shared_ptr<CameraStreamConfiguration>& configuration,
        std::shared_ptr<CameraStreamListener>& listener,
        std::shared_ptr<CameraStreamConfiguration>* effective) {
    if (!listener) {
        return CAMERA_EINVAL;
    }

    // selectStreamConfiguration examines this camera's stream configurations
    // and returns the one closest to the selected configuration.
    CameraStreamConfiguration config = selectStreamConfiguration(configuration);

    // mDevice refers to the camera handle for the platform camera service. We
    // may need to translate CameraStreamConfiguration for the platform service.
    mDevice->configure(
        configuration.mWidth, configuration.mHeight, configuration.mFormat);

    // Start a data stream with a callback object.
    if (!mDevice->startStream(mCallback)) {
        // We failed to start a data stream.
        return CAMERA_EIO;
    }

    return CAMERA_OK;
}

// Stop a data stream.
void CameraDevice::stop() {
    if (!mDevice) {
        // Nothing to do if we don't have a valid camera handle for the
        // platform camera service.
        return;
    }

    mDevice->stopStream();
}

// Get a camera descriptor.
CameraError CameraDevice::getDescriptor(std::shared_ptr<CameraDescriptor>* desc) {
    if (!mDescriptor) {
        return CAMERA_ENODATA;
    }

    *desc = *mDescriptor;
    return CAMERA_OK;
}

// Return a consumed buffer to the camera device. A client of active stream
// must return a frame buffer explicitly by calling this method.
void CameraDevice::doneWithFrame(std::shared_ptr<FrameBuffer>& buffer) {
    if (!mBufferRecords.contains(buffer.getId())) {
        // Ignore a call with unknown frame buffer.
        return;
    }

    // Simply remove from the record.
    (void)mBufferRecords.remove(buffer.getId());
}

// This method handles gear-shift events.
void Application::handleGearShift(GearSelection selection) {
    switch (selection) {
        case GEAR_SELECTION_REVERSE:
            // Upon the reverse gear selection, we are going to start a video
            // stream and show its preview on the instrument cluster display.
            (void)startStream(mCameraInputBlock);

            // FIXME: Exact method to control the camera preview window on the
            // instrument display is to be determined.
            show(mRearVisibilityWindow);
            break;

        default:
            // Upon all other gear selection, we are going to stop a video
            // stream (if it's running) and hide the preview.
            stopStream(mCameraInputBlock);

            // FIXME: Exact method to control the camera preview window on the
            // instrument display is to be determined.
            hide(mRearVisibilityWindow);
            break;
    }
}

bool Application::startStream(std::shared_ptr<CameraInputBlock> handle) {
    return handle->start(std::bind(&Application::handleStreamCallback, this);
}

void Application::stopStream(std::shared_ptr<CameraInputBlock> handle) {
    handle->stop();
}

// This method handles a stream callback.
void Application::handleStreamCallback(StreamEvent& event) {
    switch (event.getType()) {
        case StreamEventType::kNewFrameBuffer:
            // Handle a new frame buffer. We may just enqueue it for the
            // future or forward to CameraInputBlock client.
            break;

        case StreamEventType::kStreamStopped:
            // Handle as an incident if this event is not expected.
            break;

        // More cases to be added.
    }
}

void Application::handleNewFrameBuffer(StreamEvent& event) {
    // Enqueue a new frame buffer for the further processing. A buffer
    // must be returned explicitly by calling
    // CameraDevice.doneWithFrame(FrameBuffer&) method.
}

void Application::handleStreamEvent(StreamEvent& event) {
    // Handle a received stream event except a new frame buffer's
    // arrival; e.g. a video stream is terminated unexpectedly.
}

Rendimiento

La visibilidad trasera cumple con estas reglamentaciones gubernamentales.

Valor Regulaciones
Tiempo de respuesta CFR 571.111 S5.5.3
Velocidad de fotogramas UNECE R46 6.2.2.3.4
Hora de formación de la imagen UNECE R46 6.2.2.3.4.2
Latencia del sistema UNECE R46 6.2.2.3.4.3

Privacidad

Específicamente en relación con la privacidad:

  • La API no requiere que las implementaciones recopilen, registren o almacenen información de identificación personal (PII). Sin embargo, dado que los datos de imágenes capturadas (o los metadatos asociados) podrían contener PII, la app que usa la API debe obtener el consentimiento explícito del usuario.

  • Los usuarios no pueden controlar los dispositivos de cámara para obtener una vista previa en la pantalla del panel de instrumentos porque las cámaras cumplen funciones críticas para la seguridad. Los OEM obtienen el consentimiento del usuario durante la configuración o del conductor.

  • Esta API no admite clientes de cámara en segundo plano. Por lo tanto, el indicador de privacidad, que informa a los usuarios que un dispositivo de cámara está capturando datos, está fuera del alcance.