深入理解相机硬件抽象层

news2024/12/23 9:27:30

f1adc3753f3b9a145208ae5e48116bc9.gif

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、概览
二、Camera HIDL 接口
三 、Camera Provider 主程序
四、Camera HAL3 接口

一、概览

始于谷歌的Treble开源项目,基于接口与实现的分离的设计原则,谷歌加入了Camera Provider这一抽象层,该层作为一个独立进程存在于整个系统中,并且通过HIDL这一自定义语言成功地将Camera Hal Module从Camera Service中解耦出来,承担起了对Camera HAL的封装工作,纵观整个Android系统,对于Camera Provider而言,对上是通过HIDL接口负责与Camera Service的跨进程通信,对下通过标准的HAL3接口下发针对Camera的实际操作,这俨然是一个中央枢纽般的调配中心的角色,而事实上正是如此,由此看来,对Camera Provider的梳理变得尤为重要,接下来就以我个人理解出发来简单介绍下Camera Provider。

Camera Provider通过提供标准的HIDL接口给Camera Service进行调用,保持与Service的正常通信,其中谷歌将HIDL接口的定义直接暴露给平台厂商进行自定义实现,其中为了极大地减轻并降低开发者的工作量和开发难度,谷歌很好地封装了其跨进程实现细节,同样地,Camera Provider通过标准的HAL3接口,向下控制着具体的Camera HAL Module,而这个接口依然交由平台厂商负责去实现,而进程内部则通过简单的函数调用,将HIDL接口与HAL3接口完美的衔接起来,由此构成了Provider整体架构。

430acf66d0c69814694e5031d5083d6f.jpeg

程序员Android转于网络图片

由图中可以看出Camera Provider进程由两部分组成,一是运行在系统中的主程序通过提供了标准的HIDL接口保持了与Camera Service的跨进程通讯,二是为了进一步扩展其功能,通过dlopen方式加载了一系列So库,而其中就包括了实现了Camera HAL3接口的So库,而HAL3接口主要定义了主要用于实现图像控制的功能,其实现主要交由平台厂商或者开发者来完成,所以Camera HAL3 So库的实现各式各样,在高通平台上,这里的实现就是我们本文重点需要分析的CamX-CHI框架。

在开始梳理CamX-CHI之前,不防先从上到下,以接口为主线简单梳理下Camera Provider的各个部分:

二、Camera HIDL 接口

首先需要明确一个概念,就是HIDL是一种自定义语言,其核心是接口的定义,而谷歌为了使开发者将注意力落在接口的定义上而不是机制的实现上,主动封装了HIDL机制的实现细节,开发者只需要通过*.hal文件定义接口,填充接口内部实际的实现即可,接下来来看下具体定义的几个主要接口:

77dd0f115ec3f48867b6545707a4986c.jpeg

程序员Android转于网络图片

因为HIDL机制本身是跨进程通讯的,所以Camera Service本身通过HIDL接口获取的对象都会有Bn端和Bp端,分别代表了Binder两端,接下来为了方便理解,我们都省略掉Bn/Bp说法,直接用具体接口类代表,忽略跨进程两端的区别。
ICameraProvider.hal源码如下:

package android.hardware.camera.provider@2.4;

import ICameraProviderCallback;
import android.hardware.camera.common@1.0::types;
import android.hardware.camera.device@1.0::ICameraDevice;
import android.hardware.camera.device@3.2::ICameraDevice;

interface ICameraProvider {

    setCallback(ICameraProviderCallback callback) generates (Status status);

    getVendorTags() generates (Status status, vec<VendorTagSection> sections);

    getCameraIdList() generates (Status status, vec<string> cameraDeviceNames);

    isSetTorchModeSupported() generates (Status status, bool support);

    getCameraDeviceInterface_V1_x(string cameraDeviceName) generates (Status status, android.hardware.camera.device@1.0::ICameraDevice device);

    getCameraDeviceInterface_V3_x(string cameraDeviceName) generates (Status status, android.hardware.camera.device@3.2::ICameraDevice device);
};

