我是靠谱客的博主 欢呼海燕,最近开发中收集的这篇文章主要介绍[ROS项目]基于语音控制目标跟随与抓取的移动操作平台Part1: 视觉Part2:语音Part3: 运动控制Part4:建图与导航Part5: 网络连接操作Part6:实验,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Part1: 视觉

CUDA INSTALL

CUDA Version: 11.0: 从链接的网址中选择下载runfile模式,并按照提示完成安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwGOSSdL-1609663825155)(/home/hemingshan/图片/2020-12-29 11-21-40屏幕截图.png)]

CUDNN INSTALL

  1. 根据CUDA版本下载安装文件:CUDNN安装历史版本目录地址

  2. 安装CUDNN

$ sudo cp cuda/include/cudnn.h /usr/local/cuda/include/
$ sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64/
$ sudo chmod a+r /usr/local/cuda/include/cudnn.h
$ sudo chmod a+r /usr/local/cuda/lib64/libcudnn*
  1. 查看CUDNN版本
$ cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2

在这里插入图片描述

DARKNET_ROS

安装好上面两个后可以利用GPU加速了,那么就可以提高darknet_ros的性能了。中途遇到了无法定位libcuda.so的情况,经过了网上各种教程,加上摸索,需要在Makefile中进行位置的修改以及在lib64文件夹下有那个文件。最后编译成功后,完成测试。速度快了许多,也没有太多的卡顿。

对该功能包进行分析后,需要对识别的物体进行位置以及角度的发布。通过分析代码以后进行改写。通过自行添加消息文件,并且定义发布话题,对物体进行数据加载后发布到对应的话题。

注意,在新建的msg文件生效后,需要在打印消息之前需要进行source devel/setup.bash的刷新之后,才可以正常打印,要不然会显示消息没有建立或者发布。

对其中的主要文件进行了功能添加,主要是识别物体在图像坐标系下的位置和角度,其中用到了消息的发布和消息类型的编写。其次,添加了订阅控制字和选择订阅摄像头话题的功能,由于这次课设需要用到两个摄像头,所以需要先通过订阅控制字来选定摄像头,通过选定后赋值固定的话题名给变量,之后通过订阅该话题完成对应摄像头图像的订阅,这边比较绕口,但是目的很明确,为了方便通过话题来切换darknet_ros订阅的摄像头话题以及需要的数据进行发布。

补充,其中角度的确定用到了相机坐标系和世界坐标系以及图像坐标系的关系。主要是内参起的作用。
 图源水印

上图( 图源水印)很好的解释了图像是如何呈现在图像坐标系中的,我们用到了其中的角度不变的原理,也就是相似三角形的定义,其中 Z c Z_c Zc O c B O_cB OcB也就是物体在 Z c O c X c Z_cO_cX_c ZcOcXc平面上的投影,该夹角是不变的,所以我们可以通过在图像坐标系下的 x x x和相机内参 f f f进行反切函数的计算后,加上弧度制的转化,将获得目标物体在相机坐标系下,距离 O c Z c O_cZ_c OcZc的角度,那么就提供了角度伺服,而距离就可以通过订阅里程计或者计算目标物体在视觉图像中的面积来完成距离的伺服。

在原有的功能包基础上,需要新建一个消息类型,获得我们所需要的数据。所以在darknet_ros_msg文件夹下创建了Object.msg消息类型,具体程序如下:

string Class        #识别物体类型名称
float64 len_x		#识别物体框所需要的宽
float64 len_y		#识别物体框所需要的高
float64 position_x	#识别物体的中心位置宽
float64 position_y	#识别物体的中心位置高
float64 alpha		#识别物体中心距离垂直面的角度
float64 beta		#识别物体中心距离水平面的角度
float64 z			#识别物体中心距离光新的位置

这里最不容易理解的是float64 alphafloat64 beta两个角度和float64 z这个距离,那么首先前面两个角度在上面提到过,是需要通过三角形相似原理完成的。
在这里插入图片描述

从上面的(1.2)和(1.3)的公式可以了解目标物体在水平面和垂直面下的角度,具体的计算可以利用消息类型中的float position_xfloat position_y来获取目标物体在图像坐标系下的位置,加上相机内参中可以返回下面的两个角度。代码实现如下:

