【树莓派】【v4l2】在树莓派环境下取流-编码-存储

news/2025/12/9 22:06:10/文章来源:https://www.cnblogs.com/Wangzx000/p/19328502

硬件环境:树莓派3B+

Camera模块:rpi Camera(500像素)

编码库:x264

工程代码

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <signal.h>
#include <errno.h>#include <time.h>
#include <x264.h>#define DEVICE "/dev/video0"
#define WIDTH 1920
#define HEIGHT 1080
#define BUF_COUNT 4
#define MAX_FRAMES 10  // 最多保存 10 帧后自动退出(可改为无限循环)static volatile int keep_running = 1;// 信号处理函数:响应 Ctrl+C
void signal_handler(int sig) {(void)sig;keep_running = 0;
}// 安全关闭资源
void cleanup_v4l2(int fd, void *mem[], size_t len[], int buf_count, enum v4l2_buf_type type) {if (fd >= 0) {ioctl(fd, VIDIOC_STREAMOFF, &type);  // 先停止流for (int i = 0; i < buf_count; ++i) {if (mem[i] != MAP_FAILED && mem[i] != NULL) {munmap(mem[i], len[i]);}}close(fd);}
}
/*** @brief 将一帧 YUYV 数据保存为 .yuyv 文件** @param data        指向帧数据的指针(来自 mmap 映射)* @param size        帧数据的实际字节数(dqbuf.bytesused)* @param frame_index 帧序号,用于生成文件名* @return int        成功返回 0,失败返回 -1* * @call  save_frame_to_file(mem[dqbuf.index], dqbuf.bytesused, frame_count);*/
int save_frame_to_file(const void *data, size_t size, int frame_index) {if (!data || size == 0) {fprintf(stderr, "Invalid frame data or size\n");return -1;}char filename[64];snprintf(filename, sizeof(filename), "frame_%03d.yuyv", frame_index);FILE *f = fopen(filename, "wb");if (!f) {perror("fopen frame file");return -1;}size_t written = fwrite(data, 1, size, f);fclose(f);if (written != size) {fprintf(stderr, "Warning: only %zu/%zu bytes written to %s\n", written, size, filename);return -1;}printf("Saved %s (%zu bytes)\n", filename, size);return 0;
}// ============ x264 封装模块 ============
typedef struct {x264_t      *encoder;FILE        *output_file;int          width;int          height;int64_t      frame_count;
} X264Encoder;X264Encoder* x264_encoder_create(int width, int height, const char *output_path) {X264Encoder *enc = calloc(1, sizeof(X264Encoder));if (!enc) return NULL;enc->width = width;enc->height = height;// 打开输出文件enc->output_file = fopen(output_path, "wb");if (!enc->output_file) {perror("fopen output file");free(enc);return NULL;}// 配置 x264 参数x264_param_t param;x264_param_default_preset(&param, "ultrafast", "zerolatency");param.i_width = width;param.i_height = height;param.i_fps_num = 30;param.i_fps_den = 1;param.i_keyint_max = 30;param.b_intra_refresh = 1;param.rc.i_bitrate = 2000;           // 2 Mbpsparam.rc.i_rc_method = X264_RC_ABR;param.i_threads = 1;param.b_repeat_headers = 1;param.i_level_idc = 40;x264_param_apply_profile(&param, "high");enc->encoder = x264_encoder_open(&param);if (!enc->encoder) {fprintf(stderr, "Failed to open x264 encoder\n");fclose(enc->output_file);free(enc);return NULL;}// 写入 SPS/PPSx264_nal_t *nals;int i_nals;x264_encoder_headers(enc->encoder, &nals, &i_nals);for (int i = 0; i < i_nals; i++) {fwrite(nals[i].p_payload, 1, nals[i].i_payload, enc->output_file);}return enc;
}int x264_encoder_encode_frame(X264Encoder *enc,unsigned char *y, unsigned char *u, unsigned char *v) {if (!enc || !enc->encoder || !y || !u || !v) return -1;x264_picture_t pic_in, pic_out;x264_picture_init(&pic_in);pic_in.img.i_csp = X264_CSP_I420;pic_in.img.i_plane = 3;pic_in.img.plane[0] = y;pic_in.img.plane[1] = u;pic_in.img.plane[2] = v;pic_in.img.i_stride[0] = enc->width;pic_in.img.i_stride[1] = enc->width / 2;pic_in.img.i_stride[2] = enc->width / 2;pic_in.i_pts = enc->frame_count++;x264_nal_t *nals;int i_nals;int result = x264_encoder_encode(enc->encoder, &nals, &i_nals, &pic_in, &pic_out);if (result < 0) {fprintf(stderr, "x264 encode error\n");return -1;} else if (result > 0) {for (int i = 0; i < i_nals; i++) {fwrite(nals[i].p_payload, 1, nals[i].i_payload, enc->output_file);}fflush(enc->output_file); // 可选:确保实时写入}return 0;
}void x264_enc_close(X264Encoder *enc) {if (!enc) return;// Flush encoderif (enc->encoder) {x264_picture_t pic_out;x264_nal_t *nals;int i_nals;x264_picture_t pic_flush;x264_picture_init(&pic_flush);while (x264_encoder_encode(enc->encoder, &nals, &i_nals, &pic_flush, &pic_out) > 0) {for (int i = 0; i < i_nals; i++) {fwrite(nals[i].p_payload, 1, nals[i].i_payload, enc->output_file);}}x264_encoder_close(enc->encoder);}if (enc->output_file) {fclose(enc->output_file);}free(enc);
}// YUYV 转 I420(关键!)
void yuyv_to_i420(const unsigned char *yuyv, unsigned char *y, unsigned char *u, unsigned char *v,int width, int height) {int uv_width = width / 2;int uv_height = height / 2;for (int i = 0; i < height; ++i) {for (int j = 0; j < width / 2; ++j) {int yuyv_idx = i * width * 2 + j * 4;int y_idx = i * width + j * 2;int u_idx = (i / 2) * uv_width + j;int v_idx = u_idx;// Y0 U0 Y1 V0unsigned char y0 = yuyv[yuyv_idx + 0];unsigned char u0 = yuyv[yuyv_idx + 1];unsigned char y1 = yuyv[yuyv_idx + 2];unsigned char v0 = yuyv[yuyv_idx + 3];y[y_idx + 0] = y0;y[y_idx + 1] = y1;if (i % 2 == 0) { // 只在偶数行写 UV(因为 I420 是 4:2:0)u[u_idx] = u0;v[v_idx] = v0;}}}
}int main() 
{int fd = -1;void *mem[BUF_COUNT];size_t len[BUF_COUNT];enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;// 初始化内存指针for (int i = 0; i < BUF_COUNT; ++i) {mem[i] = MAP_FAILED;}// 注册信号处理signal(SIGINT, signal_handler);// 打开设备fd = open(DEVICE, O_RDWR | O_NONBLOCK);  // 使用非阻塞模式更安全(配合 DQBUF 超时)if (fd < 0) {perror("open");return EXIT_FAILURE;}// 1.设置格式struct v4l2_format fmt = {0};fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;fmt.fmt.pix.field = V4L2_FIELD_NONE;if(ioctl(fd,VIDIOC_S_FMT,&fmt) < 0){perror("VIDIOC_S_FMT");cleanup_v4l2(fd,mem,len,BUF_COUNT,type);return EXIT_FAILURE;}// 向内核申请缓冲区struct v4l2_requestbuffers req = {0};req.count = BUF_COUNT;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if(ioctl(fd,VIDIOC_REQBUFS,&req) < 0){perror("VIDIOC_REQBUFS");cleanup_v4l2(fd,mem,len,BUF_COUNT,type);return EXIT_FAILURE;}// 映射并入队所有缓冲区for (int i = 0; i < BUF_COUNT; ++i) {// 1.枚举bufferstruct v4l2_buffer buf = {0};buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {perror("VIDIOC_QUERYBUF");cleanup_v4l2(fd, mem, len, BUF_COUNT, type);return EXIT_FAILURE;}// 映射buffer地址到mem[i],长度len[i]len[i] = buf.length;mem[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (mem[i] == MAP_FAILED) {perror("mmap");cleanup_v4l2(fd, mem, len, BUF_COUNT, type);return EXIT_FAILURE;}// 将映射好的buffer地址入队if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {perror("VIDIOC_QBUF");cleanup_v4l2(fd, mem, len, BUF_COUNT, type);return EXIT_FAILURE;}}// 开始出流if(ioctl(fd,VIDIOC_STREAMON,&type) < 0){perror("VIDIOC_STREAMON");cleanup_v4l2(fd, mem, len, BUF_COUNT, type);return EXIT_FAILURE;}printf("Streaming started. Press Ctrl+C to stop.\n");// 创建编码器(替代原来的 init_x264_encoder + fopen)X264Encoder *encoder = x264_encoder_create(WIDTH, HEIGHT, "output.h264");if (!encoder) {cleanup_v4l2(fd, mem, len, BUF_COUNT, type);return EXIT_FAILURE;}printf("Encoding started. Press Ctrl+C to stop.\n");// 分配 I420 缓冲区(不变)int y_size = WIDTH * HEIGHT;int uv_size = y_size / 4;unsigned char *y_plane = malloc(y_size);unsigned char *u_plane = malloc(uv_size);unsigned char *v_plane = malloc(uv_size);if (!y_plane || !u_plane || !v_plane) {fprintf(stderr, "Failed to allocate I420 buffers\n");goto cleanup;}int frame_count = 0;struct timespec last_time, current_time;clock_gettime(CLOCK_MONOTONIC, &last_time);while (keep_running) {struct v4l2_buffer dqbuf = {0};dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;dqbuf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_DQBUF, &dqbuf) < 0) {if (errno == EAGAIN || errno == EIO) {usleep(10000);continue;} else {perror("VIDIOC_DQBUF");break;}}// 转换 YUYV -> I420yuyv_to_i420((const unsigned char *)mem[dqbuf.index],y_plane, u_plane, v_plane,WIDTH, HEIGHT);// 编码一帧(调用封装函数)if (x264_encoder_encode_frame(encoder, y_plane, u_plane, v_plane) < 0) {fprintf(stderr, "Failed to encode frame %d\n", frame_count);break;}frame_count++;// 每秒计算并打印 FPSclock_gettime(CLOCK_MONOTONIC, &current_time);double elapsed = (current_time.tv_sec - last_time.tv_sec)+ (current_time.tv_nsec - last_time.tv_nsec) / 1e9;if (elapsed >= 1.0) {printf("FPS: %.2f (captured %d frames in %.2f seconds)\n", frame_count / elapsed, frame_count, elapsed);frame_count = 0;last_time = current_time;}if (ioctl(fd, VIDIOC_QBUF, &dqbuf) < 0) {perror("Re-queue buffer failed");break;}}cleanup:// 关闭编码器(自动 flush + fclose + free)x264_enc_close(encoder);// 释放 I420 缓冲区free(y_plane);free(u_plane);free(v_plane);printf("\nDone. Output saved to output.h264\n");// 清理 V4L2cleanup_v4l2(fd, mem, len, BUF_COUNT, type);printf("\nStopping capture...\n");printf("Done.\n");return EXIT_SUCCESS;}

设计原理

1.为什么要先将yuyv转换成I420格式,才能进行编码

x264 只接受特定的、planar(平面)YUV 格式作为输入,而 YUYV 是 packed(打包)格式,不被支持。

YUYV(也叫 YUY2)是一种 packed YUV 4:2:2 格式

存储方式

[Y0][U0][Y1][V0]  → 像素0 和 像素1 共享同一组 U/V

I420(或 YUV420 Planar)

存储方式

三个分量 完全分开存储 (planar)

[YYYYYYYY...]  // 所有 Y 分量(W×H 字节)
[UUUUUUUU...]  // 所有 U 分量(W/2 × H/2 字节)
[VVVVVVVV...]  // 所有 V 分量(W/2 × H/2 字节)

**x264 是一个高度优化的 H.264 编码器,其内部算法(如运动估计、DCT 变换、色度下采样)都假设输入是 **planar 格式。

可以考虑v4l2直接输出I420,这样就可以免去软件转码

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;  // 或 V4L2_PIX_FMT_NV12

2.x264编码器的必要步骤和输入输出参数

2.1 x264编码的必要步骤

1.配置编码参数(x264_param_t)

  • 设置分辨率、帧率、码率、预设(preset)、profile等
  • 使用x264_param_default_preset()初始化

2.打开编码器(x264_encoder_open)

  • 根据参数创建编码器实例(返回x264_t*)
  • 失败时返回NULL

3.获取并写入SPS/PPS(x264_encoder_headers)

  • SPS(Sequence Parameter Set)和 PPS(Picture Parameter Set)是 H.264 解码必需的元数据。
  • 必须在第一帧前写入输出流(尤其是用于文件或网络传输时)。

4.循环编码每一帧(x264_encoder_encode)

  • 输入:YUV帧(I420/NV12等planar格式)
  • 输出:NAL单元(如IDR帧,P帧等),写入文件或网络

5.刷新并关闭编码器(x264_encoder_encode+x264_encoder_close)

  • 发送空帧(pic_in=NULL)以flush延迟帧
  • 释放内部资源

2.2.输入参数 : x264_param_t

参数 说明 ---
i_width,i_height 视频宽高(必须是偶数) 1920 1080
i_fps_num,i_fps_den 帧率(分子/分母) 10,1->30fps
rc.i_bitrate 目标码率(kbps) 2000->2Mbps
rc.i_rc_method 码率控制方式 x264_RC_ABR(平均码率)
i_keyint_max 最大GOP长度(关键帧间隔) 30
b_repeat_headers 是否在每个关键帧重复SPS/PPS 1(推荐用于流媒体)
i_threads 编码线程数 0(自动)或1(低延迟)
psz_profile/x264_param_apply_profile Profile(baseline/main/high) "high"
x264_param_t param;
x264_param_default_preset(&param, "ultrafast", "zerolatency");
// 再覆盖自定义参数

2.3 输入图像:x264_picture_t

字段 说明
img.i_csp 色彩空间(必须是planar):X264_CSP_I420(YUV420P)X264_CSP_NV12(YUV420SP)
img.plane[0..2] 指向Y/U/V平面的指针
img.i_stride[0..2] 每个平面的stride(行字节数)
i_pts 显示时间戳(Presentation Time Stamp),用于同步
i_type 可强制帧类型(如x264_TYPE_IDR),通常设为 <span class="ne-text">0</span>让编码器自动决定

x264不接受packed格式(如:YUYV、RGB24)

2.4 输出结果:NAL单元

调用x264_encoder_encode()后,输出是一组NAL(Network Abstraction Layer)单元

x264_nal_t *nals;  // NAL 数组
int i_nals;        // NAL 数量int frame_size = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out);

NAL类型常见值

nal->i_type 名称 说明
5 IDR Slice 关键帧,可独立编码
1 Coded Slice P帧或B帧
7 SPS 序列参数集
8 PPS 图像参数集
6 SEI 补充增强信息(时间戳)

输出方式:

  • 写入文件:直接fwrite(nal->p_payload,1,nal->i_payload,file)
  • 网络传输:按RTP打包(需额外封装)
  • 播放:保存为.h264或封装进MP4/MKV

2.5 完整流程(伪代码)

// 1. 配置参数
x264_param_t param;
x264_param_default_preset(&param, "medium", "zerolatency");
param.i_width = 1920;
param.i_height = 1080;
param.i_fps_num = 30;
param.i_fps_den = 1;
param.rc.i_bitrate = 2000;          // 2000 kbps
param.rc.i_rc_method = X264_RC_ABR; // 平均码率控制
if (x264_param_apply_profile(&param, "high") < 0) {fprintf(stderr, "Failed to apply 'high' profile\n");return -1;
}// 2. 打开编码器
x264_t *encoder = x264_encoder_open(&param);
if (!encoder) {fprintf(stderr, "Failed to open x264 encoder\n");return -1;
}// 3. 打开输出文件
FILE *output_file = fopen("output.h264", "wb");
if (!output_file) {perror("fopen output.h264");x264_encoder_close(encoder);return -1;
}// 4. 获取并写入 SPS/PPS(headers)
x264_nal_t *headers;
int num_headers;
if (x264_encoder_headers(encoder, &headers, &num_headers) < 0) {fprintf(stderr, "Failed to get encoder headers\n");fclose(output_file);x264_encoder_close(encoder);return -1;
}
for (int i = 0; i < num_headers; i++) {fwrite(headers[i].p_payload, 1, headers[i].i_payload, output_file);
}
fflush(output_file); // 确保立即写入// 假设你有 I420 帧数据(这里用 malloc 模拟,实际应来自摄像头或图像)
int width = 1920, height = 1080;
size_t y_size = width * height;
size_t uv_size = y_size / 4;
unsigned char *y_plane = malloc(y_size);
unsigned char *u_plane = malloc(uv_size);
unsigned char *v_plane = malloc(uv_size);
if (!y_plane || !u_plane || !v_plane) {fprintf(stderr, "Failed to allocate frame buffers\n");goto cleanup;
}// 5. 编码每一帧(例如 100 帧)
for (int frame_idx = 0; frame_idx < 100; frame_idx++) {x264_picture_t pic_in, pic_out;x264_picture_init(&pic_in);// 填充 YUV 数据(此处假设已准备好)pic_in.img.i_csp = X264_CSP_I420;pic_in.img.i_plane = 3;pic_in.img.plane[0] = y_plane;pic_in.img.plane[1] = u_plane;pic_in.img.plane[2] = v_plane;pic_in.img.i_stride[0] = width;pic_in.img.i_stride[1] = width / 2;pic_in.img.i_stride[2] = width / 2;pic_in.i_pts = frame_idx; // 必须单调递增// 编码x264_nal_t *nals;int i_nals;int frame_size = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out);if (frame_size < 0) {fprintf(stderr, "Error encoding frame %d\n", frame_idx);break;}// 写入所有 NAL 单元到文件for (int i = 0; i < i_nals; i++) {fwrite(nals[i].p_payload, 1, nals[i].i_payload, output_file);}fflush(output_file); // 可选:实时写入(用于调试或流式保存)
}// 6. Flush 编码器(获取延迟帧)
x264_picture_t pic_flush;
x264_picture_init(&pic_flush); // 空输入表示 flush
x264_nal_t *nals;
int i_nals;
x264_picture_t pic_out;
while (x264_encoder_encode(encoder, &nals, &i_nals, &pic_flush, &pic_out) > 0) {for (int i = 0; i < i_nals; i++) {fwrite(nals[i].p_payload, 1, nals[i].i_payload, output_file);}
}cleanup:// 7. 清理资源free(y_plane);free(u_plane);free(v_plane);fclose(output_file);x264_encoder_close(encoder);printf("Encoding finished. Output saved to output.h264\n");

编译运行

# 编译c文件
gcc -o my_v4l2 my_v4l2.c -lx264 -lm
# 运行(可能需要权限)
sudo ./my_v4l2
pi@raspberrypi:~/Codes/V4l2 $ ./my_v4l2 
Streaming started. Press Ctrl+C to stop.
FPS: 30.64 (captured 31 frames in 1.01 seconds)   # 720p的帧数
FPS: 29.97 (captured 30 frames in 1.00 seconds)
FPS: 29.96 (captured 30 frames in 1.00 seconds)
FPS: 29.96 (captured 30 frames in 1.00 seconds)
FPS: 30.09 (captured 31 frames in 1.03 seconds)
FPS: 30.10 (captured 31 frames in 1.03 seconds)
FPS: 29.95 (captured 30 frames in 1.00 seconds)
FPS: 29.94 (captured 30 frames in 1.00 seconds)
FPS: 30.14 (captured 31 frames in 1.03 seconds)
FPS: 30.00 (captured 30 frames in 1.00 seconds)
^C
Stopping capture...
Done.
pi@raspberrypi:~/Codes/V4l2 $ gcc my_v4l2.c -o my_v4l2
pi@raspberrypi:~/Codes/V4l2 $ ./my_v4l2 
Streaming started. Press Ctrl+C to stop.
FPS: 4.95 (captured 5 frames in 1.01 seconds)    # 1080p的帧数
FPS: 5.73 (captured 6 frames in 1.05 seconds)
FPS: 5.72 (captured 6 frames in 1.05 seconds)并在目录下生成output.h264,可以直接用vlc打开浏览

输出验证

程序会生成一个文件:captured_frame.yuv

可以用ffplay查看:

ffplay -f rawvideo -pix_fmt yuyv422 -s 1920x1080 captured_frame.yuyv

或者转换成 MP4:

ffmpeg -f rawvideo -pix_fmt yuv420p -s 1920x1080 -r 25 -i captured_frame.yuv -c:v libx264 -pix_fmt yuv420p output.mp4

调试记录

1.运行代码总是报错

pi@raspberrypi:~/Codes/V4l2 $ ./v4l2_capture 
V4L2 Raspberry Pi Camera Capture Example
Resolution: 640x480, Format: YUYV (YUY2)
Driver: unicam
Card: unicam
Bus: platform:3f801000.csi
Version: 6.1.58
Actual: 640x480, fmt=YUYV, bpl=1280, size=614400, field=1
Buffer 0 mapped at address 0x7f894ea000 (614400 bytes)
Buffer 1 mapped at address 0x7f89454000 (614400 bytes)
Buffer 2 mapped at address 0x7f893be000 (614400 bytes)
Buffer 3 mapped at address 0x7f89328000 (614400 bytes)
VIDIOC_STREAMON failed: Invalid argument
errno = 22: Invalid argument - check format/field/buffer setup

使用vcgencmd get_camera,发现返回为不支持

supported=0 detected=0, libcamera interfaces=0

但是使用预览命令,是可以在小窗中预览实时视频流的

libcamera-vid -t 0 --width 1280 --height 720 --framerate 30 -o - --qt-preview

image

排除硬件故障

后发现是配置文件问题

sudo nano /boot/firmware/config.txt

把camera_auto_detect=1给注释掉,开启这个好像会自动使用新版本的摄像头驱动( libcamera ),注释完,在下面添加 start_x=1

# camera_auto_detect=1
start_x=1

重启树莓派,这时再使用V4l2和OpenCV,就可以发现正常了

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

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

相关文章

Daily Report — Day 4 (Beta)

Daily Report — Day 4 (Beta) 📅 日期:2025/12/05 👥 参与人:zc、lzy、shr ✅ 昨日完成工作(Day 3 落地成果)单元测试体系落地(CI 集成完成)完成 Jest + TS 环境配置,成功挂载至 GitHub Actions 流水线; …

[ABC241D] Sequence Query 题解

[ABC241D] Sequence Query 题解[ABC241D] Sequence Query Description 有一个空序列 \(a\)。给定 \(q\) 次操作,每次询问是以下三种之一:1 x:向 \(a\) 中插入元素 \(x\)。 2 x k:输出 \(a\) 中所有 \(\le x\) 的元…

Prometheus + Grafana 原理和用法

Prometheus + Grafana 原理和用法(通俗易懂版) 我们可以把这个组合想象成 「智能体检中心」:Prometheus = 体检医生:主动上门,定期采集服务器/应用的“健康数据”(CPU、内存、接口响应时间等),并把数据存起来。…

2025年度不锈钢板直销优质厂家TOP榜单盘点,不锈钢中厚板/201不锈钢板/不锈钢热轧板/不锈钢板现货批发哪家好 - 品牌推荐师

随着制造业的转型升级与基建投资的稳步推进,不锈钢板作为重要的工业与建筑原材料,其市场需求持续增长,对供应商的产品品质、供应能力及综合服务提出了更高要求。面对市场上众多的不锈钢板直销厂家,如何选择一家可靠…

12.09

今天上午没课下午上了一节英语

2025年市场技术好的不锈钢热轧板生产厂家怎么选择,304不锈钢冷热轧板材/316L不锈钢冷热轧板材定制加工有哪些 - 品牌推荐师

随着高端制造业的持续升级,不锈钢热轧板作为船舶、高铁、重型装备等领域的关键基础材料,其市场需求与技术门槛同步提升。面对市场上众多的供应商,如何甄选出技术实力扎实、产品稳定可靠的生产厂家,成为采购决策中的…

完整教程:浏览器工作原理大揭秘:从输入网址到看到页面的奇妙旅程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

什么是API?一文让你彻底搞明白! - 智慧园区

API作为软件应用程序间的通信桥梁,对产品经理至关重要。懂API能助您高效沟通、拓展产品能力,还可通过API盈利。本文解析API核心组成要素,助您在产品设计中巧妙运用。 什么是API? API,英文全称是Application Prog…

Troubleshooting一定要逻辑严谨与逻辑自洽

Troubleshooting一定要逻辑严谨与逻辑自洽2025-12-09 21:55 潇湘隐者 阅读(0) 评论(0) 收藏 举报Oracle数据库升级到19.28版本后,我们的监控就比较频繁收到一类告警邮件,提示告警日志中出现下面这类告警信息: 202…

企业微信相关文档

整理了企业微信开发相关资源文档,包括企业微信开发者官方文档、WxJava开源项目地址及其企业微信模块(weixin-java-cp)的API文档,以及开发者整理的在线文档,同时提供了企业微信管理后台登录入口。最近有点忙啊,工作…

实用指南:【鸿蒙生态共建】鸿蒙6适配-API变化与兼容(2.UI交互与基础能力篇)--《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2026考研政治肖秀荣 408真题教材 资料提供

考研的家人门,是时候背肖秀荣啦分享链接 1、肖秀荣: 链接: https://yun.139.com/shareweb/#/w/i/2rJXzofrVU6xu 提取码:svpg 2、408真题【2009-2025】 链接: https://yun.139.com/shareweb/#/w/i/2rJXAcj2C5xxz …

告别选择困难!SAT辅导机构大揭秘 - 品牌测评鉴赏家

告别选择困难!SAT辅导机构大揭秘选择 SAT 辅导机构,你真的了解这些吗? 准备踏上 SAT 备考征程的小伙伴们,是不是一打开网页,就被铺天盖地的辅导机构广告晃花了眼😵 “顶尖师资”“超高通过率”“独家秘籍”………

ubuntu docker运行大模型

安装docker 安装依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release 添加官方 Docker 源 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ub…

【自荐】OneClip—— 一款简单专业的 macOS 剪贴板管理工具

官网 :https://oneclip.cloud/ 目前已经开源了早期版本 下载地址+仓库地址:https://github.com/Wcowin/OneClip/releases 或者使用 Homebrew 安装 OneClip:# 1. 首次安装请先添加 tap(从 Gitee 拉取) brew tap W…

igbt模块的栅极驱动芯片,栅极电阻计算

以FS150R12KT4为例,针对该IGBT模块选择栅极驱动芯片,栅极电阻。 首先,已知驱动栅极驱动芯片的电源是15V,开关频率是15kHZ 设定上升时间和下降时间是0.3us(为什么这么设计的呢,因为开关频率是15kHZ,转换成周期T=…

托福一对一机构怎么选?高性价比推荐+避坑指南,2025备考党必看! - 品牌测评鉴赏家

托福一对一机构怎么选?高性价比推荐+避坑指南,2025备考党必看!一、托福备考痛点:为什么越来越多人选择一对一课程? (一)传统班课的局限性 在托福备考的赛道上,许多同学都曾在传统班课的浪潮中奋力前行,却发现…

构建高准确率、可控、符合规范的政务数据库审计和监测方案

一、概要 提示:本文旨在系统性阐述政务行业数据库风险监测的整体框架与实践成效,突出数据化治理与落地成果。在数字化政务全面推进的背景下,数据库已成为政府数据资产的核心载体与安全薄弱环节。“知形-数据库风险监…

疫苗的“设计图纸”如何变成现实?浅谈重组蛋白技术

想象一下,当一种新的病毒出现,科学家需要在最短的时间内为全人类打造一面“免疫盾牌”——这听起来像是一项不可能的任务。但现代生物技术的进步,尤其是重组蛋白技术,已经让这一过程变得空前高效与精准。这篇文章将…