该文件中定义了ICameraProvider接口类,由CameraProvider继承并实现,在Camera Provider启动的时候被实例化,主要接口如下:

  • getCameraDeviceInterface_V3_x: 该方法主要用于Camera Service获取ICameraDevice,通过该对象可以控制Camera 设备的诸如配置数据流、下发request等具体行为。

  • setCallback: 将Camera Service 实现的ICameraProviderCallback传入Camera Provider,一旦Provider有事件产生时便可以通过该对象通知Camera Service。

ICameraProviderCallback.hal源码如下:

package android.hardware.camera.provider@2.4;

import android.hardware.camera.common@1.0::types;

interface ICameraProviderCallback {

    cameraDeviceStatusChange(string cameraDeviceName, CameraDeviceStatus newStatus);

    torchModeStatusChange(string cameraDeviceName, TorchModeStatus newStatus);
};

该文件中定义了ICameraProviderCallback回调接口类,该接口由Camera Service 中的CameraProviderManager::ProviderInfo继承并实现,在Camera Service 启动的时候被实例化,通过调用ICameraProvider::setCallback接口注册到Camera Provider中,其主要接口如下:

  • cameraDeviceStatusChange: 将Camera 设备状态上传至Camera Service,状态由CameraDeviceStatus定义

ICameraDevice.hal源码如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;
import ICameraDeviceSession;
import ICameraDeviceCallback;

interface ICameraDevice {

    getResourceCost() generates (Status status, CameraResourceCost resourceCost);

    getCameraCharacteristics() generates (Status status, CameraMetadata cameraCharacteristics);

    setTorchMode(TorchMode mode) generates (Status status);

    open(ICameraDeviceCallback callback) generates (Status status, ICameraDeviceSession session);

    dumpState(handle fd);
 };

该文件中定义了ICameraDevice接口类,由CameraDevice::TrampolineDeviceInterface_3_2实现,其主要接口如下:

  • open: 用于创建一个Camera设备,并且将Camera Service中继承ICameraDeviceCallback并实现了相应接口的Camera3Device作为参数传入Provider中,供Provider上传事件或者图像数据。

  • getCameraCharacteristics:用于获取Camera设备的属性。

ICameraDeviceCallback.hal源码如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;

interface ICameraDeviceCallback {
    processCaptureResult(vec<CaptureResult> results);
    notify(vec<NotifyMsg> msgs);
};

该文件中定义了ICameraDeviceCallback接口类,由Camera Service中的Camera3Device继承并实现,通过调用ICameraDevice::open方法注册到Provider中,其主要接口如下:

  • processCaptureResult_3_4: 一旦有图像数据产生会通过调用该方法将数据以及meta data上传至Camera Service。

  • notify: 通过该方法上传事件至Camera Service中,比如shutter事件等。

ICameraDeviceSession.hal源码如下:

package android.hardware.camera.device@3.2;

import android.hardware.camera.common@1.0::types;

interface ICameraDeviceSession {

    constructDefaultRequestSettings(RequestTemplate type) generates (Status status, CameraMetadata requestTemplate);

    configureStreams(StreamConfiguration requestedConfiguration) generates (Status status, HalStreamConfiguration halConfiguration);

    processCaptureRequest(vec<CaptureRequest> requests, vec<BufferCache> cachesToRemove) generates (Status status, uint32_t numRequestProcessed);

    getCaptureRequestMetadataQueue() generates (fmq_sync<uint8_t> queue);

    getCaptureResultMetadataQueue() generates (fmq_sync<uint8_t> queue);

    flush() generates (Status status); close();
};

该文件中定义了ICameraDeviceSession接口类,由CameraDeviceSession::TrampolineSessionInterface_3_2继承并实现,其主要接口如下:

  • constructDefaultRequestSettings:用于创建默认的Request配置项。

  • configureStreams_3_5:用于配置数据流,其中包括了output buffer/Surface/图像格式大小等属性。

  • processCaptureRequest_3_4:下发request到Provider中,一个request对应着一次图像需求。

  • close: 关闭当前会话。

三 、Camera Provider 主程序

接下来进入到Provider内部去看看,整个进程是如何运转的,以下图为例进行分析:

b7bc516d3c69258b2971ea181c14efa2.jpeg

程序员Android转于网络图片