float xmin_ = (rosBoxes_[i][j].x - rosBoxes_[i][j].w / 2) * frameWidth_;
float xmax_ = (rosBoxes_[i][j].x + rosBoxes_[i][j].w / 2) * frameWidth_;
float ymin_ = (rosBoxes_[i][j].y - rosBoxes_[i][j].h / 2) * frameHeight_;
float ymax_ = (rosBoxes_[i][j].y + rosBoxes_[i][j].h / 2) * frameHeight_;
float lenx = xmax_ - xmin_;
float leny = ymax_ - ymin_;
float point_x = (xmin_+xmax_)/2;
float point_y = (ymin_+ymax_)/2;
Object.len_x = lenx;
Object.len_y = leny;
Object.position_x = point_x;
Object.position_y = point_y;
Object.Class= classLabels_[i];
//相机参数
float fx = 543.5773315429688;
float fy = 543.2941284179688; 
float u0 = 313.0025202958532;
float v0 = 219.3712132622622;
float Zc = 1.0; //比例系数
Object.alpha = (atan((point_x-u0)/fx)/PI)*180;
Object.beta = (atan((point_y-v0)/fy)/PI)*180;

在没有深度相机的基础下,如何获得测得距离,是非常重要的一个部分。首先从(1.4)的比例关系,我们可以通过相机内参完成计算。但是肯定会因为相机的优劣或者其他因素产生误差,这个误差如何消除将是关键点。在这里我们提出来一种新的计算方式。首先,在t0时刻获取到的图像我们记录为img1,接着,在t1时刻下摄像头产生了微小移动,那么摄像头中的光心也会产生一定的移动,假设这个距离是z。最后,我们假设这个微笑的移动可以忽略,那么就可以在原有的光心下加上z完成新的一个相似三角形的计算,通过新的相似三角形完成误差的减小。这里只是提出了一种新思想,具体的程序操作尽可能有深度相机还是最好的。

Part2:语音

IAT_PUBLISH

这个用到了科大讯飞的SDK,主要是通过麦克风识别出采集到的信息,将该信息转换成文字。在此基础上,我进行了小部分的改写,主要是对采集的信息识别厚的文字完成发布。那么实现的功能就是,我通过麦克风输入语音命令。例如,“抓取“,就可以通过该节点将语音转换成文字,并将文字发布至对应话题上,供其他的节点完成消息的订阅。

SAY.PY

这个Python文件是通过一个功能包实现的,具体的名字忘记了,但是之前该功能包的作用就是将通过给予变量名完成该变量名的语音输出,简单来说就是给什么念什么。我把它用在了消息的反馈上。例如,我对麦克风的语音输入是”抓取“,那么可以通过该文件完成反馈,将”好的“,通过语音形式反馈回来。接下来,将通过接收到的语音文字形式,完成识别,之后对对应固定的节点,完成消息的发布。发布的消息主要是对应的控制字。

USB_CAM.LAUNCH

这个文件主要是将usb_cam_node进行了部分改写,之后可以在这个文件上运行两个该节点,也就是可以同时启动两个摄像头,其中一个用于手在眼外,另一个用于手在眼上,方便了视觉伺服控制,也可以对于视觉切换剩下了很多时间。这两个切换是通过订阅SAY.PY节点发布的话题来完成的,由于这两个摄像头分别是在手上和移动平台上,所以通过语音控制,可以实现移动平台的目标检测完成跟随和机械臂的目标检测完成抓取两个功能,为其他功能拓展留下了一定的空间。

Part3: 运动控制

在运动控制这一部分,分为移动平台底盘的移动和机械臂的移动。移动底盘是在出厂的时候建立好的,而移动底盘上的机械臂是使用的AUBO_i5。这两个部分都会订阅视觉识别的结果。有相似之处,又有一些不同

移动平台底盘

SMART_CAR_NODE.LAUNCH

这个文件是用来启动底盘通讯,其具体的代码如下:

