【ROS】小车机器视觉巡线行驶

摄像头

USB摄像头是最普遍的摄像头,如笔记本内置的摄像头,在ROS中使用这类设备很简单,可以直接使用usb_cam功能包驱动,USB摄像头输出的是二维图像数据。
usb_cam是针对V4L协议USB摄像头的ROS驱动包,核心节点是usb_cam_nod
1、使用PC内置摄像头
安装usb_cam功能包

$ sudo apt-get install ros-melodic-usb-cam

运行

$ roslaunch usb_cam usb_cam-test.launch

报错:
在这里插入图片描述

ERROR: cannot launch node of type [image_view/image_view]: image_view
安装image-view

sudo apt-get install ros-kinetic-image-view

ERROR: Cannot identify ‘/dev/video0’: 2, No such file or directory
是因为虚拟机连接不上主机的摄像头
解决:
https://blog.csdn.net/qq_54253413/article/details/128599092
在这里插入图片描述

再次运行

$ roslaunch usb_cam usb_cam-test.launch

可以成功调用本地摄像头

2、调用外部USB摄像头
usb_cam安装,在工作空间中采用源代码安装:

$ cd catkin_ws/src  
$ git clone https://github.com/bosch-ros-pkg/usb_cam.git  
$ cd ..  
$ catkin_make  

报错:
在这里插入图片描述

问题:- No package ‘libv4l2’ found
解决:

sudo apt-get install libv4l-dev

在这里插入图片描述

进入下载的包,找到usb_cam-test.launch或robot_vision中usb_cam.launch文件,修改里面的内容video0改成video1,保存并退出,source一下。
在这里插入图片描述

<launch><node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" ><param name="video_device" value="/dev/video1" /><param name="image_width" value="640" /><param name="image_height" value="480" /><param name="pixel_format" value="yuyv" /><param name="camera_frame_id" value="usb_cam" /><param name="io_method" value="mmap"/></node><node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen"><remap from="image" to="/usb_cam/image_raw" /><param name="autosize" value="true" /></node>
</launch>
source devel/setup.bash

然后执行:

roslaunch usb_cam usb_cam-test.launch #开启摄像头
或
roslaunch robot_vision usb_cam.launch

OpenCV库

OpenCV库是一个基于BSD许可发行的跨平台开源计算机库,可以运行在Linux、Windows和mac OS等操作系统上。OpenCV由一系列C函数和少量C++类构成,同时提供C++、Python、Ruby、Matlab等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
ROS开发者提供了与OpenCV的接口功能包——cv_bridge。如下图所示,开发者可以通过该功能包将ROS中的图像数据转换成OpenCV格式的图像,并且调用OpenCV库进行各种图像处理;或者将OpenCV处理过后的数据转换成ROS图像,通过话题发布,实现各节点之间的图像传输。
在这里插入图片描述

ROS中已经集成了OpenCV库和相关的接口功能包,使用如下命令即可安装:

sudo apt-get install ros-melodic-vision-opencv libopencv-dev python-opencv

或在catkin_ws/src目录下下载robot_vision安装包

git clone https://github.com/1417265678/robot_vision.git
cd ..
catkin_make

在这里插入图片描述

测试cv_bridge_test样例

$ roslaunch robot_vision usb_cam.launch
重新开启终端
$ catkin_make
$ source ./devel/setup.bash
$ rosrun robot_vision cv_bridge_test.py
再开启一个终端
$ rqt_image_view
没有的话就安装一下
sudo apt-get install ros-melodic-rqt-image-view

该例程中,一个ROS节点订阅摄像头驱动发布的图像消息,然后将其转换成OpenCV的图像格式进行显示,然后再将该OpenCV格式的图像转换成ROS图像消息进行发布并显示。
运行效果如下图所示,图像左边是通过cv_bridge将ROS图像转换成OpenCV图像数据之后的显示效果,使用OpenCV库在图像的左上角绘制了一个红色的圆;图像右边是将OpenCV图像数据再次通过cv_bridge转换成ROS图像后的显示效果,左右两幅图像背景应该完全一致。
在这里插入图片描述

巡线代码

新建scout_control_demo2.cpp,设置成可执行文件

  • 代码