在系统初始化的时候,系统会去运行android.hardware.camera.provider@2.4-service_64程序启动Provider进程,并加入HW Service Manager中接受统一管理,在该过程中实例化了一个LegacyCameraProviderImpl_2_4对象,并在其构造函数中通过hw_get_module标准方法获取HAL的camera_module_t结构体,并将其存入CameraModule对象中,之后通过调用该camera_modult_t结构体的init方法初始化HAL Module,紧接着调用其get_number_of_camera方法获取当前HAL支持的Camera数量,最后通过调用其set_callbacks方法将LegcyCameraProviderImpl_2_4(LegcyCameraProviderImpl_2_4继承了camera_modult_callback_t)作为参数传入CamX-CHI中,接受来自CamX-CHI中的数据以及事件,当这一系列动作完成了之后,Camera Provider进程便一直便存在于系统中,监听着来自Camera Service的调用。

2d5dbe1d169ea734edb08e523a9472d5.jpeg

程序员Android转于网络图片

接下来以上图为例简单介绍下Provider中几个重要流程:

  • Camera Service通过调用ICameraProvider的getCameraDeviceInterface_v3_x接口获取ICameraDevice,在此过程中,Provider会去实例化一个CameraDevice对象,并且将之前存有camera_modult_t结构体的CameraModule对象传入CameraDevice中,这样就可以在CameraDevice内部通过CameraModule访问到camera_module_t的相关资源,然后将CameraDevice内部类TrampolineDeviceInterface_3_2(该类继承并实现了ICameraDevice接口)返回给Camera Service。

  • Camera Service通过之前获取的ICameraDevice,调用其open方法来打开Camera设备,接着在Provider中会去调用CameraDevice对象的open方法,在该方法内部会去调用camera_module_t结构体的open方法,从而获取到HAL部分的camera3_device_t结构体,紧接着Provider会实例化一个CameraDeviceSession对象,并且将刚才获取到的camera3_device_t结构体以参数的方式传入CameraDeviceSession中,在CameraDeviceSession的构造方法中又会调用CameraDeviceSession的initialize方法,在该方法内部又会去调用camera3_device_t结构体的ops内的initialize方法开始HAL部分的初始化工作,最后CameraDeviceSession对象被作为camera3_callback_ops的实现传入HAL,接收来自HAL的数据或者具体事件,当一切动作都完成后,Provider会将CameraDeviceSession::TrampolineSessionInterface_3_2(该类继承并实现了ICameraDeviceSession接口)对象通过HIDL回调的方法返回给Camera Service中。

  • Camera Service通过调用ICameraDevcieSession的configureStreams_3_5接口进行数据流的配置,在Provider中,最终会通过调用之前获取的camera3_device_t结构体内ops的configure_streams方法下发到HAL中进行处理。

  • Camera Service通过调用ICameraDevcieSession的processCaptureRequest_3_4接口下发request请求到Provider中,在Provider中,最终依然会通过调用获取的camera3_device_t结构体内ops中的process_capture_request方法将此次请求下发到HAL中进行处理。

从整个流程不难看出,这几个接口最终对应的是HAL3的接口,并且Provider并没有经过太多复杂的额外的处理。

四、Camera HAL3 接口

HAL硬件抽象层(Hardware Abstraction Layer),是谷歌开发的用于屏蔽底层硬件抽象出来的一个软件层, 每一个平台厂商可以将不开源的代码封装在这一层,仅仅提供二进制文件。
该层定义了自己的一套通用标准接口,平台厂商务必按照以下规则定义自己的Module:

  • 每一个硬件模块都通过hw_module_t来描述,具有固定的名字HMI

  • 每一个硬件模块都必须实现hw_module_t里面的open方法,用于打开硬件设备,并返回对应的操作接口集合

  • 硬件的操作接口集合使用hw_device_t 来描述,并可以通过自定义一个更大的包含hw_device_t的结构体来拓展硬件操作集合

其中代表硬件模块的是hw_module_t,对应的设备是通过hw_device_t来描述,这两者的定义如下:
hw_module_t/hw_device_t源码如下:

typedef struct hw_module_t {
    uint32_t tag; //HMI
    struct hw_module_methods_t* methods;
} hw_module_t;

