【Turtlebot移动机器人实验记录】实验2. 利用激光传感器数据进行直线特征提取

2020.12.21

1. 编译报错

执行如下catkin_make时,
在这里插入图片描述
报错如下:

CMake Warning (dev) at turtlebot/navigation/move_base/CMakeLists.txt:45 (add_dependencies):
  Policy CMP0046 is not set: Error on non-existent dependency in
  add_dependencies.  Run "cmake --help-policy CMP0046" for policy details.
  Use the cmake_policy command to set the policy and suppress this warning.

  The dependency target "geometry_msgs_gencpp" of target "move_base" does not
  exist.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Generating done
-- Build files have been written to: /home/zth/work_ws/build
Invoking "cmake" failed

报错信息中给出了解决方案:

$ catkin_make -Wno-dev

编译成功后如下:
在这里插入图片描述
接下来这步编译依旧会出现上面那个问题,同样方法处理即可。
在这里插入图片描述

2. 周六去实验室验证该步骤

hokoyu 配置最后插入激光传感器,检查文件权限。

2020.12.25

1. 编译时显示无法确定目标文件的语言

  • 报错如下:
CMake Error: CMake can not determine linker language for target: DetectLine

在这里插入图片描述

  • 问题背景:编译时,显示不知道DetectLine.cpp的语言,但这文件明明是C++啊?查看CmakeList.txt文件时发现下面这句(从实验报告中copy的)存在问题,
add_executable(DetectLine src/DetectLine.cpp)
target_link_libraries(DetectLine ${
    
    catkin_LIBRARIES})
  • 解决方法:src/DetectLine.cpp中的“/”后多了一个空格,删掉空格后编译成功。

2. msg.angle_min编译报错

在这里插入图片描述
解决方法:
msg.angle_min改为msg->angle_min

3. 实验2代码

参数设定如下:

  • 一个霍夫变换算法检测的角度范围:60°
  • 每次步进的角度:10°
  • 激光传感器扫描的角度:180°
  • 选取中间的120°
  • 将其均分为30°-90°,40°-100°,……90°-150°
  • 在30°-90°这60°的范围内,选取N=11束激光
  • 将直线的方向离散化为有限个等间距的离散值=2°
  • 误差允许范围设为0.01
  • 阈值设为N-1

3.1 DetectLine.cpp版本1(存在错误,版本2是修复过的)

#include "ros/ros.h"
#include "sensor_msgs/LaserScan.h"

#define PI 3.1415926