#include <ros/ros.h>
#include <geometry_msgs/TwistStamped.h>
#include <nav_msgs/Odometry.h>
#include <sensor_msgs/Image.h>#include <tf/transform_datatypes.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>//速度控制话题消息类型类型
geometry_msgs::TwistStamped cmd_speed;
//
cv_bridge::CvImagePtr cv_ptr;
//小车x,y方向速度
double linear_x;
double linear_y;// th
//转速
double angular_z;void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);       
}int main(int argc, char **argv)
{ros::init(argc, argv, "scout_control_demo2");ros::NodeHandle n;// 发布话题 "/cmd_vel_raw"ros::Publisher pub = n.advertise<geometry_msgs::TwistStamped>("/cmd_vel_raw", 5);// 订阅话题ros::Subscriber image_sub_ = n.subscribe("/usb_cam/image_raw", 1, imageCallback);//配合r.sleep控制循环频率ros::Rate r(50);// 设置底盘运动速度,初始前进linear_x = 0.0;angular_z = 0.0;linear_y = 0.0;// 运动状态标识符int tag = 0;while(ros::ok()){  cmd_speed.twist.linear.x = linear_x;cmd_speed.twist.linear.y = linear_y;// thcmd_speed.twist.angular.z = angular_z;cv::Mat image = cv_ptr -> image;cv::Mat hsv = image.clone();cv::Mat res = image.clone();cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);//取颜色cv::inRange(hsv, cv::Scalar(0, 0, 20), cv::Scalar(180, 255, 150), res);int h = image.rows;int w = image.cols;cv::Moments M = cv::moments(res);if(M.m00 > 0){int cx = int (cvRound(M.m10 / M.m00));int cy = int (cvRound(M.m01 / M.m00));ROS_INFO("cx: %d cy: %d", cx, cy);cv::circle(cv_ptr->image, cv::Point(cx, cy), 20, (0, 255, 0));int v = cx - w / 2;linear_x = 0.1;angular_z = -float(v) / 300 * 0.3;
//?//image_pub_.publish(cv_ptr->toImageMsg());pub.publish(cmd_speed);ROS_INFO("linearx: %F,angularz: %F",linear_x,angular_z);}else{ROS_INFO("not found line!");linear_x =0;angular_z =0.2;pub.publish(cmd_speed);}//当处理到ros::spinonce()时,会去话题订阅缓冲区中查看有没有回调函数,如果有则去处理回调函数,如果没有则继续往下执行ros::spinOnce();r.sleep();}return 0;
}
  • cv::inRange() 函数在 OpenCV 中用于确定图像中像素值在指定范围内的区域,并将这些像素标记为白色(255),其他像素标记为黑色(0),结果存储在 res 中。
  • 编译日常出错及解决
    在这里插入图片描述

CMakeList
链接Opencv库文件
在这里插入图片描述
在这里插入图片描述

添加cv_bridge
在这里插入图片描述

在这里插入图片描述

运行实验


sudo ip link set can0 up type can bitrate 500000
cd catkin_ws/
终端1) 运行scout底盘节点对应的launch文件
roslaunch scout_bringup scout_robot_base.launch
​终端2) 运行摄像头
source ./devel/setup.bash
roslaunch robot_vision usb_cam.launch
roslaunch usb_cam usb_cam-test.launch 
终端3) 运行我们编写的节点 
source ./devel/setup.bash
rosrun scout_base scout_control_demo2需要的话可以检查虚拟机连接的摄像仪编号
ls /dev/video*

报错:core dumped
在这里插入图片描述

问题分析:
在回调函数中处理图像时,赋值cv::Mat image = cv_ptr -> image;和其后的颜色转换步骤可能发生在图像显示之前,导致cv_ptr 可能为空值,产生了段错误(访问空指针)。
所以在对图像进行操作之前,需要确保它已经成功接收到并且不是空的。
这种并行处理也可能出问题:在回调函数中将图像显示在窗口中,并在回调函数之外尝试进行颜色空间转换。这样的处理方式可能导致在图像处理的同时,图像数据发生变化,从而导致颜色空间转换时出现问题。

问题解决:
将将颜色空间转换放到回调函数内部:
在这里插入图片描述