<launch>
    <node name="smart_car_node" pkg="smart_car" type="smart_car_node" output="screen">
	    <param name="port_name" value="/dev/smart_car"/>
        <!-- <param name="port_name" value="/dev/ttyUSB1"/> -->
    </node>    
</launch>

CMakeList.txt文件夹中会有这样的一段代码

add_executable(smart_car_node src/main.cpp src/Protocol.cpp src/SerialClass.cpp)

我们可以通过阅读main.cpp这个主要代码知道这个launch文件启动的这么个节点是用来做什么的

//
// Created by jason on 4/11/18.
//
#include "SerialClass.hpp"
#include "smart_car/velocity_data.h"
SerialClass *serClass;

int main(int argc,char *argv[])
{
   	 ros::init(argc,argv,"smart_car");
     ros::NodeHandle nh;
   	 ros::Subscriber cmd = nh.subscribe<smart_car::smart_car_cmd>("smart_car_cmd",1000,SerialClass::callback);//订阅smart_car_cmd主题
        
	ros::Subscriber sub = nh.subscribe("cmd_vel", 20, SerialClass::callback_geometry); //订阅cmd_vel主题

	nav_msgs::Odometry odom;//定义里程计对象
        smart_car::velocity_data velocity;
        ros::Publisher velocity_pub = nh.advertise<smart_car::velocity_data>("velocity",20);    //发布的主题名称 
	ros::Publisher odom_pub = nh.advertise<nav_msgs::Odometry>("odom", 20); //定义要发布odom主题

	static tf::TransformBroadcaster odom_broadcaster;//定义tf对象
    geometry_msgs::TransformStamped odom_trans;//创建一个tf发布需要使用的TransformStamped类型消息

	//-------超声传感器信息
	sensor_msgs::Range range_msg;
	ros::Publisher pub_range = nh.advertise<sensor_msgs::Range>("sonar",20);

	range_msg.radiation_type = sensor_msgs::Range::ULTRASOUND; 
	range_msg.header.frame_id ="sonar"; 
	range_msg.field_of_view = 0.5; 
	range_msg.min_range = 0.05; 
	range_msg.max_range = 2;

	//-------红外传感器信息
	sensor_msgs::Range range_msg_IR;
	ros::Publisher pub_range_IR = nh.advertise<sensor_msgs::Range>("infrared",20);

	range_msg_IR.radiation_type = sensor_msgs::Range::INFRARED; 
	range_msg_IR.header.frame_id ="infrared"; 
	range_msg_IR.field_of_view = 0.01; 
	range_msg_IR.min_range = 0.1; 
	range_msg_IR.max_range = 0.8;

	//-----serialPort
   	 string port = "/dev/ttyUSB0";
    	int baudrate = 115200;
   	 if(ros::param::has("~port_name")){
       		 ros::param::get("~port_name",port);
   	 }
    	if(ros::param::has("~baudrate")){
       		 ros::param::get("~baudrate",baudrate);
   	 }
    	ROS_INFO_STREAM(port);

    serClass = new SerialClass(port,baudrate);

	int ncounter = 0;
    ros::Rate loop_rate(100); 
	while(ros::ok())
	{
		//---serialwork
		ros::spinOnce();
		size_t len = serClass->ser.available();
		if(len){
		    serClass->recvData(len);
		}
		
		 //---pub tf(odom->base_footprint)
            odom_trans.header.stamp = ros::Time::now();
            odom_trans.header.frame_id = "odom";
            odom_trans.child_frame_id = "base_footprint";
            odom_trans.transform.translation.x = serClass->myOdomData.position_x;
            odom_trans.transform.translation.y = serClass->myOdomData.position_y;
            odom_trans.transform.translation.z = serClass->myOdomData.position_z;
            odom_trans.transform.rotation.x = serClass->myOdomData.orientation_x;
            odom_trans.transform.rotation.y = serClass->myOdomData.orientation_y;
            odom_trans.transform.rotation.z = serClass->myOdomData.orientation_z;
            odom_trans.transform.rotation.w = serClass->myOdomData.orientation_w;

		//---pub odom
           	odom.header.stamp = ros::Time::now();
			odom.header.frame_id = "odom";
			odom.child_frame_id = "base_footprint";
			odom.pose.pose.position.x = serClass->myOdomData.position_x;
			odom.pose.pose.position.y = serClass->myOdomData.position_y;
			odom.pose.pose.position.z = serClass->myOdomData.position_z;
			odom.pose.pose.orientation.x = serClass->myOdomData.orientation_x;
			odom.pose.pose.orientation.y = serClass->myOdomData.orientation_y;
			odom.pose.pose.orientation.z = serClass->myOdomData.orientation_z;
			odom.pose.pose.orientation.w = serClass->myOdomData.orientation_w;
			odom.twist.twist.linear.x = serClass->myOdomData.linear_x;
			odom.twist.twist.linear.y = serClass->myOdomData.linear_y;
			odom.twist.twist.linear.z = serClass->myOdomData.linear_z;
			odom.twist.twist.angular.x = serClass->myOdomData.angular_x;
			odom.twist.twist.angular.y = serClass->myOdomData.angular_y;
			odom.twist.twist.angular.z = serClass->myOdomData.angular_z;

			velocity.data[0] =serClass->myOdomData.v1; //the first wheel speed for the pid test
            velocity.data[1] =serClass->myOdomData.v2; //the 2th wheel speed for the pid test

            velocity.data[2] =serClass->myOdomData.v3;// the actually angular velocity
			printf("velocity:%f,%f,%f rn",velocity.data[0],velocity.data[1],velocity.data[2]);
			
			odom_broadcaster.sendTransform(odom_trans);
			odom_pub.publish(odom);
            velocity_pub.publish(velocity);
		//---
			range_msg.range = serClass->myRange;
			pub_range.publish(range_msg);
		//-----
			range_msg_IR.range = serClass->myRange_IR;
			pub_range_IR.publish(range_msg_IR);
		//-----
		loop_rate.sleep();
		
	}
    return 0;
}

