Вид с камеры HAR

Государственные регулирующие органы устанавливают ряд требований, чтобы обеспечить достаточность информации, получаемой с помощью системы непрямого обзора заднего вида, для точного и своевременного маневрирования транспортным средством. Это влияет на то, как водитель воспринимает окружающую обстановку.

Для систем заднего вида на основе системы мониторинга с помощью камер (CMS) Национальное управление безопасностью движения на трассах (NHTSA) требует соблюдения следующих требований (S6.6.2.3, указанных в UNECE46):

  • S5.5.3. Время ответа. Изображение с камеры заднего вида, соответствующее требованиям пунктов S5.5.1 (Зона обзора) и S5.5.2 (Размер), при тестировании в соответствии с пунктом S14.2 появляется в течение 2 секунд после начала события движения задним ходом.

  • S5.5.4. Время задержки. Изображение с камеры заднего вида, соответствующее требованиям разделов S5.5.1 и S5.5.2, не показывается после завершения движения задним ходом.

  • S5.5.5. Деактивация. Изображение с камеры заднего вида, соответствующее требованиям пунктов S5.5.1 и S5.5.2, остается видимым во время движения задним ходом, пока водитель не изменит вид или селектор направления движения не будет переведен из положения заднего хода.

  • S6.6.2.3.3.5. Артефакты. В руководстве по эксплуатации должны быть указаны возможные артефакты и их влияние на частичное перекрытие поля зрения и объектов, из-за чего водителю может потребоваться особая бдительность и внимательность.

  • S6.2.2.3.4.1. Частота кадров. Движения объектов перед камерой выглядят плавными и естественными. Минимальная частота кадров системы – не менее 30 Гц (эквивалентно 30 кадрам в секунду). При слабом освещении или маневрировании на низкой скорости минимальная частота кадров системы составляет не менее 15 Гц.

  • S6.2.2.3.4.2 Время формирования изображения. Время формирования изображения на мониторе составляет менее 55 мс при температуре 22 °C ± 5 °C.

  • S6.2.2.3.4.3. Задержка системы. Система мониторинга камеры (CMS) имеет достаточно низкую задержку, чтобы отображать пейзаж почти одновременно. Задержка составляет менее 200 мс при температуре 22 °C ± 5 °C.

Чтобы обеспечить соответствие этим требованиям в AAOS без виртуализации, мы добавили в Android Automotive OS (AAOS) систему расширенного обзора (EVS). Мы также запустили аналогичный сервис для виртуализации на устройствах с AAOS, в которых используется отрисовщик с высокой доступностью (HAR). Он также соответствует этим требованиям.

Конвейер предпросмотра камеры

Предпросмотр камеры состоит из пяти этапов:

Этапы конвейера предварительного просмотра камеры

Рисунок 1. Этапы конвейера предпросмотра камеры.

Блок сервиса камеры – это платформа Camera Service и ее уровень абстракции, который позволяет приложениям получать доступ к доступным камерам и использовать их. Функция Display Service визуализирует данные изображений для пользователей. Приложение реализует целевые пути пользователей с помощью сервиса камеры и сервиса дисплея.

Основной сценарий использования заднего вида:

  1. Водитель переводит селектор направления движения (рычаг переключения передач) в положение заднего хода, чтобы запустить событие движения задним ходом.

  2. Система транслирует событие поддержки. Приложение получает широковещательное сообщение и инициализирует блок ввода камеры (сервис камеры) и отрисовщик (сервис дисплея).

  3. Блок ввода камеры инициализирует платформу Camera Service и возвращает приложению дескриптор сервиса.

  4. Рендерер инициализирует окно просмотра для входных данных камеры из шага 4.

  5. Приложение запрашивает у блока ввода камеры разрешение на отправку буферов кадров и событий.

  6. Приложение добавляет в очередь буферы доставленных кадров с помощью обратных вызовов (асинхронно). Буферы кадров принадлежат блоку ввода камеры, поэтому приложение не может их изменять.

  7. Приложение извлекает из очереди буфер кадра (если очередь не пуста) и создает представление для пользователя. Пользователи могут создать копию и изменить ее содержимое.

  8. Приложение отправляет буфер в средство визуализации.

  9. Рендерер отрисовывает содержимое полученного буфера на экране.

  10. Если основное событие ещё не завершилось, перейдите к шагу 7. Когда событие завершается, приложение запрашивает у блока ввода с камеры прекращение отправки буферов кадров и событий после того, как представление будет скрыто от пользователя.

  11. Приложение может закрыть камеру и освободить отрисовщик.