#include <ros/ros.h>
#include <geometry_msgs/TwistStamped.h>
#include <nav_msgs/Odometry.h>
#include <sensor_msgs/Image.h>#include <tf/transform_datatypes.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>//速度控制话题消息类型类型
geometry_msgs::TwistStamped cmd_speed;
//
cv_bridge::CvImagePtr cv_ptr;
//小车x,y方向速度
double linear_x;
double linear_y;// th
//转速
double angular_z;
//image
cv::Mat image;
cv::Mat hsv;
cv::Mat res;
int h;
int w;
cv::Moments M;void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{try{cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);}catch(cv_bridge::Exception &e){ROS_ERROR("cv_bridge exception %s", e.what());}image = cv_ptr->image;hsv = image.clone();res = image.clone();cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);//取颜色cv::inRange(hsv, cv::Scalar(0, 0, 20), cv::Scalar(180, 255, 150), res);h = image.rows;w = image.cols;M = cv::moments(res);cv::imshow("camera_view", image);cv::waitKey(3);}int main(int argc, char **argv)
{ros::init(argc, argv, "scout_control_demo2");ros::NodeHandle n;// 发布话题 "/cmd_vel_raw"ros::Publisher pub = n.advertise<geometry_msgs::TwistStamped>("/cmd_vel_raw", 5);// 订阅话题ros::Subscriber image_sub_ = n.subscribe("/usb_cam/image_raw", 1, imageCallback);//配合r.sleep控制循环频率ros::Rate r(50);// 设置底盘运动速度,初始前进linear_x = 0.0;angular_z = 0.0;linear_y = 0.0;// 运动状态标识符int tag = 0;while(ros::ok()){  cmd_speed.twist.linear.x = linear_x;cmd_speed.twist.linear.y = linear_y;// thcmd_speed.twist.angular.z = angular_z;//image = cv_ptr->image;if(M.m00 > 0){int cx = int (cvRound(M.m10 / M.m00));int cy = int (cvRound(M.m01 / M.m00));ROS_INFO("cx: %d cy: %d", cx, cy);cv::circle(cv_ptr->image, cv::Point(cx, cy), 20, (0, 255, 0));int v = cx - w / 2;linear_x = 0.1;angular_z = -float(v) / 300 * 0.3;
//?//image_pub_.publish(cv_ptr->toImageMsg());pub.publish(cmd_speed);ROS_INFO("linearx: %F,angularz: %F",linear_x,angular_z);}else{ROS_INFO("not found line!");linear_x =0;angular_z =-0.2;pub.publish(cmd_speed);}//当处理到ros::spinonce()时,会去话题订阅缓冲区中查看有没有回调函数,如果有则去处理回调函数,如果没有则继续往下执行ros::spinOnce();r.sleep();}return 0;
}

实验结果

找不到什么问题,代码好像也没毛病,能够正常识别线条更改速度并显示在屏幕上但是驱动不了小车。可能是src内部某些文件出了问题吧。实验失败。。
这里是同学的代码,更换了他的src文件夹,能够正常运行。

#include<ros/ros.h>
#include<sensor_msgs/Image.h>
#include<geometry_msgs/Twist.h>
#include<cv_bridge/cv_bridge.h>
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/imgproc/types_c.h>
#include<opencv2/core/core.hpp>double twist_linear_x , twist_angular_z;				// two kinds speedsensor_msgs::Image hsv_image;						//svoid image_Callback(const sensor_msgs::Image& msg);int main(int argc, char **argv){ros::init(argc, argv, "follower_line");			// init noteros::NodeHandle nh;ros::Subscriber img_sub = nh.subscribe("/usb_cam/image_raw", 10, image_Callback); 	// 更改为订阅 /usb_cam/image_raw 订阅者img_sub来接收来自USB摄像头的原始图像,and image_Callbackros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 10);	// 分别用于发布	小车的速度指令	和	处理后的图像。		ros::Publisher img_pub = nh.advertise<sensor_msgs::Image>("/image_hsv",10);while(ros::ok()){geometry_msgs::Twist twist;twist.linear.x = twist_linear_x;twist.angular.z = twist_angular_z;cmd_pub.publish(twist);img_pub.publish(hsv_image);ros::spinOnce();}return 0;
}void image_Callback(const sensor_msgs::Image& msg){// 当从摄像头接收到图像时,函数触发, public speed cmdcv_bridge::CvImagePtr cv_ptr;							// 确保使用正确的图像编码try {cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);		// 使用cv_bridge将ROS的图像消息转换为OpenCV的图像格式} catch (cv_bridge::Exception& e) {ROS_ERROR("cv_bridge exception: %s", e.what());return;}cv::Mat image = cv_ptr->image;			// 原始图像cv::Mat hsv = image.clone();			// 用于后续的HSV转换cv::Mat res = image.clone();			// 用于存储颜色过滤后的结果 (keep medium)cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);	// 颜色空间转换cv::inRange(hsv, cv::Scalar(0, 0, 0), cv::Scalar(180, 255, 46), res);		// 颜色过滤 -> res// show/*cv::imshow("Filtered Image", res);  // 显示过滤后的图像cv::waitKey(1);  // 等待1毫秒以更新窗口*/// 处理逻辑// origin imageint h = image.rows;int w = image.cols;// search windowint search_top = 5 * h / 6;int	search_bot = search_top + 20;for(int i = 0; i < search_top; i ++){for(int j = 0; j < w; j ++){res.at<uchar>(i,j) = 0;			// set = 0 ,if not in search window}}for(int i = search_bot; i < h; i++){for(int j = 0; j < w; j ++){res.at<uchar>(i,j) = 0;			// set = 0 ,if not in search window}}cv::Moments M = cv::moments(res);			// 图像矩if(M.m00 > 0){int cx = int (cvRound(M.m10 / M.m00));int cy = int (cvRound(M.m01 / M.m00));// center in imageROS_INFO("cx: %d cy: %d", cx, cy);cv::circle(image, cv::Point(cx, cy), 10, (255, 255, 255));// set speed // 假设摄像头是再中间的int v = cx - w / 2;twist_linear_x = 0.1;twist_angular_z = -float(v) / 300 * 0.4;//cmd_pub.publish(twist);} else{ROS_INFO("not found line!");twist_linear_x = 0;twist_angular_z = -0.1;//cmd_pub.publish(twist);}// line's center,in image sensor_msgs::ImagePtr hsv_image_ = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image).toImageMsg();hsv_image = *hsv_image_;
}