从上面的代码知道,这个节点启动之后会将传感器信息在ros下完成发布与订阅,其中包括了底盘的轮子、速度、红外传感以及里程计等传感器信息,也可以对速度进行发布后在其他节点上对速度进行解算后发布到每一个轮子上。所以如果要让车子动起来的话,就一定需要启动上面的节点,对小车进行后续的控制。

KEYBOARD_MECANUM_MANIPULATION

这部分是通过键盘控制小车移动的,具体工作是这样的。首先,在键盘处进行中断处理,如果有键盘那被按下,那么进入中断服务程序。进入中断服务程序之后,就可以对按下的键盘进行判断了,虽然Ubuntu系统下的键盘判断没有Windows下那么方便,但是也没有像微机原理课程那样需要行列的程序编写判断。只需要包含头文件以后对按下的char类信进行判断即可,注意的是可以利用char也可以用int进行判断,看个人喜好。判断好以后进入对应的控制方法,发布不同的控制数据到cmd_vel话题上去。下面的程序展示了部分控制,以向前为例。

#include <ros/ros.h> 
#include <geometry_msgs/Twist.h>
#include<stdio.h>
#include </home/hemingshan/mecanum_ws/src/nexus_4wd_mecanum_simulator/mecanum_manipulation/include/keyboard.h>

int main(int argc, char **argv){
    geometry_msgs::Twist command_(float linear_x, float linear_y, float linear_z, float angular_z);
    void command_update (char c_);

    ros::init(argc, argv, "Nexus_move_test");
    ros::NodeHandle nh_;
    ros::Publisher command_pub = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1000);
    ros::Rate loop_rate(10);

    init_keyboard();

    char c, c_tmp;
    ROS_INFO("Input a Command: n");
    c=readch();
    ROS_INFO("%d",c);

    while(ros::ok())
    {
        
        if(c == 119)
        {
            ROS_INFO("You entered w. Forward!!");
            geometry_msgs::Twist Twist_;
            Twist_ = command_(5.0, 0.0, 0.0, 0.0);
            command_pub.publish(Twist_);
            ROS_INFO("Publish");
            ros::spinOnce();
            c_tmp = c;
            kbhit();
            c=readch();
        }
    }
    return 0;

}
geometry_msgs::Twist command_(float linear_x, float linear_y, float linear_z, float angular_z)
{
    geometry_msgs::Twist Twist_;
    Twist_.linear.x = linear_x;
    Twist_.linear.y = linear_y;
    Twist_.linear.z = linear_z;
    Twist_.angular.z = angular_z;
    return Twist_;
}
void command_update (char c_)
{
    if(kbhit())
    {
        c_ = readch();
    }
}