На рисунке 1 показан поток данных. На этом изображении используются элементы API QNX Camera Library для работы с платформой Camera Service.

Основной путь пользователя HAR

Рисунок 2. Основной путь пользователя HAR.

Блок ввода с камеры объявляет три интерфейса:

  • CameraManager, в котором объявлены методы управления камерами. Например, приложение использует этот интерфейс, чтобы открыть (зарезервировать) целевую камеру.

  • CameraDevice содержит методы управления камерой, например для запуска или остановки потока данных.

  • CameraStreamListener объявляет один метод для получения различных событий от целевой камеры.

Дизайн

В этом разделе описана архитектура системы.

Удобство использования

Водитель может посмотреть изображение с задней камеры на дисплее приборной панели, когда включает заднюю передачу. Изображение с камеры исчезает с экрана, когда водитель переключает передачу.

Можно включить дополнительные пути пользователей. Например, водитель может посмотреть, что происходит в зоне, невидимой в зеркалах, когда включен указатель поворота.

Как включить предпросмотр камеры

При использовании камер приложение перечисляет и оценивает доступные камеры, чтобы найти лучшую для поставленной цели. Например, для обзора задней части автомобиля приложение ищет в списке доступных камер ту, которая показывает заднюю часть автомобиля.

Приложение оценивает это, изучая характеристики каждой камеры, например местоположение, направление объектива, частоту кадров, разрешение и формат выходных данных. Если у нескольких камер одинаковые обязательные характеристики, приложение может проверить дополнительные, например поле зрения и фокусное расстояние.

На изображении показана последовательность действий для запуска предварительного просмотра с помощью статической конфигурации камеры:

Как запустить предварительный просмотр с камеры со статической конфигурацией

Рисунок 3. Начать предварительный просмотр с камеры со статической конфигурацией.

Как остановить предварительный просмотр камеры

Приложение перестает показывать изображение с задней камеры, когда событие, связанное с движением задним ходом, заканчивается. Чтобы избежать показа пустого экрана или неподвижного изображения, приложение сначала скрывает изображение от пользователя, а затем запрашивает, чтобы блок ввода камеры прекратил отправку событий.

На изображении показана последовательность действий для остановки потока данных с целевой камеры:

Остановить поток данных с целевого устройства камеры

Рисунок 4. Остановите поток данных с целевого устройства камеры.

Ошибки

Камера может неожиданно перестать отправлять новый буфер кадра. Чтобы обнаруживать такие случаи, блок ввода камеры может реализовать таймер, который истекает при поступлении нового кадра, и отправлять уведомление, когда этот таймер истекает.

Когда приложение получает уведомление, оно сообщает пользователю, что предварительный просмотр камеры больше не активен, и пытается восстановить его, закрывая и снова открывая устройство камеры. На рисунке 5 показано, как приложение обрабатывает тайм-аут:

Как обрабатывать время ожидания

Рисунок 5. Обработка тайм-аута (зависание потока данных).

Блок ввода камеры может сообщать об инцидентах, отличных от зависания потока данных, и встраивать больше деталей в буферы. Производители оригинального оборудования могут использовать эти метаданные для обработки инцидентов на своей платформе.