typedef struct hw_module_methods_t {
    int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;

typedef struct hw_device_t {
    struct hw_module_t* module;
    int (*close)(struct hw_device_t* device);
} hw_device_t;

从上面的定义可以看出,主要是通过hw_module_t 代表了模块,通过其open方法用来打开一个设备,而该设备是用hw_device_t来表示,其中除了用来关闭设备的close方法外,并无其它方法,由此可见谷歌定义的HAL接口,并不能满足绝大部分HAL模块的需要,所以谷歌想出了一个比较好的解决方式,那便是将这两个基本结构嵌入到更大的结构体内部,同时在更大的结构内部定义了各自模块特有的方法,用于实现模块的功能,这样,一来对上保持了HAL的统一规范,二来也扩展了模块的功能。

基于上面的方式,谷歌便针对Camera 提出了HAL3接口,其中主要包括了用于代表一系列操作主体的结构体以及具体操作函数,接下来我们分别进行详细介绍:

1. 核心结构体解析

HAL3中主要定义了camera_module_t/camera3_device_t/camera3_stream_configuration/camera3_stream以及camera3_stream_buffer几个主要结构体。

其中camera_module_t以及camera3_device_t代码定义如下:

typedef struct camera_module {
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    nt (*set_callbacks)(const camera_module_callbacks_t *callbacks);
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
    int (*open_legacy)(const struct hw_module_t* module, const char* id, uint32_t halVersion, struct hw_device_t** device);
    int (*set_torch_mode)(const char* camera_id, bool enabled);
    int (*init)();
    int (*get_physical_camera_info)(int physical_camera_id, int (*is_stream_combination_supported)(int camera_id,const camera_stream_combination_t *streams);
    int (*is_stream_combination_supported)(int camera_id, const camera_stream_combination_t *streams);
    void (*notify_device_state_change)(uint64_t deviceState);
} camera_module_t;

typedef struct camera3_device {
    hw_device_t common;
    camera3_device_ops_t *ops; //拓展接口,Camera HAL3定义的标准接口
    void *priv;
} camera3_device_t;

由定义不难发现,camera_module_t包含了hw_module_t,主要用于表示Camera模块,其中定义了诸如get_number_of_cameras以及set_callbacks等扩展方法,而camera3_device_t包含了hw_device_t,主要用来表示Camera设备,其中定义了camera3_device_ops操作方法集合,用来实现正常获取图像数据以及控制Camera的功能。

结构体camera3_stream_configuration代码定义如下:

typedef struct camera3_stream_configuration {
    uint32_t num_streams;
    camera3_stream_t **streams;
    uint32_t operation_mode;
    const camera_metadata_t *session_parameters;
} camera3_stream_configuration_t;

该结构体主要用来代表配置的数据流列表,内部装有上层需要进行配置的数据流的指针,内部的定义简单介绍下:

  • num_streams: 代表了来自上层的数据流的数量,其中包括了output以及input stream。

  • streams: 是streams的指针数组,包括了至少一条output stream以及至多一条input stream。

  • operation_mode: 当前数据流的操作模式,该模式在camera3_stream_configuration_mode_t中被定义,HAL通过这个参数可以针对streams做不同的设置。

  • session_parameters: 该参数可以作为缺省参数,直接设置为NULL即可,CAMERA_DEVICE_API_VERSION_3_5以上的版本才支持。

结构体camera3_stream_t的代码定义如下:

typedef struct camera3_stream {
    int stream_type;
    uint32_t width;
    uint32_t height;
    int format;
    uint32_t usage;
    uint32_t max_buffers;
    void *priv;
    android_dataspace_t data_space;
    int rotation;
    const char* physical_camera_id;
}camera3_stream_t;

该结构体主要用来代表具体的数据流实体,在整个的配置过程中,需要在上层进行填充,当下发到HAL中后,HAL会针对其中的各项属性进行配置,这里便简单介绍下其内部的各个元素的意义:

  • stream_type: 表示数据流的类型,类型在camera3_stream_type_t中被定义。

  • width: 表示当前数据流中的buffer的宽度。

  • height: 表示当前数据流中buffer的高度。

  • format: 表示当前数据流中buffer的格式,该格式是在system/core/include/system/graphics.h中被定义。

  • usage: 表示当前数据流的gralloc用法,其用法定义在gralloc.h中。

  • max_buffers: 指定了当前数据流中可能支持的最大数据buffer数量。

  • data_space: 指定了当前数据流buffer中存储的图像数据的颜色空间。

  • rotation:指定了当前数据流的输出buffer的旋转角度,其角度的定义在camera3_stream_rotation_t中,该参数由Camera Service进行设置,必须在HAL中进行设置,该参数对于input stream并没有效果。

  • physical_camera_id: 指定了当前数据流从属的物理camera Id。

结构体camera3_stream_buffer_t定义如下:

typedef struct camera3_stream_buffer {
    camera3_stream_t *stream;
    buffer_handle_t *buffer
    int status;
    int acquire_fence;
    int release_fence;
} camera3_stream_buffer_t;

该结构体主要用来代表具体的buffer对象,其中重要元素如下:

  • stream: 代表了从属的数据流

  • buffer:buffer句柄

2. 核心接口函数解析

HAL3的核心接口都是在camera3_device_ops中被定义,代码定义如下:

typedef struct camera3_device_ops {
    int (*initialize)(const struct camera3_device *, const camera3_callback_ops_t *callback_ops);
    int (*configure_streams)(const struct camera3_device *, camera3_stream_configuration_t *stream_list);
    int (*register_stream_buffers)(const struct camera3_device *,const camera3_stream_buffer_set_t *buffer_set);
    const camera_metadata_t* (*construct_default_request_settings)(const struct camera3_device *, int type);
    int (*process_capture_request)(const struct camera3_device *, camera3_capture_request_t *request);
    void (*get_metadata_vendor_tag_ops)(const struct camera3_device*, vendor_tag_query_ops_t* ops);
    void (*dump)(const struct camera3_device *, int fd);
    int (*flush)(const struct camera3_device *);
    void (*signal_stream_flush)(const struct camera3_device*, uint32_t num_streams, const camera3_stream_t* const* streams);
    int (*is_reconfiguration_required)(const struct camera3_device*, const camera_metadata_t* old_session_params, const camera_metadata_t* new_session_params);
} camera3_device_ops_t;

从代码中可以看见,该结构体定义了一系列的函数指针,用来指向平台厂商实际的实现方法,接下来就其中几个方法简单介绍下:

a) initialize
该方法必须在camera_module_t中的open方法之后,其它camera3_device_ops中方法之前被调用,主要用来将上层实现的回调方法注册到HAL中,并且根据需要在该方法中加入自定义的一些初始化操作,另外,谷歌针对该方法在性能方面也有严格的限制,该方法需要在5ms内返回,最长不能超过10ms。

b) configure_streams
该方法在完成initialize方法之后,在调用process_capture_request方法之前被调用,主要用于重设当前正在运行的Pipeline以及设置新的输入输出流,其中它会将stream_list中的新的数据流替换之前配置的数据流。在调用该方法之前必须确保没有新的request下发并且当前request的动作已经完成,否则会引起无法预测的错误。一旦HAL调用了该方法,则必须在内部配置好满足当前数据流配置的帧率,确保这个流程的运行的顺畅性。
其中包含了两个参数,分别是camera3_device以及stream_list(camera3_stream_configuration_t ),其中第二个参数是上层传入的数据流配置列表,该列表中必须包含至少一个output stream,同时至多包含一个input stream。
另外,谷歌针对该方法有着严格的性能要求,平台厂商在实现该方法的时候,需要在500ms内返回,最长不能超过1000ms。

