Государственные регулирующие органы устанавливают ряд требований, чтобы обеспечить достаточную непрямую видимость сзади для точного и своевременного маневрирования транспортным средством. Это влияет на осведомленность водителя об окружающей обстановке.
Для систем заднего обзора, основанных на системе видеонаблюдения (CMS), Национальное управление безопасности дорожного движения (NHTSA) требует соблюдения следующих требований (пункт 6.6.2.3, взятый из UNECE46):
S5.5.3 Время отклика. Изображение с камеры заднего вида, соответствующее требованиям S5.5.1 (Поле зрения) и S5.5.2 (Размер), при тестировании в соответствии с S14.2, отображается в течение 2,0 секунд с начала движения задним ходом.
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 градуса Цельсия ± 5 градусов Цельсия.
S6.2.2.3.4.3 Системная задержка. Система видеонаблюдения (CMS) имеет достаточно малую задержку, чтобы отображать сцену практически одновременно. Задержка составляет менее 200 мс при температуре 22 градуса Цельсия ± 5 градусов Цельсия.
Для соответствия этим требованиям на устройствах AAOS без операционной системы мы внедрили систему расширенного просмотра (EVS) Android Automotive OS (AAOS) . Аналогичный сервис для виртуализации на устройствах AAOS с высокодоступным рендерером (HAR) также демонстрирует соответствие этим требованиям.
Конвейер предварительного просмотра камеры
Эти пять этапов составляют конвейер предварительного просмотра камеры:

Рисунок 1. Этапы конвейера предварительного просмотра камеры.
Блок «Сервис камеры» относится к платформе «Сервис камеры» и ее уровню абстракции, который позволяет приложениям получать доступ к доступным камерам и использовать их данные. Функция «Сервис отображения» визуализирует данные изображений для пользователей. Приложение реализует целевые сценарии взаимодействия пользователя с помощью «Сервиса камеры» и «Сервиса отображения».
Основной сценарий использования системы обеспечения обзора сзади выглядит следующим образом:
Водитель переводит рычаг переключения передач в положение «Задний ход», чтобы инициировать движение задним ходом.
Система передает событие, отвечающее за обработку данных. Приложение получает это событие и инициализирует блок ввода с камеры (служба камеры) и средство отрисовки (служба отображения).
Блок ввода данных с камеры инициализирует платформу службы камеры и возвращает дескриптор службы приложению.
Рендерер инициализирует окно просмотра для входных данных с камеры, полученных на шаге 4.
Приложение запрашивает у блока ввода с камеры начало отправки буферов кадров и событий.
Приложение добавляет в очередь доставленные буферы кадров через функции обратного вызова (асинхронно). Буферы кадров принадлежат блоку ввода с камеры, поэтому приложение не может их изменять.
Приложение извлекает кадр из буфера (если очередь не пуста) и формирует представление пользователя. Пользователи могут создать копию для изменения содержимого.
Приложение отправляет буфер в средство отрисовки.
Рендерер отображает содержимое полученного буфера на экране.
Если процесс обработки события еще не завершен, перейдите к шагу 7. После завершения обработки события приложение запросит у блока ввода с камеры прекратить отправку буферов кадров и событий после скрытия изображения от пользователя.
Приложение может по желанию закрыть камеру и освободить средство отрисовки.
На рисунке 1 показана схема работы. На этом изображении используются элементы API библиотеки камер QNX для работы с платформой Camera Service.

Рисунок 2. Основной пользовательский путь HAR.
Блок ввода данных с камеры объявляет три интерфейса:
CameraManagerобъявляет методы для управления камерами; например, приложение использует этот интерфейс для открытия (резервирования) целевого устройства камеры.CameraDeviceобъявляются методы для управления камерой, например, для запуска или остановки потока данных.CameraStreamListenerобъявляет единственный метод для приема различных событий от целевой камеры.
Дизайн
В этом разделе подробно описывается конструкция системы.
пользовательский опыт
Водитель может просмотреть изображение с задней камеры на дисплее приборной панели, когда включает заднюю передачу. Предварительный просмотр изображения с камеры прекращается, когда водитель выключает заднюю передачу.
Можно включить дополнительные сценарии взаимодействия пользователя с системой. Например, водитель может предварительно просмотреть область, невидимую в зеркалах заднего вида при включении указателя поворота.
Начать предварительный просмотр камеры
При использовании камер приложение перечисляет и оценивает доступные камеры, чтобы найти наиболее подходящую для конкретной цели. Например, для обеспечения обзора сзади приложение ищет в списке доступных камер камеру, которая показывает заднюю часть автомобиля.
Приложение оценивает это, анализируя характеристики каждой камеры, например, местоположение, направление объектива, частоту кадров, разрешение и формат выходного изображения. Если несколько камер имеют одинаковые требуемые характеристики, приложение может проверить дополнительные параметры, такие как поле зрения и фокусное расстояние.
На этом изображении показана последовательность действий для запуска предварительного просмотра камеры со статической конфигурацией камеры:

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

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

Рисунок 5. Обработка таймаута (зависания потока данных).
Блок ввода с камеры может сообщать о происшествиях, помимо зависания потока данных, и встраивать более подробную информацию в буферы. Производители оборудования могут использовать эти метаданные событий для обработки инцидентов на своей платформе.
Деятельность
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 не поддерживает фоновые приложения камеры. Поэтому индикатор конфиденциальности, информирующий пользователей о том, что камера ведет сбор данных, выходит за рамки данной области применения.