这里尤其需要注意的是,我们在按下一个键盘的时候会进行中断,那么后续的键盘就不会读取,但是在中断操作的时候,Linux系统不会在中断输入一个按键的时候就进行中断,我们需要按下Enter键对中断进行按键输入才会进入中断。那么问题来了,在程序中,首先按下了一个键盘那后会进入中断服务程序,但是中断服务程序执行结束后,会因为前面按下的Enter键再次进入中断服务程序,所以我们需要判断两次。为了解决这个问题,我重新便写了void command_update程序也加上了不再判断Enter键的程序,但是最初的目的是,通过按下键盘后完成持续发布的功能,由于Enter键的加入,我只能判断一次后发布一条消息,需要人工的持续发布。但是这个问题在python下的文件就不会遇到。

MECANUM_MANIPULATION

上面那个程序实在仿真情况下进行测试的,下面的这个程序是在移动平台上运行的程序。这个节点的功能就是,首先,从视觉节点获取Object消息类型,为了完成跟随任务,我们需要调节小车的前进后退和转弯。那么,我就把这个前进后退分成一组,转弯分成一组。其中前进后退,利用摄像机图像中的距离z进行任务判断,或者利用面积area也可以;转弯,利用摄像机图像中的目标物体的水平偏角alpha进行判断的。具体程序如下:

#include <ros/ros.h> 
#include <geometry_msgs/Twist.h>
#include <stdio.h>
#include <ros/ros.h> 
#include <geometry_msgs/Twist.h>
#include <iostream>
#include <std_msgs/Float64.h>
#include <darknet_ros_msgs/Object.h>
#include <math.h>
class Manipulation
{
public:
Manipulation(){
    P_alpha = 0.01;
    I_alpha = 0.0;
    D_alpha = 0.0;
    P_area = 0.000004;
    I_area = 0.0;
    D_area = 0.0;
    Expect_area = 300*250;
    Expect_alpha = 0.0;
    Integral_area = 0.0;
    Integral_alpha = 0.0;
    Error_alpha_previous = 0.0;
    Error_area_previous = 0.0;
    area_threshold_up = 0.8;
    area_threshold_down = 0.01;
    angular_threshold_up = 0.8;
    angular_threshold_down = 0.01;
}

~Manipulation(){}

geometry_msgs::Twist command_(float linear_x, float linear_y, float linear_z, float angular_z)
{
    geometry_msgs::Twist Twist_;
    Twist_.linear.x = linear_x;
    Twist_.linear.y = linear_y;
    Twist_.linear.z = linear_z;
    Twist_.angular.z = angular_z;
    return Twist_;
}
void callback(const darknet_ros_msgs::Object &object)
{
    Class = object.Class;
    std::string s;
    s = "person";
    if(s == Class)
    {
        ROS_INFO("Yes, it is a person");
        len_x = object.len_x;
        len_y = object.len_y;
        position_x = object.position_x;
        position_y = object.position_y;
        alpha = object.alpha;
        area = len_x * len_y;

        //控制器从传感器得到测量结果,然后用需求结果减去测量结果来得到误差
        Error_alpha = Expect_alpha - alpha;
        Integral_alpha += Error_alpha;

        Error_area = Expect_area - area;
        Integral_area += Error_area;
    
        //角度PID输出
        U_alpha = P_alpha*Error_alpha + I_alpha*Integral_alpha + D_alpha*(Error_alpha - Error_alpha_previous);
        Error_alpha_previous = Error_alpha;
        //面积PID输出
        U_area = P_area*Error_area + I_alpha*Integral_area + D_area*(Error_area - Error_area_previous);
        Error_area_previous = Error_area;

        //发布
        geometry_msgs::Twist Twist_;
        //Twist_ = command_(U_area, 0.0, 0.0, U_alpha);
        ROS_INFO("U_alpha:%f",U_alpha);
        if (fabs(U_alpha) < angular_threshold_up && fabs(U_alpha) > angular_threshold_down)
        {
            ROS_INFO("In Threshold, Pub the result of PID");
            U_alpha = U_alpha;
        }
        else if (abs(U_alpha) < angular_threshold_down)
        {
            ROS_INFO("Bellow Threshold, Pub the stop");
            U_alpha = 0.0;
        }
        else
        {
            ROS_INFO("Over Threshold, Pub the Max");
            U_alpha = angular_threshold_up;
        }
        ROS_INFO("U_area:%f",U_area);
        if (fabs(U_area) < area_threshold_up && fabs(U_area) > area_threshold_down)
        {
            ROS_INFO("In Threshold, Pub the result of PID");
            Twist_ = command_(U_area, 0.0, 0.0, U_alpha);
        }
        else if (abs(U_area) < area_threshold_down)
        {
            ROS_INFO("Bellow Threshold, Pub the stop");
            Twist_ = command_(0.0, 0.0, 0.0, U_alpha);
        }
        else
        {
            ROS_INFO("Over Threshold, Pub the Max");
            Twist_ = command_(area_threshold_up, 0.0, 0.0, U_alpha);
        }
        command_pub.publish(Twist_);
        ros::spinOnce();
    }
    ROS_INFO("back");
}

private:
ros::NodeHandle             nh_;
ros::Publisher              command_pub = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1000);
ros::Subscriber             visual_command_sub = nh_.subscribe("/darknet_ros/Object",1,&callback, this);