c) construct_default_request_settings
该方法主要用于构建一系列默认的Camera Usecase的capture 设置项,通过camera_metadata_t来进行描述,其中返回值是一个camera_metadata_t指针,其指向的内存地址是由HAL来进行维护的,同样地,该方法需要在1ms内返回,最长不能超过5ms。

d) process_capture_request
该方法用于下发单次新的capture request到HAL中, 上层必须保证该方法的调用都是在一个线程中完成,而且该方法是异步的,同时其结果并不是通过返回值给到上层,而是通过HAL调用另一个接口process_capture_result()来将结果返回给上层的,在使用的过程中,通过in-flight机制,保证短时间内下发足够多的request,从而满足帧率要求。

该方法的性能依然受到谷歌的严格要求,规定其需要在一帧图像处理完的时长内返回,最长不超过4帧图像处理完成的时长,比如当前预览帧率是30帧,则该方法的操作耗时最长不能超过120ms,否则便会引起明显的帧抖动,从而影响用户体验。

e) dump
该方法用于打印当前Camera设备的状态,一般是由上层通过dumpsys工具输出debug dump信息或者主动抓取bugreport的时候被调用,该方法必须是非阻塞实现,同时需要保证在1ms内返回,最长不能超过10ms。

f) flush
当上层需要执行新的configure_streams的时候,需要调用该方法去尽可能快地清除掉当前已经在处理中的或者即将处理的任务,为配置数据流提供一个相对稳定的环境,其具体工作如下:

  • 所有的还在流转的request会尽可能快的返回

  • 并未开始进行流转的request会直接返回,并携带错误信息

  • 任何可以打断的硬件操作会立即被停止

  • 任何无法进行打断的硬件操作会在当前状态下进行休眠