参考资料

[1] https://zhuanlan.zhihu.com/p/370996539
[2 ]https://www.bilibili.com/read/cv14789297/

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

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

相关文章

2024年跨境电商上半年营销日历最全整理

2024年伊始&#xff0c;跨境电商开启新一轮的营销竞技&#xff0c;那么首先需要客户需求&#xff0c;节假日与用户需求息息相关&#xff0c;那么接下来小编为大家整理2024上半年海外都有哪些节日和假期&#xff1f;跨境卖家如何见针对营销日历选品&#xff0c;助力卖家把握2024…

软件测试|MySQL 非空约束详解

简介 MySQL中的非空约束&#xff08;NOT NULL Constraint&#xff09;是一种用于确保表中某列不允许为空值的数据库约束。非空约束的作用是保证特定列的数据始终包含有效值&#xff0c;防止在插入或更新操作时出现空值&#xff0c;从而维护数据的完整性和一致性。在本文中&…

学习笔记之——3D Gaussian Splatting及其在SLAM与自动驾驶上的应用调研

之前博客介绍了NeRF-SLAM&#xff0c;其中对于3D Gaussian Splatting没有太深入介绍。本博文对3D Gaussian Splatting相关的一些工作做调研。 学习笔记之——NeRF SLAM&#xff08;基于神经辐射场的SLAM&#xff09;-CSDN博客文章浏览阅读967次&#xff0c;点赞22次&#xff0…

matlab生成列是0-255渐变的图像

图像大小&#xff1a;640512 8位灰度图 %% 生成图像 %大小&#xff1a;640*512 %类型&#xff1a;灰度图 %灰度值&#xff1a;列按照0-255渐变&#xff0c;故命名为column shade。 clc,clear all,close all; %输入的图 imadouble(imread(lenna2.bmp));%原图 imargb2gray(ima)…

MYSQL InnoDB引擎

逻辑存储结构 架构 内存架构 磁盘结构 后台线程 事务原理 redolog undo log MVCC 基本概念 实现原理 隐藏字段 undo log readview

6个提升Python编程能力的PyCharm插件

大家好&#xff0c;PyCharm作为一款强大的集成开发环境&#xff0c;本身已经提供了许多功能&#xff0c;但一些插件将进一步扩展和增强PyCharm的能力。通过使用这些插件&#xff0c;大家能够更快速地编写代码、提高代码质量、进行调试和优化&#xff0c;并将开发体验提升到一个…

软件测试|MySQL DISTINCT关键字过滤重复数据

简介 在MySQL中&#xff0c;有时候我们需要从表中检索唯一的、不重复的数据。这时&#xff0c;我们可以使用DISTINCT关键字来过滤掉重复的数据行。在本文中&#xff0c;我们将深入探讨MySQL中DISTINCT的用法以及如何在查询中使用它来得到不重复的结果集。 基本语法 DISTINCT…

14.网络编程入门和网络应用开发