Объекты activity

API используется приложениями, которые работают на хосте и управляют дисплеем приборной панели через HAR (синие блоки на схеме ниже).

На рисунке 5 приведена схема системы.

Схема системы

Рисунок 6. Системная диаграмма.

Услуги

Вызовы API должны выполняться в контексте вызывающего процесса.

API

Новый API предназначен только для приложений, которые управляют предварительным просмотром изображений с камеры на дисплее приборной панели через HAR. API доступен через уровень абстракции платформы и связывается динамически.

Интерфейс CameraInputBlock содержит методы для инициализации функций камеры и получения менеджера блокировки ввода. Приложение использует возвращенный экземпляр CameraManager для управления камерами.

// 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;
};

Класс CameraManager содержит методы для открытия (или получения права собственности на) камер и освобождения их, когда приложение завершает работу с камерой. Приложение может открыть несколько камер и использовать их потоки для создания более широкого поля зрения или многоканального просмотра.

// 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;
};

Если приложения не могут определить, какую камеру использовать, они могут выбрать камеру, которая лучше всего подходит для текущей ситуации. CameraManager::getCameraList() возвращает список экземпляров CameraDescriptor, в котором указаны характеристики каждой камеры.

Класс CameraDevice представляет собой одно устройство камеры и объявляет методы для запуска и остановки потока данных. Если характеристики камеры не заданы статически, клиенты получают их из дескриптора и анализируют.

Например, клиент может получить список конфигураций потока, которые предлагает целевое устройство камеры, из его метаданных и выбрать конфигурацию с лучшими атрибутами (например, частотой кадров, разрешением и форматом вывода).

// 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() принимает три аргумента:

  • Конфигурация потока, выбранная вызывающим абонентом.

  • Прослушиватель для получения событий потока.

  • Указатель на эффективную конфигурацию потока. Настоятельно рекомендуем вызывающей стороне проверять это значение, чтобы правильно обрабатывать буферы кадров.

Когда CameraDevice::start() запускает поток данных с платформой Camera Service, он сохраняет слабую ссылку на объект прослушивателя вызывающего объекта, чтобы обнаружить неожиданное завершение вызывающего объекта.

Когда клиент завершает работу с буфером кадров, он должен уведомить устройство камеры о том, что буфер больше не нужен, вызвав метод CameraDevice::doneWithFrame().

Когда поток запускается, клиент получает сообщения о событиях. Чаще всего это новый буфер кадра. Клиент получает событие kNewFrameBuffer, содержащее данные изображений и метаданные буфера кадра, через зарегистрированную функцию обратного вызова. StreamEventType объявляет больше типов для обработки других событий потока. Например, поток данных может быть остановлен или зависнуть.

// 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;
};

В этом примере показана реализация интерфейса CameraInputBlock и его приложения:

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.
}

Эффективность

Обзорность сзади соответствует требованиям государственных стандартов.

Значение Регламент
Срок рассмотрения CFR 571.111 S5.5.3
Частота кадров UNECE R46 6.2.2.3.4
Время формирования изображения UNECE R46 6.2.2.3.4.2
Задержка системы UNECE R46 6.2.2.3.4.3

Конфиденциальность

Конфиденциальность:

  • API не требует от реализаций собирать, регистрировать или хранить информацию, позволяющую идентифицировать личность. Однако, поскольку полученные изображения (или связанные с ними метаданные) могут содержать сведения, позволяющие идентифицировать личность, приложение, использующее API, должно получить явное согласие пользователя.

  • Пользователи не могут управлять камерами, чтобы просматривать изображения на дисплее приборной панели, поскольку камеры выполняют важные функции безопасности. Производители получают согласие пользователя во время настройки или от водителя.

  • Этот API не поддерживает фоновые клиенты камеры. Поэтому индикатор конфиденциальности, который сообщает пользователям, что камера устройства собирает данные, не входит в область действия.