float                       P_alpha;
float                       I_alpha;
float                       D_alpha;

float                       P_area;
float                       I_area;
float                       D_area;


float                       len_x;
float                       len_y;
float                       position_x;
float                       position_y;
float                       alpha;
float                       area;

float                       Expect_alpha;
float                       Expect_area;

float                       Error_alpha;
float                       Error_alpha_previous;
float                       Integral_alpha;

float                       Error_area;
float                       Error_area_previous;
float                       Integral_area;

float                       U_alpha;
float                       U_area;
std::string                 Class;

float                       area_threshold_up;
float                       area_threshold_down;
float                       angular_threshold_up;
float                       angular_threshold_down;
};

int main(int argc, char **argv){

    ros::init(argc, argv, "Nexus_move_test");
    Manipulation manipulation;
    ros::Rate loop_rate(10);

    while(ros::ok())
    {
        //ROS_INFO("IN ROS OK.");
	ros::spinOnce();
    }
    return 0;
}

从程序中可以看出,我用到了在计算机控制系统中学习到离散系统的PID控制方法,并应用其中发现效果不错。其PID公式如下
在这里插入图片描述

将控制前后的变量z和控制转向的变量alpha作为控制输入,进行PID控制后,输出控制结果到对应的cmd_vel话题上。

AUBO_i5机械臂

在机械臂部分,主要是订阅视觉的结果,并按照上面小车跟随的思想,完成机械臂跟随目标物体的中心位置,并调整到待抓取目标物体的上方,并根据距离判断下降的高度完成抓取。由于手抓的不可用性,我们只能通过胶带的方式弥补这个缺陷。

在之前以网络方式连接机械臂示教器ip地址的情况下,由于网络传输快慢的原因,导致无法顺滑的控制机械臂的移动,只好改成以太网的有线连接来完成控制。在Ubuntu下的网络配置如下图。

在这里插入图片描述

其中的地址是电脑本身的ip地址,而机械臂的ip可以通过示教器来完成设定。

MOVEIT_PLANNING_EXECUTION.LAUNCH

此文件是在配置Moveit过程中会自动建立的,也就是在设定机械臂ip之后在Rviz下启动Moveit包来对机械臂进行控制。主要用到的事MoveGroup接口,这个接口能够完成机械臂的位置控制或者关节控制等等。主要关系图如下图所示:
在这里插入图片描述

MoveGroup提供了多种接口,有C++、Python、GUI和其他接口,在此次项目中我们用的是C++接口,因为所有的方案是C++语言程序写的,为了统一以下,这里我们也用到的是C++接口。