flush会在所有的buffer都得以释放,所有request都成功返回后才真正返回,该方法需要在100ms内返回,最长不能超过1000ms。

上面的一系列方法是上层直接对下控制Camera Hal,而一旦Camera Hal产生了数据或者事件的时候,可以通过camera3_callback_ops中定义的回调方法将数据或者事件返回至上层,该结构体定义如下:

typedef struct camera3_callback_ops {

    void (*process_capture_result)(const struct camera3_callback_ops *, const camera3_capture_result_t *result);
    void (*notify)(const struct camera3_callback_ops *, const camera3_notify_msg_t *msg);

    camera3_buffer_request_status_t (*request_stream_buffers)(
        const struct camera3_callback_ops *,
        uint32_t num_buffer_reqs,
        const camera3_buffer_request_t *buffer_reqs,
        /*out*/uint32_t *num_returned_buf_reqs,
        /*out*/camera3_stream_buffer_ret_t *returned_buf_reqs);

    void (*return_stream_buffers)( const struct camera3_callback_ops *, uint32_t num_buffers, const camera3_stream_buffer_t* const* buffers);
} camera3_callback_ops_t;

其中常用的回调方法主要有两个:用于返回数据的process_capture_result以及用于返回事件的notify,接下来分别介绍下:

a) process_capture_result
该方法用于返回HAL部分产生的metadata和image buffers,它与request是多对一的关系,同一个request,可能会对应到多个result,比如可以通过调用一次该方法用于返回metadata以及低分辨率的图像数据,再调用一次该方法用于返回jpeg格式的拍照数据,而这两次调用时对应于同一个process_capture_request动作。

同一个Request的Metadata以及Image Buffers的先后顺序无关紧要,但是同一个数据流的不同Request之间的Result必须严格按照Request的下发先后顺序进行依次返回的,如若不然,会导致图像数据显示出现顺序错乱的情况。

该方法是非阻塞的,而且并且必须要在5ms内返回。

b) notify
该方法用于异步返回HAL事件到上层,必须非阻塞实现,而且要在5ms内返回。

谷歌为了将系统框架和平台厂商的自定义部分相分离,在Android上推出了Treble项目,该项目直接将平台厂商的实现部分放入vendor分区中进行管理,进而与system分区保持隔离,这样便可以在相互独立的空间中进行各自的迭代升级,而互不干扰,而在相机框架体系中,便将Camera HAL Module从Camera Service中解耦出来,放入独立进程Camera Provider中进行管理,而为了更好的进行跨进程访问,谷歌针对Provider提出了HIDL机制用于Camera Servic对于Camera Provier的访问,而HIDL接口的实现是在Camera Provider中实现,针对Camera HAL Module的控制又是通过谷歌制定的Camera HAL3接口来完成,所以由此看来,Provider的职责也比较简单,通过HIDL机制保持与Camera Service的通信,通过HAL3接口控制着Camera HAL Module。

原文链接:https://blog.csdn.net/u012596975/article/details/107137523

