usb摄像头应用编程
作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:IMX8MP,本专栏记录imx8mp开发板,学习开发过程中的问题及解决方法记录
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
usb摄像头应用编程
最近在京东上买了一个usb摄像头。想研究研究,应用到项目开发中。此文档记录我学习的过程。
1.V4L2简介
1.1 什么是v4l2
V4L2,即 Video for linux two ,是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范使用 V4L2 设备驱动框架注册的设备会在 Linux 系统/dev/目录下生成对应的设备节点文件,设备节点的名称通常为 videoX(X为0、1、2…)
V4L2是Linux视频处理模块的最新标准代码,包括对视频输入设备的处理,如高频(即、电视信号输入端子)或摄像头,还包括视频处理输出装置。一般来说,最常见的是使用V4L2来处理相机数据采集的问题。我们通常使用的相机实际上是一个图像传感器,将捕捉到的光线通过视频芯片处理后,编码成JPG/MJPG或YUV格式输出。我们可以很容易地通过V4L2与第一台摄像机设备“通信”,如设置或获取它们的工作参数。
1.2 Video设备的V4L2框架
基于Video设备的V4L2框架
Linux系统中视频设备主要包括以下四个部分:
1.字符设备驱动程序核心:V4L2本身就是一个字符设备,具有字符设备所有的特性,暴露接口给用户空间;用户空间可以通过Ioctl系统调用控制设备;在应用层,我们可以在 /dev 目录发现 videoxx 类似的设备节点。
2.V4L2驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数;
3.平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关V4L2驱动部分,包括注册video_device和v4l2_dev。
4.具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。
1.3 V4L2支持的设备类型
4L2支持的设备十分广泛,但是其中只有很少一部分在本质上是真正的视频设备:
video capture interface:视频采集接口,从camera上获取视频数据,视频捕获是V4L2的基本应用;
video output interface:视频输出接口,允许应用程序驱动外设提供视频图像
video overlay interface:视频覆盖接口,是捕获接口的一个变体,其工作是便于捕获设备直接显示到显示器,无需经过CPU;Android拍照应用在进行预览时,可能就是这种模式。
VBI interfaces:基于电视场消隐实现远程传送文字的技术与设备;
radio interface:无线电接口,从AM和FM调谐器设备访问音频流。
Codec Interface:编解码接口,对视频数据流执行转换。
通常情况下V4L2的主设备号是81,次设备号为0~255;这些次设备号里又分为多类设备:视频设备、Radio(收音机)设备、Teletext on VBI等。因此V4L2设备对应的文件节点有:/dev/videoX、/dev/vbiX、/dev/radioX。对于Radio设备,即用于收发声音。但要提醒注意的是,对于声音的采集与处理,在我们的Android手持设备中常会有个Mic设备,它则是属于ALSA子系统的。我们主要讨论的是Video设备。
2.V4L2-utils、ffplay工具测试usb摄像头
详细操作讲解请参考:摄像头应用测试
输入密令,录制视频
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=640,height=480,pixelformat=YUYV --stream-mmap --stream-to=video100.yuv --stream-count=100
将录制得到的video100.yuv拷贝到虚拟机上,利用ffplay工具播放
ffplay -video_size 640x480 -pixel_format yuyv422 -framerate 10 -i video100.yuv
usb摄像头视频采集
说明USB摄像头工作是正常的
3. V4L2接口编程
3.1 v4l2接口编程流程
- 首先是打开摄像头设备;
- 查询设备的属性或功能;
- 设置设备的参数,譬如像素格式、 帧大小、 帧率;
- 申请帧缓冲、 内存映射;
- 帧缓冲入队;
- 开启视频采集;
- 帧缓冲出队、对采集的数据进行处理;
- 处理完后,再次将帧缓冲入队,往复;
- 结束采集。
可以看到 V4L2 的应用编程的内容并不复杂,其实就是 open,ioctl,mmap,munmap,close 这几个调用,其中只有 ioctl 的使用稍微复杂。
VIDIOC_QUERYCAP // 查询设备属性和功能
VIDIOC_ENUM_FMT // 列举数据帧格式
VIDIOC_G_FMT // 获取数据帧格式
VIDIOC_S_FMT // 设置数据帧格式
VIDIOC_REQBUFS // 申请帧缓冲区
VIDIOC_QUERYBUF // 查询帧缓冲区
VIDIOC_STREAMON // 开始视频采集
VIDIOC_DQBUF // 帧缓冲出队
VIDIOC_QBUF // 帧缓冲入队
VIDIOC_STREAMOFF // 停止视频采集
3.2 v4l2视频采集原理
通过 V4L2 采集图像之前,我们需要做的很多,但是很重要的一步是分配帧缓冲区,并将分配的帧缓冲区从内核空间映射到用户空间,然后将申请到的帧缓冲区在视频采集输入队列排队,剩下的就是等待视频数据的到来。
其具体过程为,当启动视频采集后,驱动程序开始采集一帧图像数据,会把采集的图像数据放入视频采集输入队列的第一个帧缓冲区,一阵图像数据就算采集完成了。第一个帧缓冲区存满一帧图像数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出,应用程序取出图像数据可以对图像数据进行处理或存储操作,然后将帧该缓冲区放入视频采集输入队列的尾部。驱动程序接下来采集下一帧数据,放入第二个缓冲区,同样的帧缓冲区存满一帧数据后,驱动程序将该缓冲区移至视频采集输出队列,应用程序将该帧缓冲区的图像数据取出后又将该帧缓冲区放入视频输入队列尾部,这样循环往复就实现了循环采集。
不同的 USB 摄像头所支持的种类不同,所支持的种类数量也不相同,以本章的 USB 摄像头为例,可以查看摄像头支持的视频帧尺寸。
tjf@ubuntu16:~$ lsusb -vVideoStreaming Interface Descriptor:bLength 15bDescriptorType 36bDescriptorSubtype 1 (INPUT_HEADER)bNumFormats 2wTotalLength 365bEndPointAddress 130bmInfo 0bTerminalLink 3bStillCaptureMethod 1bTriggerSupport 0bTriggerUsage 0bControlSize 1bmaControls( 0) 11bmaControls( 1) 11VideoStreaming Interface Descriptor:bLength 11bDescriptorType 36bDescriptorSubtype 6 (FORMAT_MJPEG)bFormatIndex 1bNumFrameDescriptors 5bFlags 1Fixed-size samples: YesbDefaultFrameIndex 1bAspectRatioX 0bAspectRatioY 0bmInterlaceFlags 0x00Interlaced stream or variable: NoFields per frame: 1 fieldsField 1 first: NoField pattern: Field 1 onlybCopyProtect 0VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 7 (FRAME_MJPEG)bFrameIndex 1bmCapabilities 0x01Still image supportedwWidth 1920wHeight 1080dwMinBitRate 995328000dwMaxBitRate 995328000dwMaxVideoFrameBufferSize 4147200dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 7 (FRAME_MJPEG)bFrameIndex 2bmCapabilities 0x01Still image supportedwWidth 1280wHeight 720dwMinBitRate 442368000dwMaxBitRate 442368000dwMaxVideoFrameBufferSize 1843200dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 7 (FRAME_MJPEG)bFrameIndex 3bmCapabilities 0x01Still image supportedwWidth 800wHeight 600dwMinBitRate 230400000dwMaxBitRate 230400000dwMaxVideoFrameBufferSize 960000dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 7 (FRAME_MJPEG)bFrameIndex 4bmCapabilities 0x01Still image supportedwWidth 640wHeight 480dwMinBitRate 147456000dwMaxBitRate 147456000dwMaxVideoFrameBufferSize 614400dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 7 (FRAME_MJPEG)bFrameIndex 5bmCapabilities 0x01Still image supportedwWidth 640wHeight 360dwMinBitRate 110592000dwMaxBitRate 110592000dwMaxVideoFrameBufferSize 460800dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 6bDescriptorType 36bDescriptorSubtype 13 (COLORFORMAT)bColorPrimaries 1 (BT.709,sRGB)bTransferCharacteristics 1 (BT.709)bMatrixCoefficients 4 (SMPTE 170M (BT.601))VideoStreaming Interface Descriptor:bLength 27bDescriptorType 36bDescriptorSubtype 4 (FORMAT_UNCOMPRESSED)bFormatIndex 2bNumFrameDescriptors 5guidFormat {59555932-0000-1000-8000-00aa00389b71}bBitsPerPixel 16bDefaultFrameIndex 1bAspectRatioX 0bAspectRatioY 0bmInterlaceFlags 0x00Interlaced stream or variable: NoFields per frame: 2 fieldsField 1 first: NoField pattern: Field 1 onlybCopyProtect 0VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)bFrameIndex 1bmCapabilities 0x01Still image supportedwWidth 1920wHeight 1080dwMinBitRate 165888000dwMaxBitRate 165888000dwMaxVideoFrameBufferSize 4147200dwDefaultFrameInterval 2000000bFrameIntervalType 1dwFrameInterval( 0) 2000000VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)bFrameIndex 2bmCapabilities 0x01Still image supportedwWidth 1280wHeight 720dwMinBitRate 147456000dwMaxBitRate 147456000dwMaxVideoFrameBufferSize 1843200dwDefaultFrameInterval 1000000bFrameIntervalType 1dwFrameInterval( 0) 1000000VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)bFrameIndex 3bmCapabilities 0x01Still image supportedwWidth 800wHeight 600dwMinBitRate 153600000dwMaxBitRate 153600000dwMaxVideoFrameBufferSize 960000dwDefaultFrameInterval 500000bFrameIntervalType 1dwFrameInterval( 0) 500000VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)bFrameIndex 4bmCapabilities 0x01Still image supportedwWidth 640wHeight 480dwMinBitRate 147456000dwMaxBitRate 147456000dwMaxVideoFrameBufferSize 614400dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 30bDescriptorType 36bDescriptorSubtype 5 (FRAME_UNCOMPRESSED)bFrameIndex 5bmCapabilities 0x01Still image supportedwWidth 640wHeight 360dwMinBitRate 110592000dwMaxBitRate 110592000dwMaxVideoFrameBufferSize 460800dwDefaultFrameInterval 333333bFrameIntervalType 1dwFrameInterval( 0) 333333VideoStreaming Interface Descriptor:bLength 6bDescriptorType 36bDescriptorSubtype 13 (COLORFORMAT)bColorPrimaries 1 (BT.709,sRGB)bTransferCharacteristics 1 (BT.709)bMatrixCoefficients 4 (SMPTE 170M (BT.601))
可以看到打印的信息很多,我们需要找到 VideoStreaming Interface Descriptor 就是用于 USB 数据传输的接口,然后查看 wWidth 和 wHeight 这两个属性就可以知道摄像头所支持的视频帧的尺寸,本章选用的是 640 * 480 的视频帧大小。
3.3 v4l2设备操作流程
V4L2支持多种接口:capture(捕获)
、output(输出)
、overlay(预览)
等等
这里讲解如何使用capture
功能,下面讲解操作流程
step1:打开设备
在Linux中,视频设备节点为/dev/videox
,使用open函数将其打开
//1.打开设备int fd = open("/dev/video0",O_RDWR);if(fd < 0){perror("打开设备失败");return -1;}
step 2:获取摄像头支持的格式
有的摄像头支持多种像素格式,有的摄像头只支持一种像素格式,在设置格式之前,要先枚举出所有的格式,看一看是否支持要设置的格式,然后再进一步设置
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集int i =0;
while(1)
{v4fmt.index = i++;int ret = ioctl(fd,VIDIOC_ENUM_FMT,&v4fmt);if(ret < 0){perror("获取失败");break;} printf("index=%d\n",v4fmt.index);printf("flags=%d\n",v4fmt.flags);printf("description=%s\n",v4fmt.description);unsigned char *p = (unsigned char *)&v4fmt.pixelformat;printf("pixelformat=%c%c%c%c\n",p[0],p[1],p[2],p[3]);printf("reserved=%d\n",v4fmt.reserved[0]);
}
step 3:设置/获取采集格式
struct v4l2_format vfmt;vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集vfmt.fmt.pix.width = 640;//设置宽vfmt.fmt.pix.height = 480;//设置高vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);if(ret < 0){perror("设置格式失败");close(fd);return -1;}memset(&vfmt, 0, sizeof(vfmt));vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 确保type字段正确设置ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);if(ret < 0){perror("获取格式失败");close(fd);return -1;}if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG){printf("设置成功\n");}else{printf("设置失败\n");close(fd);return -1;}
step 4:申请内核空间
v4l2设备读取数据的方式有两种,一种是read方式,一种是streaming方式,具体需要看v4l2_capability是支持V4L2_CAP_READWRITE
还是V4L2_CAP_STREAMING
看一看v4l2_capability
struct v4l2_capability {__u8 driver[16]; /* i.e. "bttv" */__u8 card[32]; /* i.e. "Hauppauge WinTV" */__u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */__u32 version; /* should use KERNEL_VERSION() */__u32 capabilities; /* Device capabilities */__u32 reserved[4];
};
其中最重要的是capabilities
字段,这个字段标记着v4l2设备的功能,capabilities
有以下部分标记位
ID | 描述符 |
---|---|
V4L2_CAP_VIDEO_CAPTURE | 设备支持捕获功能 |
V4L2_CAP_VIDEO_OUTPUT | 设备支持输出功能 |
V4L2_CAP_VIDEO_OVERLAY | 设备支持预览功能 |
V4L2_CAP_STREAMING | 设备支持流读写 |
V4L2_CAP_READWRITE | 设备支持read、write方式读写 |
read方式很容易理解,就是通过read函数读取,那么streaming是什么意思呢?
streaming就是在内核空间中维护一个缓存队列
,然后将内存映射
到用户空间,应用读取图像数据就是一个不断地出队列
和入队列
的过程,如下图所示
struct v4l2_requestbuffers reqbuffer;reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;reqbuffer.count = 4;//申请4个缓冲区reqbuffer.memory = V4L2_MEMORY_MMAP;//映射方式ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);if(ret < 0){perror("申请队列空间失败");close(fd);return -1;}
step 5:映射内存
为什么要映射缓存?
因为如果使用read方式读取的话,图像数据是从内核空间拷贝会应用空间,而一副图像的数据一般来讲是比较大的,所以效率会比较低。而如果使用映射的方式,讲内核空间的内存应用到用户空间,那么用户空间读取数据就想在操作内存一样,不需要经过内核空间到用户空间的拷贝,大大提高效率
unsigned char *mptr[4];//保存映射后用户空间的首地址unsigned int size[4]; struct v4l2_buffer mapbuffer;//初始化type,indexmapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;for(int i=0; i<4; i++){mapbuffer.index = i;ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射if(ret < 0){perror("查询内核空间队列失败");close(fd);return -1;}mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset);size[i] = mapbuffer.length;//通知使用完毕--‘放回去’ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);if(ret < 0){perror("放回失败");close(fd);return -1;} }
step 6:开始采集
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ret = ioctl(fd, VIDIOC_STREAMON, &type);if(ret < 0){perror("开启失败");}
step 7:从队列中提取一帧数据
struct v4l2_buffer readbuffer;readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);if(ret < 0){perror("提取数据失败");}FILE *file=fopen("my.jpg","w+");fwrite(mptr[readbuffer.index],readbuffer.length,1,file);fclose(file);//通知内核已经使用完毕ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);if(ret < 0){perror("放回队列失败");return -1;}
step 8:停止采集
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);if(ret < 0){ perror("停止采集失败");return -1;}
step 9:释放映射
for(int i=0; i<4; i++){munmap(mptr[i],size[i]);}
step 10:关闭设备
close(fd);
完整代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>int main(void)
{//1.打开设备int fd = open("/dev/video0",O_RDWR);if(fd < 0){perror("打开设备失败");return -1;}//2.获取摄像头支持的格式struct v4l2_fmtdesc v4fmt;v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集int i =0;while(1){v4fmt.index = i++;int ret = ioctl(fd,VIDIOC_ENUM_FMT,&v4fmt);if(ret < 0){perror("获取失败");break;} printf("index=%d\n",v4fmt.index);printf("flags=%d\n",v4fmt.flags);printf("description=%s\n",v4fmt.description);unsigned char *p = (unsigned char *)&v4fmt.pixelformat;printf("pixelformat=%c%c%c%c\n",p[0],p[1],p[2],p[3]);printf("reserved=%d\n",v4fmt.reserved[0]); }//3设置采集格式struct v4l2_format vfmt;vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集vfmt.fmt.pix.width = 640;//设置宽vfmt.fmt.pix.height = 480;//设置高vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);if(ret < 0){perror("设置格式失败");close(fd);return -1;}memset(&vfmt, 0, sizeof(vfmt));vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 确保type字段正确设置ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);if(ret < 0){perror("获取格式失败");close(fd);return -1;}if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG){printf("设置成功\n");}else{printf("设置失败\n");close(fd);return -1;}//4.申请内核空间struct v4l2_requestbuffers reqbuffer;reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;reqbuffer.count = 4;//申请4个缓冲区reqbuffer.memory = V4L2_MEMORY_MMAP;//映射方式ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);if(ret < 0){perror("申请队列空间失败");close(fd);return -1;}//5.映射unsigned char *mptr[4];//保存映射后用户空间的首地址unsigned int size[4]; struct v4l2_buffer mapbuffer;//初始化type,indexmapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;for(int i=0; i<4; i++){mapbuffer.index = i;ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射if(ret < 0){perror("查询内核空间队列失败");close(fd);return -1;}mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset);size[i] = mapbuffer.length;//通知使用完毕--‘放回去’ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);if(ret < 0){perror("放回失败");close(fd);return -1;} }//6.开始采集int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ret = ioctl(fd, VIDIOC_STREAMON, &type);if(ret < 0){perror("开启失败");}//7.从队列中提取一帧数据struct v4l2_buffer readbuffer;readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);if(ret < 0){perror("提取数据失败");}FILE *file=fopen("my.jpg","w+");fwrite(mptr[readbuffer.index],readbuffer.length,1,file);fclose(file);//通知内核已经使用完毕ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);if(ret < 0){perror("放回队列失败");return -1;}//8.停止采集ret = ioctl(fd, VIDIOC_STREAMOFF, &type);if(ret < 0){ perror("停止采集失败");return -1;} //9.释放映射for(int i=0; i<4; i++){munmap(mptr[i],size[i]);}//10.关闭设备close(fd);return 0;}
4.运行测试
在ubuntu上编译程序,并运行
gcc -o video video.c
./video
可以看到测usb摄像头支持JPEG和YUYV 4:2:2 两种格式,并生成my.jpg格式图片
输入eog my.jpg
使用图像查看器来查看它
相关文章:
usb摄像头应用编程
作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习 擅长领域:驱动开发,嵌入式软件开发,BSP开发 作者主页:一个平凡而乐于分享的小比特的个人主页…...
康谋分享 | 自动驾驶联合仿真——功能模型接口FMI(一)
功能模型接口FMI(Functional Mock-up Interface)是一个开放且与工具解耦的标准。FMI包含了一个C-API(接口),一个用于描述接口的XML文件以及可交换的功能模型单元FMU(Functional Mock-up Unit)&a…...
OPenCV中绘制多条多边形曲线函数polylines的使用
操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:Visual Studio Code编程语言:C11 功能描述 绘制多条多边形曲线 原型1 void cv::polylines ( InputOutputArray img, InputArrayOfArrays pts, bool isClosed, const Scalar & color…...
气膜球幕影院:娱乐体验的新高度—轻空间
气膜球幕影院以其独特的全景沉浸体验和丰富的娱乐内容,成为了现代娱乐产业的重要组成部分。轻空间带您来探索一下气膜球幕影院带来的独特娱乐体验。 全景沉浸式体验 气膜球幕影院的360度全景沉浸式体验,彻底改变了传统观影方式。观众被包围在一个球形屏幕…...
阿里CEO个人投资的智驾公司,走了不一样的路
佑驾创新在去年8月和11月完成两轮融资,在今年5月底递表港交所,目前拿到了29家车企88款车型的量产订单。自动驾驶赛道不缺明星,这些因素本不足以凸显它的差异化。但是在招股书中,一条特殊的发展路线,却让佑驾创新显得不…...
Arduino平台软硬件原理及使用——无源蜂鸣器模块的使用
文章目录 一、蜂鸣器发声原理 二、无源蜂鸣器与有源蜂鸣器的区分 三、无源蜂鸣器模块在Arduino中的使用 一、蜂鸣器发声原理 上图为常见的不同封装及规格的蜂鸣器。 同蜜蜂、知了等昆虫发声原理一样,蜂鸣器同样靠振动来发出声音; 如上图为无源蜂鸣器的内…...
【Go】用 DBeaver、db browser 和 SqlCipher 读取 SqlCipher 数据库
本文档主要描述如何用 DBeaver、db browser 和 SqlCipher 上打开加密的 SQLite3 数据库(用 SqlCipher v3 加密) 软件版本 DBeaver:v24.1.0 SQLite-driver: sqlite-jdbc-3.46.0.0.jar dbbrowser-for-sqlite-cipher:3.12.2 SqlCipher cli(ubuntun)&am…...
ROS操作过程中的报错
文章目录 错误:E: Unable to locate package ros-noetic-desktop-full报错问题报错原因解决方法 错误2:ERROR: cannot download default source list from:报错问题错误原因解决办法 错误:E: Unable to locate package ros-noetic-desktop-fu…...
Qt项目学习-20240617
Qt项目学习 1.0 文件构建 1.1 预处理命令 C预处理命令是编译过程中的第一步,发生在编译器进行实际编译之前。预处理器(preprocessor)执行这些命令,它们不是C语言的一部分,但对源代码的编译过程至关重要。以下是一些常…...
加密好的WPSword文档,忘记密码怎么办?
在日常办公和学习中,我们经常使用WPS Word等文档处理软件来创建和编辑重要文件。为了保护这些文件不被未经授权的人访问,我们通常会选择给文档设置密码。然而,有时我们可能会因为时间久远或其他原因而忘记自己设置的密码,这时该如…...
C# WPF 读写CAN数据
C# WPF 读写CAN数据 CAN 分析仪 分析仪资料下载 官方地址:https://www.zhcxgd.com/1.html CSDN: 项目配置 复制Dll库文件 文件在上面的资料里面 设置不安全代码 CAN C#工具类 CAN_Tool.cs using Microsoft.VisualBasic; using System; using Sys…...
力扣2517.礼盒的最大甜蜜度
力扣2517.礼盒的最大甜蜜度 二分答案求最小值 排完序判断是否有k个差距至少为mid的元素别用i遍历 可能会越界 用 : 有多少取多少 class Solution {public:int maximumTastiness(vector<int>& price, int k) {ranges::sort(price);auto check [&](int mid) -&…...
多模块存储器
随着计算机技术的发展,处理的信息量越来越多,对存储器的速度和容量要求也越来越高;而且随着CPU性能的不断提高、IO设备数量不断增加,导致主存的存取速度已经称为了整个计算机系统的性能瓶颈。这就要求我们必须提高主存的访问速度。…...
Windows反截屏开发实现
文章目录 Windows反截屏开发实现1. SetWindowDisplayAffinity2. 反截屏系统3. 总结 Windows反截屏开发实现 最近在我们云桌面中需要做到反截屏能力,所谓反截屏就是我们无法通过截图软件(微信,QQ,截图等程序)截取桌面的…...
Android.mk的用法
前言 Android.mk 文件是 Android 编译系统中用于描述项目源文件、库和模块的 Makefile。它采用 GNU Make 的语法,但也包含了一些特定于 Android 编译系统的规则和变量。以下是对其语法和使用方法的详细解释及示例。 一:模块种类 一个Android.mk file用来向编译系统描述你的源…...
android studio CreateProcess error=2, 系统找不到指定的文件
【问题记录篇】 在AndroidStudio编译开发jni相关工程代码的时候,编译遇到的这个报错: CreateProcess error2, 系统找不到指定的文件。排查处理步骤: 先查看Build Output的具体日志输出 2.了解到问题出在了NDK配置上,此时需要根据自己的gra…...
jQuery如何把单选框设置为选中状态
在网页开发中,我们经常需要使用表单元素来收集用户数据。其中,单选框(radio button)是一种常见的表单元素,用于从一组选项中选择一个。使用jQuery,我们可以轻松地控制这些单选框的状态,包括将它…...
Mware Fusion Pro 13 mac版:一键掌控虚拟世界
VMware Fusion Pro 13是一款功能卓越的虚拟化软件,专为Mac操作系统量身打造。这款软件为用户提供了一个一站式的虚拟化解决方案,能够满足各种多样化的需求。 VMware Fusion Pro 13 Mac获取 VMware Fusion Pro 13的强大之处在于其采用了最 先进的虚拟化…...
PTA - 函数的定义与调用
编写一个名为collatz()的函数,它有一个名为number的参数: 如果number是偶数,那么collatz()就打印number加上2如果number是奇数,那么collatz()就打印number乘以2 函数接口定义: def collatz(number)裁判测试程序样例: /* 请在这里填写答案…...
Solr7.4.0报错org.apache.solr.common.SolrException
文章目录 org.apache.solr.common.SolrException: Exception writing document id MATERIAL-99598435990497269125316 to the index; possible analysis error: cannot change DocValues type from NUMERIC to SORTED_NUMERIC for field "opt_time"Exception writing…...
从2-3-4树开始理解红黑二叉树(JAVA代码手撸版)
经典的红黑二叉树在新增/删除数据时维持自平衡始终对应着一个2-3-4 树。本文只关注2-3-4 对应的经典红黑二叉树。 暂时不考虑 2-3 树对应的左倾红黑二叉树。 背景知识 2-3-4 树简介 一棵 2-3-4 树的结点分为 内部结点 (internal nodes) 和 叶子结点 (leaf nodes) ,…...
模板类与继承
1模板类继承普通类(常见) #include<iostream> using namespace std; class AA { public:int m_a;AA(int a) :m_a(a) { cout << "调用了AA的构造函数\n"; }void func1() { cout << "调用func1()…...
随手记:uniapp图片展示,剩余的堆叠
UI效果图: 实现思路: 循环图片数组,只展示几张宽度就为几张图片边距的宽度,剩下的图片直接堆叠展示 点击预览的时候传入当前的下标,如果是点击堆叠的话,下标从堆叠数量开始计算 <template><…...
微服务迁移、重构最佳经验
1. 了解现有的单体应用: - 应用架构和技术栈 要了解现有的应用架构和技术栈,可以采取以下几个步骤: 1. 了解应用的背景和目标:首先要了解应用的背景和目标,包括应用所属的行业、应用的类型(例如Web应用、移动应用等…...
【Python】从0开始的Django基础
Django框架基础 unit01一、Django基础1.1 什么是Django?1.2 安装与卸载1.2.1 Python与Django的版本1.2.2 安装1.2.3 查看Django版本1.2.4 卸载 二、Django项目2.1 概述2.2 创建项目2.3 启动项目2.4 项目的目录结构2.5 配置 三、URL 调度器3.2 定义URL路由3.2 定义首页的路由3.…...
红黑树(数据结构篇)
数据结构之红黑树 红黑树(RB-tree) 概念: 红黑树是AVL树的变种,它是每一个节点或者着成红色,或者着成黑色的一棵二叉查找树。对红黑树的操作在最坏情形下花费O(logN)时间,它的插入操作使用的是非递归形式实现红黑树的高度最多是…...
高级视频编码器性能对比(H265、VP9、AV1)
1、背景介绍 目前在视频编解码器中,H264已经成为绝对的主流,被大部分设备、浏览器所支持。虽然有更先进的编码器推出,但是受限于推广速度和设备支持成本,一直未能成为主流。 今年公司目标是持续降本增效,现在将”屠刀…...
示例:WPF中DataGrid简单设置合并列头
一、目的:应用DataGridTemplateColumn列模板,去拆分列头和单元格布局的方式设置列头合并样式 二、实现 效果如下 三、环境 VS2022 四、示例 应用DataGridTemplateColumn自定义列头信息和单元格信息 <DataGrid AutoGenerateColumns"False"…...
Matlab图像处理——细胞图像的分割和计数显示
一. 项目介绍 使用MATLAB编写的细胞图像分割及计数系统,实现了对图像内细胞的计数,以及对每个细胞周长和面积的测量,并分别展示了分割后的每个细胞的图像。实验步骤共分为图像预处理、图像预分割、空洞填充、黏连细胞分割、细胞个数统计、细胞…...
六爻排盘神机
选修课留了3000字的论文......确实,削微有那么一点小困难…… 但是,倘若我拿出已经占了6419个字符的 “六爻排盘神机” ,阁下…应该…不会…骂我吧 且看,六爻排盘神机! import random import datetime from lunarcale…...
免费外贸网站/cdq百度指数
一般程序写的爬虫程序都会自带请求头,不知不觉就被网站拒绝了,请求之前可以看看自己的请求头是什么,确保不被禁 地址:https://httpbin.org/ 如果网站太慢打不开,想在本地搭建测试环境,可以在docker环境下…...
温州seo优化网络推广/seo查询seo优化
通常我们在遇到简单问题的解决时,不可避免的会出现大大小小的问题。 这里对于通过在A函数中调用B函数修改自己的值,需要通过传指针,解引用进行值的修改。 可能出现以下几个问题: 1:正常思维,直接进行值的交…...
世界室内设计公司排名/郑州官网网站推广优化
-------------------siwuxie095 ResponseBody 使用须知 使用 ResponseBody 注解映射响应体 ResponseBody 注解可被应用于方法上,标志该方法的返回值 应该被直接写回到 HTTP 响应体中去(而不会被被放置到 Model 中 或 被解释为一个视图名) …...
电子商城网站建设/郑州百度推广外包
Python Python开发 Python语言 python 列表中[ ]中冒号‘:’的作用python中括号[ ]:用于定义列表或引用列表、数组、字符串及元组中元素位置 list1 [physics, chemistry, 1997, 2000] list2 [1, 2, 3, 4, 5, 6, 7] print"list1[0]:", list1[0…...
wordpress模拟论坛/网站优化及推广方案
1)PowerDesigner运行在Microsoft Windows平台上; 2)它支持60多种关系数据库管理系统(RDBMS)/版本; 3)它几乎包括了数据库模型设计的全过程;利用Power Designer可以制作数据流程图、概念数据模型、物理数据模型,还可以为数据仓库…...
一个企业是如何做网站建设的/网站建设找哪家好
RHCE考试Troubleshooting发布时间:2008-03-13 10:33:59来源:红联作者:xlsogb一、MBR错误由于意外原因,主引导记录被破坏,新手安装多操作时系统常发生先装linux再装windows类系统后只能引导win的情况。这种情况下,grub.conf或lilo.conf正常&am…...