ROBOT_ARM_MANIPULATION

此文件是能够让节点订阅摄像头识别物体的结果,得到目标抓取物体的数据,将数据作为PID控制器的输入,得到的输出通过MoveGroup接口对机械臂进行控制完成目标物体的抓取。在小车跟随的情况中我加了死区,也就是在一定范围内小车不再做一定的任务作业,但是在机械臂上不能存在死区。为了抓取的高路帮性,我们要对视觉识别和机械臂的控制进行高速的传输与决策。所以视觉上,我们利用了GPU加速,机械臂上应用了有线连接。

由于机械臂的抓取中视觉部分和小车跟随类型相似,在程序上不再进行展示。思路大致相同,不同点在于多加了一个维度,也就是水平面的偏角度beta。在抓取中,需要用alphabeta角度来控制机械臂移动到待抓取目标的中心位置,并在下降的过程中持续调节。

Part4:建图与导航

建图

终端1:roslaunch bringup gmapping_bringup.launch #启动机器,开启建图模式
终端2:rosrun teleop_twist_keyboard teleop_twist_keyboard.py #遥控辅助。注:遥控时,请保持点击该终端为活跃状态
终端3:rviz #显示地图信息等
终端4:rosrun bringup auto_mapbuilding.sh #自动建图指令,生成 auto_map 地图。也可自定义地图名字,运行下一步。
终端4:rosrun bringup mapbuilding.sh #手动处理地图,自定义命名,管理之前的地图。

注: 运行 建图操作 结束时,请关闭所有终端。

导航

终端1:roslaunch bringup navigation_bringup.launch map_name:=auto_map #启动机器,开启导航模式。备注:后缀map_name:=auto_map 为地图选择,auto_map为自定义地图名字,可修改。此后缀也可省略,将默认加载auto_map地图。
终端2:rosrun teleop_twist_keyboard teleop_twist_keyboard.py #遥控辅助。注:遥控时,请保持点击该终端为活跃状态
终端3:rviz #显示地图信息等

注: 运行 导航操作 结束时,请关闭所有终端。

INITIAL_ROBOT

在执行导航任务之前,我们需要通过在Rviz上点击位姿初始化,将在Rviz上机器人模型的位置和姿态与实物完成匹配。就和在仿真的时候,需要将Rviz下的turtlebot3模型和Gazebo下运行的turtlebot3模型一致的任务相同,这个程序就是完成这个功能,主要就是通过消息类型,发布到inital这个话题中去来完成的。

SEND_GOAL

在初始化结束以后,我们就可以通过该节点来对图中的点进行发布,然后通过导航算法进行路径规划,并发布到底盘中去,来完成移动操作任务。

Part5: 网络连接操作

本次设计总共用到了一台电脑,两台工控机。一台电脑是我的PC端,另外两台都在移动操作平台上,一个用来控制机械臂,一个用来控制底盘。由于底盘的工控方便而且性能较佳,而且我的电脑在处理视觉的过程中会用到大量的GPU和CPU运算,所以决定在机器人上的工控机来进行启动roscore。那么这里的问题就在于我的PC端如何连接底盘的roscore就成了问题。由于ROS本身是分布式操作系统,所以可拓展性做的非常好,我们按照教程,在本地的.bashrc文件上进行了修改,并在存储机器人的ip地址于host里面,重启ssh服务通信后便可以通过ssh登录机器人的工控端。并在机器人端启动roscore以后,本地启动的ros文件也可以成功通过无线网段和机器人端的ros进行通信。

Part6:实验

实验室网址:http://www.roboticlabofmingshan.com/
Github地址:https://github.com/MingshanHe/move_operate_platform_project

最后

以上就是欢呼海燕为你收集整理的[ROS项目]基于语音控制目标跟随与抓取的移动操作平台Part1: 视觉Part2:语音Part3: 运动控制Part4:建图与导航Part5: 网络连接操作Part6:实验的全部内容,希望文章能够帮你解决[ROS项目]基于语音控制目标跟随与抓取的移动操作平台Part1: 视觉Part2:语音Part3: 运动控制Part4:建图与导航Part5: 网络连接操作Part6:实验所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部