【腾讯文档】Camera学习知识库
https://docs.qq.com/doc/DSWZ6dUlNemtUWndv

友情推荐:

Android 开发干货集锦

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

6068b7b0a85605bd8f7cb78758a95a2b.jpeg

点击阅读原文,为大佬点赞!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/644972.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AUTOSAR】UDS协议的代码分析与解读(二)----ECU诊断协议概述

UDSO诊断服务技术规范 1 范围 本规范规定了增强型诊断需求的诊断服务部分的内容&#xff0c;定义了通用电子系统需遵循的UDS通用执行 规则。 本规范适用于集团x事业部所有平台车型&#xff0c;所有电子控 制单元(ECU) 的诊断需求&#xff0c;均需按此规范执行。 本规范定义的…

Python神经网络编程学习笔记

文章目录 神经网络基本原理线性分类器学习率一个线性分类器的局限性逻辑AND、逻辑OR逻辑XOR 神经元sigmoid function的logistic function(逻辑函数) 多层神经元演示只有两层&#xff0c;每层两个神经元的神经网络的工作矩阵大法(点乘)使用矩阵乘法的三层神经网络示例反向传播误…

AJAX概述

1.1什么是AJAX. Ajax即AsynchronousJavascript And XML&#xff1a;异步数据回调。 使用Ajax技术网页应用能够快速地将更新呈现在用户界面上&#xff0c;不需要重载&#xff08;刷新&#xff09;整个页面【只刷新局部】&#xff0c;这使得程序能够更快地回应用户的操作。、 1…

H5U PLC EtherCAT总线伺服回原(6098H=3)

回原方式35请参看下面文章 汇川H5U PLC通过 EtherCAT总线控制伺服回原_RXXW_Dor的博客-CSDN博客大部分运动控制都会对机械回原点进行大篇幅讲解,也可以看出机械回原点的重要性。常规的回原点方式大概有几十种吧,本文会给出常用回原点的注意事项,和编程推荐写法。如果原点回…

二层和三层交换机到底有啥区别?二者如何切换?

概要 计算机网络中的交换机是用于在局域网&#xff08;LAN&#xff09;中转发数据包的重要设备。其中&#xff0c;二层交换机和三层交换机是两种常见的交换机类型。本文将详细介绍二层交换机和三层交换机的特点、工作原理、各自的优缺点以及在思科、华为、瞻博网络三家厂商如何…

互联网架构师联合总结的 Java 面试攻略,GitHub 标星 30K!

2023 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;一直以来我都想整理一套完美的面试宝典&#xff0c;奈何难抽出时间&#xff0c;这套 1000道的 Java 面试手册是行业内各大神联合总结出来的&#xff0c;上传到 Git 上目前 star 数达到了 30K 这套互…

金九银十Java面试八股文汇总(2023最新整理)

前言 这才刚刚进入 6 月&#xff0c;我就看到了许多朋友在焦急的准备“金九银十”跳槽面试&#xff0c;甚至很多即将毕业的大学生都在备战秋招&#xff0c;对于学历还算优秀的大学生来说&#xff0c;这是一次离大厂最近的机会&#xff0c;毕竟是应届毕业生&#xff0c;不会对技…

LatticeXP2深力科 LFXP2-8E-6TN144I 灵活的flexiFLASH架构 应用笔记介绍

LatticeXP2深力科 LFXP2-8E-6TN144I 是一款瞬时上电、安全、小尺寸的FPGA&#xff0c;具有多功能的开发平台,采用flexiFLASH™架构&#xff0c;结合了一个基于FPGA基本结构的 4输入查找表&#xff08;LUT&#xff09;以及用于设计数据片上存储的闪存非易失性单元。flexiFLASH架…

Triton教程 -- 利用Triton部署你自己的模型

Triton教程—利用Triton部署你自己的模型 给定一个经过训练的模型&#xff0c;我如何使用 Triton 推理服务器以最佳配置大规模部署它&#xff1f; 本文档旨在帮助回答这个问题。 对于那些喜欢高级概述的人&#xff0c;下面是大多数用例的通用流程。 对于那些希望直接进入的人…

ceph分布式存储实战

