图像读取
01.opencv读入数据
摄像头 cv:: VideoCapture()
视频 cv:: VideoCapture()
读取视频时可以传入视频路径,而读取摄像头传入的是index索引号
图像数据集cv:: cv2.imread()
02.摄像头挂载点
ls /dev/video*
或者 ls /dev | grep video
是否存在有效usb摄像头设备 dmesg | grep video
03.摄像头内核驱动与Linux内核
就是通过总线与硬件设备进行通信,控制硬件进去各种工作状态,获取器件相关寄存器的值,从而得到设备的状态
V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动
linux上的camera都 是按照v4l2框架来设计,它的地址位于Linux kernel
V4L2的核心源码位于 drivers/media/v4l2-core
V4L2支持三类设备:视频输入输出设备、VBI设备和radio设备
在 Linux中,硬件的驱动程序,都是由内核支持的
Linux内核是Linux系统的核心程序,主要完成任务调度、内存管理、IO设备管理等等功能
03.摄像头硬件结构和工作原理
摄像头引脚作用&硬件连接
摄像头模组,全称CameraCompact Module,简写为CCM。
CCM 包含四大件: 镜头(lens)、传感器(sensor)、软板(FPC)、图像处理芯片(DSP)
sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,
实现各种设备控制方法供上层调用并注册 v4l2_subdev
接口:
USB接口 UVC,全称 USB Video Class 、是 USB-IF 定制的一套标准
MIPI接口 MIPI(移动行业处理器接口)是Mobile Industry Processor Interface的
从Camera到ROS
通过用openCV来捕获视频流
在 cv_bridge::CvImage类中执行toImageMsg 将OpenCV图像转换为ROS消息的
发布ros消息
CvImage类中执行的OpenCV图像转换为ROS消息的成员函数为 toImageMsg
cv::Mat frame;
std_msgs::Header header;
header.seq = cnt;
header.stamp = ros::Time::now();
sensor_msgs::Image img_msg ;
cv_bridge::CvImage img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, frame);
img_bridge.toImageMsg(img_msg);
从ROS中到opencv图像
cv_bridge中的
Convert a sensor_msgs::Image message to an OpenCV-compatible CvImage, copying the image data.
toCvCopy 返回值是 CvImagePtr
toCvShare 返回值是 CvImageConstPtr
//toCvCopy函数会从ROS消息中拷贝一个图像数据
cv_bridge::CvImagePtr image_ptr = cv_bridge::toCvCopy(RGB_image, sensor_msgs::image_encodings::RGB8);
cv::Mat RGB_image= image_ptr->image;
其中
typedef boost::shared_ptr<CvImage> CvImagePtr;
typedef boost::shared_ptr<CvImage const> CvImageConstPtr;
ROS中cv_bridge源码中执行转换的类为 CvImage,该类包含了OpenCV的Mat格式的图像、该消息的encoding以及ROS header
CvImage
std_msgs::Header header; //!< ROS header
std::string encoding; //!< Image encoding ("mono8", "bgr8", etc.)
cv::Mat image;
发布和订阅
生产和消费 // 0.包含头文件 #include "ros/ros.h"
//1.初始化 ROS 节点 ros::init(argc,argv,"talker"); //参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
//2.实例化 ROS 句柄 ros::NodeHandle nh; //该类封装了 ROS 中的一些常用功能
//3.实例化 发布者 对象
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
std_msgs::String msg; + 构造消息== 组织被发布的数据,并编写逻辑发布数据
pub.publish(msg);
//3.实例化 订阅者 对象
ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
void doMsg(const std_msgs::String::ConstPtr& msg_p){ } 定义回调函数-处理订阅的消息(回调函数)
设置循环调用回调函数
image_transport 被用在image订阅和发布上 image_transport 默认的传输,通过ROS传输sensor_msgs/Image
其他通信类型
nh.advertiseService nh.serviceClient
显示器
opencv显示
cv2.imshow()
接口的物理标准和通讯协议
计算机显示接口
显示器的 VGA、DVI 、HDMI 和 DP 接口 USB Type-C接口-连接主机和显示器的外部显示接口
HDMI High-Definition Multimedia Interface 基于 TMDS 技术
DP(Display Port)接口是HDMI接口的竞争对手 基于 LVDS 技术
DP口定义了两种接头,全尺寸(Full Size)和迷你DP(Mini),两种接头皆是20针
VGA接口也称为D-Sub接口。在CRT显示器时代,VGA接口是必备的。因为CRT是模拟设备,而VGA采用的也是模拟协议
USB 3.1的接口来说,有Type-A、Type-B以及Type-C
Type C接口的好处在于可以正反插拔,部分Type C接口也同样具有数据、电力、信号传输的功能
电1和雷电2外形上采用的是Mini DP的外形,而雷电3采用的是USB Type-C的外形
大多数人来说网线可能只是用来连接网络的。但是其实网线还可以用给设备供电,如果采用HDBaseT协议甚至还能传送高清影音
家庭影音设备
模拟信号:复合信号Composite Video
模拟信号:分量信号Component Video
数字信号 DVI Digital Visual Interface:
示例代码
// 一、创建功能包 catkin_create_pkg communication std_msgs roscpp
// 二、编写功能包的代码
#include<iostream>
#include<vector>
#include<string>
#include<ros/ros.h> //包含了ros当中常用的API,订阅,发布,日志的输出
#include<sensor_msgs/Image.h>
#include<image_transport/image_transport.h>
#include<cv_bridge/cv_bridge.h>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
int main(int argc,char*argv[])
{
const std::string package_name = "get_camera";
ROS_INFO("Node read camera begin...");
// ROS节点初始化 argc argv和main函数保持一致 package_name 是节点名称
ros::init(argc,argv,package_name);
// 创建节点句柄
ros::NodeHandle nh("~");
std::cout<<"In main function"<<std::endl;
// 1.首先用openCV来捕获视频流
std::string video_name = "~/Data/show.mp4" ;
int camera_number = 0 ;
//VideoCapture 先实例化再初始化
cv::VideoCapture capture;
capture.open(video_name);
if(!capture.isOpened())
{
ROS_INFO("read video %s failed,please check...",video_name.c_str());
return -1;
}
//2.创建ROS发布者
image_transport::ImageTransport it(nh);
// 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String 1000是缓存队列长度
// ros::Publisher chatter_pub = nh.advertise<std_msgs::String>("chatter", 1000);
image_transport::Publisher img_pub_ = it.advertise("/camera_compressed",1);
// 设置循环的频率
ros::Rate r(30);
int cnt = 0;
cv::Mat img;
cv_bridge::CvImage img_bridge;
sensor_msgs::Image img_msg;
cv::Mat frame;
//循环发布消息
while( ros::ok() && capture.read(frame) )
{
// 循环等待回调函数 //查询一次回调函数是否有消息进来
ros::spinOnce();
ROS_INFO("img width = %d img height=%d ",frame.cols,frame.rows);
std_msgs::Header header;
header.seq = cnt;
header.stamp = ros::Time::now();
//通过CvImage类 在CvImage类中执行的OpenCV图像转换为ROS消息的成员函数为 toImageMsg()
img_bridge = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, frame);
img_bridge.toImageMsg(img_msg);
//ROS发送图片(Image)数据类型的消息(message)
// ros::Publisher pub_img = node.advertise<sensor_msgs::Image>("topic", queuesize);
img_pub_.publish(img_msg);
cv::imshow("img",frame);
cv::waitKey(1);
r.sleep();
ROS_INFO("publish img success frame = %d ",cnt);
cnt += 1;
}
capture.release();
return 0;
}
// 任何一个话题的发布和订阅,都会有其对应的发布缓存区和订阅缓存区
// ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter", 10, doMsg);
//一共有3个参数:订阅话题,回调函数队列长度,回调函数
// ros::spinOnce()时,会去话题订阅缓冲区中查看有没有回调函数,如果有则去处理回调函数,如果没有则继续往下执行
//ros::spin() 在调用后不会再返回,也就是你的主程序到这儿就不往下执行了 是一直调用;ros::spin()函数一般不会出现在循环中
// ros::spinOnce()是只调用一次, 在调用后还可以继续执行之后的程序 .如果还想再调用,就需要加上循环了 ros::spinOnce() 可以配合其它函数,一起放在while循环中处理
// 使用ros::spinOnce()而不是ros::spin()意味着你需要自己管理循环的控制和休眠。你需要根据实际情况调整循环的频率和休眠时间。
// catkin_make source ./devel/setup.bash rosrun 包名 C++节点
// 定义msg文件 2.编辑配置文件 编译后的中间文件查看: ++ 需要调用的中间文件(.../工作空间/devel/include/包名/xxx.h) 后续调用相关 msg 时,是从这些中间文件调用的
// 把消息发布与订阅封装成launch roslaunch task_topic task_topic_7.launch
// ROS中给出的解决方案是:Nodelet,通过Nodelet可以将多个节点集成进一个进程
参考:
cv_bridge用于ROS图像和OpenCV图像的转换 https://blog.csdn.net/bigdog_1027/article/details/79090571