网络编程入门 计算机网络基础 计算机网络是独立自主的计算机互联而成的系统的总称&#xff0c;组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数&#xff0c;而计算机网络也可以称得上是一个“复…

最具代表性的意大利葡萄酒之一:阿马罗内

阿马罗内是最具代表性的意大利葡萄酒之一&#xff0c;适合搭配各种食物&#xff0c;包括肉类菜肴&#xff0c;面食&#xff0c;意大利调味饭和奶酪等等。阿马罗内是一款浓郁、复杂的意大利葡萄酒&#xff0c;富含水果味。 阿马罗内的食物搭配很有挑战性&#xff0c;因为考虑这…

CMake入门教程【核心篇】导入外部库Opencv

😈「CSDN主页」:传送门 😈「Bilibil首页」:传送门 😈「动动你的小手」:点赞👍收藏⭐️评论📝 文章目录 环境准备示例:在Windows上配置OpenCV路径示例:在Linux上配置OpenCV路径环境准备 首先确保你的系统中安装了CMake。可以通过以下命令安装: Windows: 下载并…

记一次生产事故排查

背景&#xff1a;刚接手一个新工程&#xff0c;是一个给国内top级医院开发的老项目&#xff0c;因为历史原因&#xff0c;代码质量略低&#xff0c;测试难度略高。 上线很久的功能&#xff0c;最近一直频繁的爆发各种问题&#xff0c;经排查发现都是因为在业务过程中im聊天账号…

权威评测:K9、sc、希喂三款主食冻干大比拼,哪款更适合布偶猫?

关注布偶猫的饮食&#xff1a;作为肉食动物&#xff0c;它们肠胃脆弱需谨慎对待。主食冻干是理想之选&#xff0c;它既符合猫咪天然的饮食结构&#xff0c;又采用新鲜生肉为原料。搭配其他营养元素&#xff0c;既美味又营养&#xff0c;还能增强抵抗力。我们将为您测评市场上热…

数模学习day08-拟合算法

这里拟合算法可以和差值算法对比 引入 插值和拟合的区别 与插值问题不同&#xff0c;在拟合问题中不需要曲线一定经过给定的点。拟 合问题的目标是寻求一个函数&#xff08;曲线&#xff09;&#xff0c;使得该曲线在某种准则下与所 有的数据点最为接近&#xff0c;即曲线拟…

揭开JavaScript数据类型的神秘面纱

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 JavaScript作为一门动态类型语言,其数据类型一直是开发者们关注的话题。本文将深入探讨Jav…

深度解析分布式算法:构建高效稳定的分布式系统

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

uni-app中轮播图实现大图预览

参考效果 当轮播图滑动切换的时候更新自定义下标&#xff0c;当图片被点击的时候大图预览。 参考代码 商品详情页轮播图交互 <script setup lang"ts"> // 轮播图变化时 const currentIndex ref(0) const onChange: UniHelper.SwiperOnChange (ev) > …

MPEG4Extractor

1、readMetaData 必须要找到 Moov box&#xff0c;找到 Mdat box或者 Moof box&#xff0c;并且创建了 ItemTable 大端 box 分为 box header 和 box content&#xff1a; box header由8个字节组成&#xff0c;前面四个字节表示这个box 的大小&#xff08;包含这个头的8字节&a…

PCL 格网法计算点云的占地面积

目录 一、算法原理二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、算法原理 该方法主要用于粗略统计机载点云的占地面积。方法原理是将点云沿 X O Y XOY

【深度学习】SDXL tensorRT 推理,Stable Diffusion 转onnx,转TensorRT

文章目录 sdxl 转 diffusers转onnx转TensorRT sdxl 转 diffusers def convert_sdxl_to_diffusers(pretrained_ckpt_path, output_diffusers_path):import osos.environ["HF_ENDPOINT"] "https://hf-mirror.com" # 设置 HF 镜像源&#xff08;国内用户使…

长期使用外接键盘,外物压着自带键盘,容易导致华硕飞行堡垒FX53VD键盘全部失灵【除电源键】

华硕飞行堡垒FX53VD键盘全部失灵【除电源键】 前言一、故障排查二、发现问题三、使用方法总结 前言 版本型号&#xff1a; 型号 ASUS FX53VD&#xff08;华硕-飞行堡垒&#xff09; 板号&#xff1a;GL553VD 故障情况描述&#xff1a; 键盘无法使用&#xff0c;键盘除开机键外…