ceph分布式存储实战 分布式存储系统简介 性能与优势对比 虚拟机安装ceph集群 ceph存储系统简介 分布式存储概述 ceph基础 高可用ceph分布式存储系统部署 部署ceph集群 节点管理 ceph使用基础及数据存储案例 PG状态、数据读写流程及存储池操作 mon服务器的高可用: # apt in…

天线设计中的磁介质材料 探索可重构潜力

​from&#xff1a;IEEE Antennas & Propagation Magazine (Vol. 61 / No. 1 / Feb. 2019, pp:29-40) -- 文 前 -- 这篇文章针对铁氧体在外置磁场下磁导率发生变化这个特点&#xff0c;探讨铁氧体在可重构天线中的应用。文中对铁氧体材料的选择&#xff0c;磁导率数学模型…

C++ Builder XE AdvStringGrid的表格画线显示隐藏经典实例

源码实例下载&#xff1a;https://download.csdn.net/download/lzksword/87904787 //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" //----------------------------…

linuxOPS基础_yum详解

yum是如何安装软件的 yum仓库&#xff08;也称yum源&#xff09;用于存放各种rpm的软件包以及软件包之间的依赖关系&#xff08;repodata目录&#xff09;需要安装软件的计算机连接到指定yum仓库来安装软件包 yum源作用 软件包管理器&#xff0c;类似Windows下的软件管家 yu…

屋大维和王莽,同时同路不同命的双雄

汉朝与罗马&#xff0c;虽然他们相隔万里分布在欧亚大陆东西两端&#xff0c;但他们几乎同时成为世界性文明&#xff0c;分别都是当时世界上东西方最强大的所在。公元元年前后&#xff0c;这两个庞然大物内部都发生了一次巨变&#xff0c;罗马由共和制变为了元首制。而汉朝也由…

ThreadLocal引发的内存泄漏分析

预备知识&#xff08;引用&#xff09; Object o new Object(); 这个o&#xff0c;我们可以称之为对象引用&#xff0c;而new Object()我们可以称之为在内存中产生了一个对象实例。 当写下 onull时&#xff0c;只是表示o不再指向堆中object的对象实例&#xff0c;不代表这个…

chatgpt赋能python:Python在SEO排名中的重要性

Python在SEO排名中的重要性 Python作为一种开源、高级程序设计语言&#xff0c;在Web开发、科学计算、人工智能等领域得到了广泛应用。它的发展速度快、可扩展性强、易于学习和使用等特点&#xff0c;使得Python越来越受到开发者的欢迎&#xff0c;成为重要的编程语言之一。在…

Question Log(★ > 使用VsCode构建Unity 开发环境 )

★ > 使用VsCode构建Unity 开发环境 配置Vscode Unity 环境 官方文档 Unity Development with VS Code 1.The .NET Core SDK cannot be located: A valid dotnet installation could not be found. .NET Core debugging will not be enabled. Make sure the .NET Core SDK …

CVE-2023-0215

mysql 安全漏洞 生产版本&#xff1a;5.5 漏洞编号&#xff1a; CVE编号 CVE-2023-0215 CNNVD编号 CNNVD-202302-521 另外还有以下其他漏洞&#xff1a; 解决办法&#xff1a; 1.下载补丁需要CSI,买了oracle服务才可能会有这个认证码&#xff1b; 2.没有这个认证码&#…

从reflect?metadata理解Nest实现原理

目录 正文入口Module 引入模块CatsService操作数据库Reflect Metadata那元数据存在哪呢&#xff1f;nest 的源码&#xff1a;总结 正文 Nest 是 Node.js 的服务端框架&#xff0c;它最出名的就是 IOC&#xff08;inverse of control&#xff09; 机制了&#xff0c;也就是不需…

Mocha Pro:Track 模块

Track&#xff08;跟踪&#xff09;模块中提供了几组选项&#xff0c;进行适当设置之后再实施跟踪&#xff0c;可以得到更好的跟踪结果。 ◆ ◆ ◆ 模块选项说明 Input 输入 Clip 剪辑 选择要跟踪的素材。 --Input 输入 --Layer Below 图层下方 Track Individual Fields 跟…