// 回调函数
void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg)
{
    
    
	std::vector<float> ranges = msg->ranges;
	std::vector<double> angle;
	// std::vector<double> X;
	// std::vector<double> Y;
	
	// 转换到二维XY平面坐标系下
	for(int i = 0; i < ranges.size(); i++)
	{
    
    
		angle.push_back(msg->angle_min + i * msg->angle_increment);
		// X.push_back(ranges[i] * cos(angle[i]));
		// Y.push_back(ranges[i] * sin(angle[i]));	
	}
	
	// 激光传感器扫描的角度为180度,选取其中间的120度
	int angle_start_order = ranges.size() * 30/180; // 要计算的起始角度 = 30度
	int angle_end_order = ranges.size() * 150/180; // 要计算的结束角度 = 150度
	
	// 选定霍夫变换的角度范围为60度,每次歩进的角度为10度
	int Hough_angle_range_order = ranges.size() * 60/180; // 霍夫变换的角度范围 = 60度
	int Hough_angle_step_order = ranges.size() * 10/180; // 每次歩进的角度 = 10度
	
	// 对每一个60度范围执行1次霍夫变换检测
	int N = 11; // 在这60度的范围内选取N束激光 = 11条
	int M = Hough_angle_range_order / (N - 1); // 均匀选取N束激光时,每束激光的角度间隔数,即个间隔几个msg->angle_increment。如60度的范围内取11束激光,就是60/(11-1)=6度,M=6/msg->angle_increment,M向下取整,得到977
	int times = 7; // 霍夫变换次数 = (angle_end_order - angle_start_order - Hough_angle_range_order) / Hough_angle_step_order + 1 = (150-30-60)/10 + 1 = 7
	
	std::vector<double> laser_angle; // 激光角度
	std::vector<double> x;
	std::vector<double> y;
	std::vector<std::vector<double> > r(N, std::vector<double>(180/2+1)); // 格式:二维动态数组N行180/2+1列
	std::vector<std::vector<double> > error(N, std::vector<double>(N)); // 每组中 N 个 r 值之间的误差。格式:二维动态数组N行N列
	for(int i = 0; i <  times; i++) // 霍夫变换次数 = (angle_end - angle_start - Hough_angle_range) / Hough_angle_step + 1 = (150-30-60)/10 + 1 = 7
	{
    
    
		double Hough_angle_start = angle[angle_start_order] + angle[Hough_angle_step_order] * i; // 每次霍夫变换的起始角度
		
		for(int j = 0; j < N; j++)
		{
    
    
			int laser_order = angle_start_order + Hough_angle_step_order * i + j * M;
			laser_angle.push_back(angle[laser_order]);
			x.push_back(ranges[laser_order] * cos(angle[laser_order]));
			y.push_back(ranges[laser_order] * sin(angle[laser_order]));
			
			// 计算每个角度对应的参数空间的值 r
			for(int k = 0; k < 180/2+1; k++)
			{
    
    
				r[j][k] = (x[j] * cos(k*2/180*PI) + y[j] * sin(k*2/180*PI));
			}
		}
		
		// 在每个离散化角度下,对计算出的 N 个 r 值进行比较
		for(int k = 0; k < 180/2+1; k++)
		{
    
    
			for(int m = 0; m < N; m++)
			{
    
    
				int vote_times = 0;
				for(int n = 0; n < N; n++)
				{
    
    
					error[m][n] = r[k][m] - r[k][n];
					
					// 取误差绝对值
					if(error[m][n] < 0)
						error[m][n] = - error[m][n];
						
					// 如果误差在设定的范围内
					if(error[m][n] < 0.01)
					{
    
    
						vote_times++;
					}
				}
				// 投票次数达到阈值,则这是一条直线,输出结果; 否则,投票数清零,计算其他组合
				if(vote_times >= N - 1)
					ROS_INFO("IsStraightLine: 1");
			}
		}
	// 没有检测到直线
	ROS_INFO("IsStraightLine: 0");
	}
}
int main(int argc, char **argv)
{
    
    
	// 初始化ROS,新建DetectLine节点
	ros::init(argc, argv, "DetectLine");
  
  // 创建句柄
  ros::NodeHandle n;
  
  // 新建一个ros:Subscribe对象,该对象订阅话题/sacn上发布的消息
  ros::Subscriber sub = n.subscribe("/scan", 10, laserCallback);
  
  // 进入自循环,等待消息的到达
  ros::spin();
  
  return 0;
}

实物验证时,上述代码存在数组访问越界,误差阈值设置过小的问题。
下面的代码是修改后测试效果良好的。

3.2 DetectLine.cpp版本2

#include "ros/ros.h"
#include "sensor_msgs/LaserScan.h"

#define PI 3.1415926

// 回调函数
void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg)
{
    
    
	ROS_INFO("laserCallback is called!");
	
	std::vector<float> ranges = msg->ranges;
	std::vector<double> angle;
	// std::vector<double> X;
	// std::vector<double> Y;
	
	// 转换到二维XY平面坐标系下
	for(int i = 0; i < ranges.size(); i++)
	{
    
    
		angle.push_back(msg->angle_min + i * msg->angle_increment);
		// X.push_back(ranges[i] * cos(angle[i]));
		// Y.push_back(ranges[i] * sin(angle[i]));
		//ROS_INFO("range_order: %d, ranges: %f", i, ranges[i]);	
	}
	
	// 激光传感器扫描的角度为180度,选取其中间的120度
	int angle_start_order = ranges.size() * 30/180; // 要计算的起始角度 = 30度
	//ROS_INFO("angle_start_order: %d\n", angle_start_order);
	int angle_end_order = ranges.size() * 150/180; // 要计算的结束角度 = 150度
	//ROS_INFO("angle_end_order: %d\n", angle_end_order);
	// 选定霍夫变换的角度范围为60度,每次歩进的角度为10度
	int Hough_angle_range_order = ranges.size() * 60/180; // 霍夫变换的角度范围 = 60度
	//ROS_INFO("Hough_angle_range_order: %d\n", Hough_angle_range_order);
	int Hough_angle_step_order = ranges.size() * 10/180; // 每次歩进的角度 = 10度
	//ROS_INFO("Hough_angle_step_order: %d\n", Hough_angle_step_order);
	// 对每一个60度范围执行1次霍夫变换检测
	int N = 11; // 在这60度的范围内选取N束激光 = 11条
	int M = Hough_angle_range_order / (N - 1); // 均匀选取N束激光时,每束激光的角度间隔数,即个间隔几个msg->angle_increment。如60度的范围内取11束激光,就是60/(11-1)=6度,M=6/msg->angle_increment,M向下取整,得到
	//ROS_INFO("M: %d\n", M);
	int times = 7; // 霍夫变换次数 = (angle_end_order - angle_start_order - Hough_angle_range_order) / Hough_angle_step_order + 1 = (150-30-60)/10 + 1 = 7
	
	//std::vector<double> laser_angle; // 激光角度
	//std::vector<double> x;
	//std::vector<double> y;
	std::vector<std::vector<double> > r(N, std::vector<double>(180/2+1)); // 格式:二维动态数组N行180/2+1列
	std::vector<std::vector<double> > error(N, std::vector<double>(N)); // 每组中 N 个 r 值之间的误差。格式:二维动态数组N行N列
	for(int i = 0; i <  times; i++) // 霍夫变换次数 = (angle_end - angle_start - Hough_angle_range) / Hough_angle_step + 1 = (150-30-60)/10 + 1 = 7
	{
    
    
		//ROS_INFO("Hough times: %d", i);
		double Hough_angle_start = angle[angle_start_order] + angle[Hough_angle_step_order] * i; // 每次霍夫变换的起始角度
		
		for(int j = 0; j < N; j++)
		{
    
    
			int laser_order = angle_start_order + Hough_angle_step_order * i + j * M;
			//laser_angle.push_back(angle[laser_order]);
			double x = (ranges[laser_order] * cos(angle[laser_order]));
			double y = (ranges[laser_order] * sin(angle[laser_order]));
			//ROS_INFO("x = %f", x);
			//ROS_INFO("y = %f", y);
			// 计算每个角度对应的参数空间的值 r
			for(int k = 0; k < 180/2+1; k++)
			{
    
    
				//r[j][k] = (x[j] * cos(k*2/180*PI) + y[j] * sin(k*2/180*PI));
				r[j][k] = (x * cos(k*2/180*PI) + y * sin(k*2/180*PI));
				//ROS_INFO("r: %f", r[j][k]);
			}
		}
		
		// 在每个离散化角度下,对计算出的 N 个 r 值进行比较
		for(int k = 0; k < 180/2+1; k++)
		{
    
    
			for(int m = 0; m < N; m++)
			{
    
    
				int vote_times = 0;
				for(int n = 0; n < N; n++)
				{
    
    
					error[m][n] = r[m][k] - r[n][k];
					//ROS_INFO("error: %f", error[m][n]);
					// 取误差绝对值
					if(error[m][n] < 0)
						error[m][n] = - error[m][n];
						
					// 如果误差在设定的范围内
					if(error[m][n] < 0.1)
					{
    
    
						vote_times++;
						//ROS_INFO("vote_times: %d", vote_times);
					}
				}
				// 投票次数达到阈值,则这是一条直线,输出结果; 否则,投票数清零,计算其他组合
				if(vote_times >= N - 1)
					ROS_INFO("IsStraightLine: 1");
			}
		}
	// 没有检测到直线
	//ROS_INFO("IsStraightLine: 0");
	}
}
int main(int argc, char **argv)
{
    
    
	// 初始化ROS,新建DetectLine节点
	ros::init(argc, argv, "DetectLine");
  
  // 创建句柄
  ros::NodeHandle n;
  
  // 新建一个ros:Subscribe对象,该对象订阅话题/sacn上发布的消息
  ros::Subscriber sub = n.subscribe("/scan", 10, laserCallback);
  
  // 进入自循环,等待消息的到达
  ros::spin();
  
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44324181/article/details/111991648
今日推荐