IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> 基于ROS的语音控制机器人 -> 正文阅读

[人工智能]基于ROS的语音控制机器人

文章目录


前言

用来记录一下我的ros学习过程,小车主要用到的有stm32和树莓派。

本文需要一些ros基础


提示:以下是本篇文章正文内容,下面案例可供参考

一、ubuntu16.04和树莓派安装ROS-kinetic

关于安装ros,网上有很多教程,这里就省略不讲。

不过ubuntu安装ros时,rosdep init和rosdep update很容易出现问题 ,在这里列出一个有效的 解决方法。

首先使用以下指令:

cd /etc/ros/rosdep/sources.list.d
sudo gedit 20-default.list

此时20-default.list内容如下

打开这些网址,将五个文件下载下来,拷贝到/etc/ros/rosdep文件夹下。然后sudo rosdep update即可成功。

树莓派安装ros,如果是ubuntun mate安装ros,需要一个显示器,安装过程一样;如果是树莓派自己的系统安装ros,这个过程十分麻烦,建议有条件的可以买一个别人的镜像。

安装成功后记得打开终端输入roscore命令测试以下。

二、树莓派和PC机之间的ros通信

二者之间通过网络进行通信,首先需要确定pc和树莓派各自的ip地址,确定PC机和树莓派谁作为ros的master,本文以PC端作为ros的master为例。

1.修改环境变量

pc端和树莓派都输入sudo ~/.bashrc,在最后一行填入?

export ROS_MASTER_URI=https://${你的master主机的ip地址}:11311    //声明master主机ip地址
export ROS_HOSTNAME=${本机ip地址}

此时只需要在master主机启动roscore即可。

2.数据通信

树莓派和pc机之间的通信主要是利用topic,pc端节点向topic发送数据,树莓派订阅该topic即可收到数据。在这里列出发布者和订阅者的C++版通用代码模板。

发布者:

//头文件部分
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>

int main(int argc,char** argv)
{
    //初始化部分,前两个是命令行参数,第三个参数是发布者的节点名称,该名称必须唯一,不能重复
    ros::init(argc,argv,"publish_name");
    //创建节点句柄,方便对节点管理
    ros::NodeHandle n;
    //注册一个发布者,并告诉系统该节点将会发布以topic为话题的String类型消息。第二个参数表示消息发布队列大小
    ros::Publisher pub =  n.advertise<std_msgs::String>("topic",1000);
    //设置循环频率单位HZ,调用Rate::sleep()时会根据该频率休眠相应的时间,不需要时可以省略
    ros::Rate loop_rate(10);//不必要可省略

    //节点正常时则一直循环,发生异常会返回false跳出循环
    while(ros::ok())
    {
        //初始化std_msgs::String消息
        std_msgs::String pubmsgs;    //发布的消息
        std::stringssteram tempmsg;

        //注意消息的赋值方式,不能使用std::string tempmsgs直接赋值,会出现错误!
        tempmsg << "此处为发布的内容";
        pubmsgs.data = tempmsg.str();
        ROS_INFO("%s",pubmsgs.data.c_str());    //后面跟string类型数据会出现乱码情况!
    
        //该函数用来处理回调函数,为了功能无误,无论是否用到回调函数默认都加上
        ros::spinOnce();  
  
        loop_rate.sleep();//不必要可省略
    }
     return 0;  
}

订阅者:

由于C++支持C语言,所以可以选择两种方式编写,任选一种

//C语言风格
#include "ros/ros.h"
#include "std_msgs/String.h"

//回调函数
void SubCallback(const std_msgs::String::ConstPtr& msg)
{
    ROS_INFO("Receive: %s",msg->data.c_str());
}

int main(int argc,char** argv)
{
    ros::init(argc,argv,"subscribe");
    ros::NodeHandle n;
    
    //创建订阅者,订阅topic话题,注册回调函数    
    ros::Subscriber sub = n.subscribe("topic",1000,SubCallback);

    //循环等待回调函数,spinOnce只执行一次,spin循环执行
    ros::spin();
    return 0;
}
//C++风格
#include "ros/ros.h"
#include "std_msgs/String.h"

Class TopicSub{
    private:
        ros::NodeHandle n;
        ros::Subscriber sub;
    public:
        //构造函数,完成初始化
        TopicSub()
        {
             sub = n.subscribe("topic",1000,&TopicSub::SubCallback,this);
        }
        ~TopicSub(){}
        
        //回调函数
        void SubCallback(const std_msgs::String::ConstPtr& msg)
        {
            ROS_INFO("Receive: %s",msg->data.c_str());
        }
};

int main(int argc,char** argv)
{
    ros::init(argc,argv,"subscribe");
    TopicSub subscriber;
    ros::spin()
    return 0;
}

三、科大讯飞sdk下载

1.登录科大讯飞官网www.xfyun.com,注册登录后创建应用。

2.点击左上角sdk下载,选择应用,平台和功能

3.点击sdk下载并复制到虚拟机下

4.进入samples目录下,选择32位或64位的脚本运行即可编译

5.cd ../../bin,执行可执行文件即可看到运行结果

四、树莓派和STM32串口通信

关于树莓派串口的配置,网上有许多教程资源,在这里就略过不写;

树莓派和stm32之间通过串口进行通信,树莓派发送指令给串口,stm32接收后则执行命令。

由于命令长度不同,因此使用串口空闲中断进行接收,以下是串口配置代码

//usart1.c
#include "usart.h"
#include "string.h"
#include "analyse.h"

void Usart1_Init(u32 baud)
{
	GPIO_InitTypeDef GPIO_InitStruct;     /* 定义GPIO模块结构体类型变量 */
	USART_InitTypeDef USART_InitStruct;  /* 定义USART模块结构体类型变量 */
	NVIC_InitTypeDef NVIC_InitStructure;  /* 定义NVIC中断结构体类型变量 */
	
	// 设置UASART模块功能管脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  /* 使能GPIOA端口模块时钟 */
	// USART1_RX(PA10)浮空输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	// USART1_TX(PA9)复用推挽输出
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; 
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	// 设置USART模块工作模式
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 					// 使能USART1模块发送和接收
	USART_InitStruct.USART_BaudRate = baud;                      					// 设置USART1模块波特率
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;     					// USART1模块8位数据长度
	USART_InitStruct.USART_Parity = USART_Parity_No;               					// USART1模块禁止奇偶校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;            					// USART1模块1位停止位
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 	// 禁止硬件流
	USART_Init(USART1,&USART_InitStruct);      										// 参数初始化USART_3模块 
	
	// USART模块NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1 ;  					// 抢占优先级等级为1 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		    					// 响应优先级等级为3 
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									// 使能中断源NVIC中断 
	NVIC_Init(&NVIC_InitStructure);	 												// 使用NVIC_InitStructure 参数初始化NVIC控制器

	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);    								// 接收中断
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);									// 开启串口接受中断

	USART_Cmd(USART1, ENABLE);														// 使能串口1
}

//串口发送
void Usart1_Send_Str(u8 *Data)
{
	while( *Data != '\0')
	{
		while( !USART_GetFlagStatus(USART1,USART_FLAG_TC))
		{
		}			//发送完成
		USART_SendData(USART1,*Data);
		Data++;
	}
}

//中断服务函数
u8 tempbuff[128];//串口缓冲数组
u8 u1count = 0;
u8 rxflag = 0;//接收完成标志
void USART1_IRQHandler(void)                 
{
	int a;
	if(rxflag == 0 && USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
		tempbuff[u1count++] = USART_ReceiveData(USART1);	
	}
	if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)//空闲中断
	{
		a = USART1->DR;
		a = USART1->SR;
		rxflag = 1;//接受标志置1
		memset(command_buff,0,sizeof(command_buff));
		memcpy(command_buff,tempbuff,u1count);
		memset(tempbuff,0,sizeof(tempbuff));
		u1count = 0;
	}
}
//usart1.h
#ifndef __USART_H_
#define __USART_H_
#include "sys.h"

extern u8 rxflag;

void Usart1_Init(u32 baud);
void Usart1_Send_Str(u8 *Data);

#endif

stm32命令解析代码:

//analyse.c
#include "analyse.h"
#include "usart.h"
#include "string.h"
#include "motor.h"

u8 command_buff[128] = {0};
PidObject car_left;
PidObject car_right;

int Robot_Command(void)//command_buff命令解析函数
{
	if( (sizeof(command_buff) != 0) && (rxflag == 1) )	//如果接收到数据
	{
		rxflag = 0;
		if(strcmp((const char*)command_buff,"go") == 0)				return GO;
		else if(strcmp((const char*)command_buff,"back") == 0)		return BACK;
		else if(strcmp((const char*)command_buff,"left") == 0)		return LEFT;
		else if(strcmp((const char*)command_buff,"right") == 0)		return RIGHT;
		else if(strcmp((const char*)command_buff,"stop") == 0)		return STOP;
	}
	return STOP;
}

void Robot_Work(int command)
{
	switch(command)
	{
		case STOP:Motor_Stop();break;
		case GO:Motor_Forward(); break;
		case BACK:Motor_Back(); break;
		case LEFT:Motor_Left(); break;
		case RIGHT:Motor_Right(); break;
		default:break;
	}
}
#ifndef __ANALYSE_H_
#define __ANALYSE_H_
#include "sys.h"
#include "pid.h"
#define STOP	0	//制动
#define GO 		1	//前进
#define BACK	2	//后退
#define	LEFT	3	//左转
#define RIGHT	4	//右转

extern u8 command_buff[128];
extern	PidObject car_left;
extern	PidObject car_right;

int Robot_Command(void);//command_buff命令解析函数
void Robot_Work(int command);

#endif

五、opencv实现人脸识别

在PC端和树莓派分别创建ros工作空间,用来存放代码

1.pc端接收图像topic数据

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <cv_bridge/cv_bridge.h>
#include <iostream>
#include <string>

using namespace cv;
using namespace std;

CascadeClassifier face_detector;
string filepath = "/opt/ros/kinetic/share/OpenCV-3.3.1-dev/haarcascades/haarcascade_frontalface_alt.xml"; 

class ImageShow{
	private:
		ros::NodeHandle nh;	//定义ros句柄
		image_transport::ImageTransport it;	//
		image_transport::Subscriber	FaceShow;	//定义订阅者
		cv_bridge::CvImagePtr cv_ptr;//定义一个cvimage指针实例
	public:
		ImageShow():it(nh)
		{
			FaceShow = it.subscribe("image_compressed",1,&ImageShow::imageCallback,this,image_transport::TransportHints("compressed"));//选择图像压缩,否则帧数会过低
			cv::namedWindow("pi_image");
		}
		~ImageShow()
		{
			cv::destroyWindow("pi_image");
		}
		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("exception:%s",e.what());
			}
			if(!face_detector.load(filepath))
			{
				cout<<"could not load"<<endl;
			}
			Mat gray_src;
			cvtColor(cv_ptr->image,gray_src,COLOR_BGR2GRAY);
			equalizeHist(gray_src,gray_src);

			vector<Rect> faces;
			face_detector.detectMultiScale(gray_src,faces,1.1,3,0,Size(30,30));
			for(size_t t=0;t<faces.size();t++)
			{
				rectangle(cv_ptr->image,faces[t],Scalar(255,255,0),2,8,0);
			}
			image_show(cv_ptr->image);
		}
		void image_show(cv::Mat  img)
		{
			cv::imshow("pi_image",img);
			cv::waitKey(1);
		}
};

int main(int argc,char** argv)
{
	ros::init(argc,argv,"imageSub_node");
	ImageShow test;
	ros::spin();
}

2.树莓派端发布图像topic

#include <iostream>
#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>  // 将ROS下的sensor_msgs/Image消息类型转化为cv::Mat数据类型
#include <sensor_msgs/image_encodings.h> // ROS下对图像进行处理
#include <image_transport/image_transport.h> // 用来发布和订阅图像信息

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/videoio.hpp>

int main(int argc, char** argv)
{
    ros::init(argc, argv, "imageGet_node");  // ros初始化,定义节点名为imageGet_node
    ros::NodeHandle nh;                      // 定义ros句柄
    image_transport::ImageTransport it(nh);  //  类似ROS句柄
    image_transport::Publisher image_pub = it.advertise("image_compressed", 1);   // 发布话题名/cameraImage

    ros::Rate loop_rate(50);   // 设置刷新频率,Hz

    cv::Mat imageRaw;  // 原始图像保存
    cv::VideoCapture capture(0);   // 创建摄像头捕获,并打开摄像头0(一般是0,2....)

    if(capture.isOpened() == 0)      // 如果摄像头没有打开
    {
        std::cout << "Read camera failed!" << std::endl;
        return -1;
    }

    while(nh.ok())
    {
        capture.read(imageRaw);          // 读取当前图像到imageRaw
	cv::Size dsize = cv::Size(imageRaw.cols*0.5, imageRaw.rows*0.5);
	cv::Mat img2 = cv::Mat(dsize, CV_32S);
	cv::resize(imageRaw,img2,dsize);
        //cv::imshow("veiwer", imageRaw);  // 将图像输出到窗口
        sensor_msgs::ImagePtr  msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", img2).toImageMsg();  // 图像格式转换
        image_pub.publish(msg);         // 发布图像信息
        ros::spinOnce();                // 保证完整
        loop_rate.sleep();              // 照应上面设置的频率
        if(cv::waitKey(1) >= 0)         // 延时ms,按下任何键退出(必须要有waitKey,不然是看不到图像的)
            break;
    }
}

六、键盘控制小车

1.PC端键盘控制发布

#include <termios.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <boost/thread/thread.hpp>
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <sstream>

#define KEY_W	0X77    //w键
#define KEY_A	0X61    //a键
#define KEY_S	0X73    //s
#define KEY_D	0X64    //d
#define KEY_P	0X70    //p

struct termios cooked,raw;
int fd = 0;

class KeyControlNode{
	private:
		std_msgs::String msg;
		ros::NodeHandle n;
		ros::Publisher pub;
	public:
		KeyControlNode()
		{
			pub = n.advertise<std_msgs::String>("keycmd",1000);//向“keycmd”主题发布消息
		}
		~KeyControlNode(){}
		
		void keyboardloop()
		{
			char key;
			bool dirty = false;
			tcgetattr(fd,&cooked);
			memcpy(&raw,&cooked,sizeof(struct termios));
			raw.c_lflag &= ~(ICANON|ECHO);
			raw.c_cc[VEOL] = 1;
			raw.c_cc[VEOF] = 2;
			tcsetattr(fd,TCSANOW,&raw);
			puts("WASD 控制移动,P停止\n");
			
			struct pollfd ufd;
			ufd.fd = fd;
			ufd.events = POLLIN;
			
			while(1)
			{
				boost::this_thread::interruption_point();
				int num;
				std::stringstream ss;
                //利用boost库创建线程
				if( (num = poll(&ufd,1,250)) < 0)
				{
					perror("poll():");
					exit(0);
				}
				else if(num > 0)
				{
					if(read(fd,&key,1) < 0)
					{
						perror("read");
						exit(1);
					}
				}
				
				switch(key)
				{
					case KEY_W:
						ss<<"go";
						msg.data = ss.str();
						dirty = true;
						break;
					case KEY_A:
						ss<<"left";
						msg.data = ss.str();
						dirty = true;
						break;
					case KEY_S:
						ss<<"back";
						msg.data = ss.str();
						dirty = true;
						break;
					case KEY_D:
						ss<<"right";
						msg.data = ss.str();
						dirty = true;
						break;
					case KEY_P:
						ss<<"stop";
						msg.data = ss.str();
						dirty = true;
						break;
					default:
						ss<<"";
						msg.data = ss.str();
						dirty = true;
						break;
				}	
				key = 0;
				ROS_INFO("%s",msg.data.c_str());	
				pub.publish(msg);//消息发布
			}
		}
};

int main(int argc,char** argv)
{
	ros::init(argc,argv,"key",ros::init_options::AnonymousName|ros::init_options::NoSigintHandler);
	KeyControlNode tbk;
	
    //线程
	boost::thread t = boost::thread(boost::bind(&KeyControlNode::keyboardloop,&tbk));
	ros::spin();
	
	t.interrupt();
	t.join();
	tcsetattr(fd,TCSANOW,&cooked);
}

2.树莓派订阅主题获取命令

#include <stdio.h>
#include "wiringPi.h"
#include <stdlib.h>
#include "wiringSerial.h"//wiringPi库
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <string>

class SerialKeyboard{
	private:
		int fd;
		ros::NodeHandle n;
		ros::Subscriber sub;
		std::string oldmsg;
	public:
		SerialKeyboard(int baund,const char* dev_name)//构造函数初始化波特率和设备号
		{
			fd = serialOpen(dev_name,baund);
			if(wiringPiSetup()<0)
		 	{
		   		printf("Initialize fail!\r\n");
		  	}
			if(fd < 0)
			{
				printf("open serial fail!\r\n");
			}
            //订阅“keycmd”话题接收命令
			sub = n.subscribe("keycmd",1000,&SerialKeyboard::SerialkeyboardCallback,this);
			oldmsg = " ";

		}
		~SerialKeyboard()
		{
			serialClose(fd);
		}
		void SerialkeyboardCallback(const std_msgs::String::ConstPtr& msg)
		{
			ROS_INFO("pc send:%s",msg->data.c_str());
			if(msg->data.c_str() != oldmsg)
			{
				oldmsg = msg->data.c_str();
				serialPuts(fd,msg->data.c_str());	//串口发送给stm32	
			}		
		}
};


int main(int argc,char** argv)
{
	ros::init(argc,argv,"keycmd");

	SerialKeyboard key(115200,"/dev/ttyAMA0");//波特率115200,设备号ttyAMA0
	ros::spin();
  
    return 0;
}

六、PC语音控制树莓派

最后就是整合,通过语音去控制上述功能

1.PC端语音识别发送命令

将语音合成的语音放在工作空间新建的music目录下。

语音合成功能通过运行sdk的samples中的tts_online_sample中的.sh脚本编译,然后在bin目录执行可执行文件即可生成wav文件,其语音内容和文件名在tts_online_sample.c文件第151行进行修改

修改完成后,就能在bin目录获得wav文件,拷贝到工作空间的music目录下即可

将讯飞sdk的语音听写代码复制到工作空间里,.h头文件放在工作空间的include目录下,.c文件放在src目录下,修改iat_online_record_sample.c文件名为voice.cpp(可以任取)

?修改相关代码,添加ros模板,使其作为发布者发布语音识别的结果

/*
* 语音听写(iFly Auto Transform)技术能够实时地将语音转换成对应的文字。
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ros_image/qisr.h"
#include "ros_image/msp_cmn.h"
#include "ros_image/msp_errors.h"
#include "ros_image/speech_recognizer.h"
#include "ros/ros.h"
#include "std_msgs/String.h"

#define FRAME_LEN	640 
#define	BUFFER_SIZE	4096

static void show_result(char *string, char is_over)//显示识别结果
{
	printf("\rResult: [ %s ]", string);
	
	if(is_over)
		putchar('\n');
}

static char *g_result = NULL;
static unsigned int g_buffersize = BUFFER_SIZE;
std_msgs::String msgs;//定义消息全局变量

void on_result(const char *result, char is_last)
{
	if (result) {
		size_t left = g_buffersize - 1 - strlen(g_result);
		size_t size = strlen(result);
		if (left < size) {
			g_result = (char*)realloc(g_result, g_buffersize + BUFFER_SIZE);
			if (g_result)
				g_buffersize += BUFFER_SIZE;
			else {
				ROS_INFO("mem alloc failed\n");
				return;
			}
		}
		strncat(g_result, result, size);
		show_result(g_result, is_last);
		if(g_result != "")
			msgs.data = g_result;
	}
}
void on_speech_begin()
{
	if (g_result)
	{
		free(g_result);
	}
	g_result = (char*)malloc(BUFFER_SIZE);
	g_buffersize = BUFFER_SIZE;
	memset(g_result, 0, g_buffersize);

	ROS_INFO("Start Listening...\n");
}
void on_speech_end(int reason)
{
	if (reason == END_REASON_VAD_DETECT)
		ROS_INFO("\nSpeaking done \n");
	else
		ROS_INFO("\nRecognizer error %d\n", reason);
}

/* demo recognize the audio from microphone */
static void demo_mic(const char* session_begin_params)
{
	int errcode;
	int i = 0;

	struct speech_rec iat;

	struct speech_rec_notifier recnotifier = {
		on_result,
		on_speech_begin,
		on_speech_end
	};

	errcode = sr_init(&iat, session_begin_params, SR_MIC, &recnotifier);
	if (errcode) {
		ROS_INFO("speech recognizer init failed\n");
		return;
	}
	errcode = sr_start_listening(&iat);
	if (errcode) {
		ROS_INFO("start listen failed %d\n", errcode);
	}
	/* demo 15 seconds recording */
	while(i++ < 5)
		sleep(1);
	errcode = sr_stop_listening(&iat);
	if (errcode) {
		ROS_INFO("stop listening failed %d\n", errcode);
	}

	sr_uninit(&iat);
}

/* main thread: start/stop record ; query the result of recgonization.
 * record thread: record callback(data write)
 * helper thread: ui(keystroke detection)
 */
int main(int argc, char** argv)
{
	int ret = MSP_SUCCESS;
	/* login params, please do keep the appid correct */
	const char* login_params = "appid = ${你自己的ID}, work_dir = .";
	int aud_src = 0; /* from mic or file */

	/*
	* See "iFlytek MSC Reference Manual"
	*/
	const char* session_begin_params =
		"sub = iat, domain = iat, language = zh_cn, "
		"accent = mandarin, sample_rate = 16000, "
		"result_type = plain, result_encoding = utf8";

	/* Login first. the 1st arg is username, the 2nd arg is password
	 * just set them as NULL. the 3rd arg is login paramertes 
	 * */
	ret = MSPLogin(NULL, NULL, login_params);
	if (MSP_SUCCESS != ret)	{
		ROS_INFO("MSPLogin failed , Error code %d.\n",ret);
		MSPLogout(); // Logout...
	}
	ros::init(argc,argv,"VoiceRecognize");
	ros::NodeHandle n;
	
    //发布消息到“voicewords”主题
	ros::Publisher voicepub = n.advertise<std_msgs::String>("voicewords",1000);
	
	while(ros::ok())
	{
		ROS_INFO("Demo recognizing the speech from microphone\n");
		ROS_INFO("Speak in 5 seconds\n");

		demo_mic(session_begin_params);

		ROS_INFO("5 sec passed\n"); 
		voicepub.publish(msgs);//发布数据
	}
	
	ros::spin();
	MSPLogout();//退出讯飞云登录

	return 0;
}

新建voicecmd.cpp使其作为订阅者接收语音识别数据,并处理,代码如下图

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "signal.h"
#include "sys/types.h"
#include "sys/wait.h"

class VoicecmdSub{
	private:
		ros::NodeHandle n;
		std_msgs::String cmd;
		ros::Subscriber voicesub;	//定义订阅着
		ros::Publisher 	cmdpub;		//定义发布者
		std::string oldmsg;
		bool opencam;                //摄像头打开标志
		bool openface;                //人脸识别打开标志
		bool openkeyboard;            //键盘控制打开标志
		pid_t pid_cam;				//摄像头子进程号
		pid_t pid_face;				//人脸识别子进程号
		pid_t pid_keyboard;			//键盘控制子进程号
	public:
		VoicecmdSub()
		{
			cmdpub = n.advertise<std_msgs::String>("keycmd",1000);//发布到“keycmd”上
			voicesub = n.subscribe("voicewords",1000,&VoicecmdSub::voicecmdCallback,this);//订阅“voicewords”主题
			oldmsg = "";
			opencam = false;
			openface = false;
			openkeyboard = false;
			pid_cam = -1;
			pid_face = -1;
			pid_keyboard = -1;
		}
		~VoicecmdSub(){}
		void voicecmdCallback(const std_msgs::String::ConstPtr& msg)
		{
			std::string msgs = msg->data.c_str();//这里语音转发后使用std_msgs::String会出现乱码情况,所以采取std::string
			std::stringstream ss;
			ss<<"";
			if(msgs != oldmsg)
			{	
				oldmsg = msgs;
				std::cout<<"I Heard:"<<msgs<<std::endl;
				
				//通过识别的命令执行对应的操作,利用system函数创建进程播放合成的语音
				if(msgs == "好兄弟在吗?")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/hxdwz.wav");
				}
				else if(msgs == "你能做什么?")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/allcmd.wav");
				}
				else if(msgs == "前进。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/cmdrcv.wav");
					ss<<"go";
				}
				else if(msgs == "后退。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/cmdrcv.wav");
					ss<<"back";
				}
				else if(msgs == "左转。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/cmdrcv.wav");
					ss<<"left";
				}
				else if(msgs == "右转。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/cmdrcv.wav");
					ss<<"right";
				}
				else if(msgs == "打开摄像头。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/opencam.wav");
					ss<<"opencam";
					if(!opencam)
					{
						opencam = true;
						pid_cam = fork();//创建子进程执行操作
						if(pid_cam == 0)
						{	
                        //利用execl函数,使用rosrun指令						        execl("/opt/ros/kinetic/bin/rosrun","rosrun","ros_image","imageSub",NULL);
						}
					}	
				}
				else if(msgs == "关闭摄像头。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/closecam.wav");
					ss<<"closecam";
					if(opencam)
					{
						opencam = false;
						if(pid_cam > 0)
						{
							kill(pid_cam,SIGKILL);//杀死子进程
							wait(NULL);
						}
					}	
				}
				else if(msgs == "打开人脸识别。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/faceopen.wav");
					ss<<"opencam";
					if(!openface)
					{
						openface = true;
						pid_face = fork();
						if(pid_face == 0)
						{	
							execl("/opt/ros/kinetic/bin/rosrun","rosrun", "ros_image", "face",NULL);
						}
					}	
				}
				else if(msgs == "关闭人脸识别。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/faceclose.wav");
					ss<<"closecam";
					if(openface)
					{
						openface = false;
						if(pid_face > 0)
						{
							kill(pid_face,SIGKILL);
							wait(NULL);	
						}
					}	
				}
				else if(msgs == "打开键盘控制。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/keyopen.wav");
					if(!openkeyboard)
					{
						openkeyboard = true;
						pid_keyboard = fork();
						if(pid_keyboard == 0)
						{
							execl("/opt/ros/kinetic/bin/rosrun","rosrun","ros_image","key_control",NULL);
						}
					}	
				}
				else if(msgs == "关闭键盘控制。")
				{
					system("mplayer /home/mzq/catkin_ws/src/ros_image/music/keyclose.wav");
					if(openkeyboard)
					{	
						openkeyboard = false;
						if(pid_keyboard > 0)
						{
							kill(pid_keyboard,SIGKILL);
							wait(NULL);
						}
					}	
				}
				cmd.data = ss.str();
				cmdpub.publish(cmd);//发布消息
			}
			
		}
};		

int main(int argc,char** argv)
{
	ros::init(argc,argv,"voicecmdpub");
	VoicecmdSub it;
	ros::spin();
	return 0;
}

2.树莓派端接收指令

利用上文的按键控制订阅的代码,修改一下

#include <stdio.h>
#include "wiringPi.h"
#include <stdlib.h>
#include "wiringSerial.h"
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <string>
#include "sys/types.h"
#include "unistd.h"
#include "sys/wait.h"
#include "signal.h"

class SerialKeyboard{
	private:
		int fd;
		ros::NodeHandle n;
		ros::Subscriber sub;
		std::string oldmsg;
		bool opencam;
		pid_t pid_cam;
	public:
		SerialKeyboard(int baund,const char* dev_name)
		{
			fd = serialOpen(dev_name,baund);
			if(wiringPiSetup()<0)
		 	{
		   		printf("Initialize fail!\r\n");
		  	}
			if(fd < 0)
			{
				printf("open serial fail!\r\n");
			}
			sub = n.subscribe("keycmd",1000,&SerialKeyboard::SerialkeyboardCallback,this);
			oldmsg = " ";
			opencam = false;
			pid_cam = -1;
		}
		~SerialKeyboard()
		{
			serialClose(fd);
		}
		void SerialkeyboardCallback(const std_msgs::String::ConstPtr& msg)
		{
			ROS_INFO("pc send:%s",msg->data.c_str());
			if(msg->data.c_str() != oldmsg)
			{
				oldmsg = msg->data.c_str();
				if(oldmsg == "opencam")//打开摄像头,打开人脸识别都是这个命令
				{	
				    if(!opencam)
					{	
						opencam = true;							
						pid_cam = fork();//创建进程
						if(pid_cam == 0)
						{
							execl("/opt/ros/kinetic/bin/rosrun","rosrun", "mycamera","image_Get",NULL);
							
						}
					}	
				}
				else if(oldmsg == "closecam")//关闭摄像头或者关闭人脸识别
				{
					if(opencam)
					{
						//pid_cam = -1;
						opencam = false;
						if(pid_cam > 0)
						{
							kill(pid_cam,SIGKILL);//杀死子进程
							wait(NULL);
						}
					}
				}	
				else	
					serialPuts(fd,msg->data.c_str());		
			}		
		}
};


int main(int argc,char** argv)
{
	ros::init(argc,argv,"keycmd");
	SerialKeyboard key(115200,"/dev/ttyAMA0");
	ros::spin();
  
  return 0;
}

该处使用的url网络请求的数据。

3.修改工作空间的CMakeList

添加下列语句

(1)PC端

add_compile_options(-std=c++11)

find_package(catkin REQUIRED COMPONENTS
? cv_bridge
? image_transport
? roscpp
? rospy
? sensor_msgs
? std_msgs
)
find_package(OpenCV REQUIRED)
find_package(Boost)

catkin_package(
? INCLUDE_DIRS include
? #LIBRARIES ros_image
? CATKIN_DEPENDS ${catkin_deps}
# ?DEPENDS system_lib
)

include_directories(
?include
? ${catkin_INCLUDE_DIRS}
)
include_directories(
# include
? ${OpenCV_INCLUDE_DIRS}
)

#生成可执行文件
add_executable(imageSub src/show.cpp)
add_executable(face src/face.cpp)
add_executable(key_control src/keycontrol.cpp)
add_executable(voicepub src/voice.cpp src/speech_recognizer.c src/linuxrec.c)
add_executable(voicesub src/voicecmd.cpp)
#链接
target_link_libraries(imageSub ${catkin_LIBRARIES} ${OpenCV_LIBS})
target_link_libraries(face ${catkin_LIBRARIES} ${OpenCV_LIBS})
target_link_libraries(key_control ${catkin_LIBRARIES} ${Boost_LIBRARIES})
target_link_libraries(voicepub ${catkin_LIBRARIES} libmsc.so -ldl -lpthread -lm -lrt -lasound)
target_link_libraries(voicesub ${catkin_LIBRARIES})

(2)树莓派端

find_package(catkin REQUIRED COMPONENTS
  cv_bridge
  image_transport
  roscpp
  rospy
  sensor_msgs
  std_msgs
)
find_package(OpenCV REQUIRED)
set(wiringPi_include "/usr/include")

include_directories(
# include
  ${catkin_INCLUDE_DIRS}
  ${wiringPi_include}
  LINK_DIRECTORIES("usr/lib")
)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(image_Get src/pub.cpp)
add_executable(imageSub src/camera.cpp)
add_executable(keycmd src/keycmd.cpp)

target_link_libraries(image_Get ${catkin_LIBRARIES} ${OpenCV_LIBS})
target_link_libraries(imageSub ${catkin_LIBRARIES} ${OpenCV_LIBS})
target_link_libraries(keycmd ${catkin_LIBRARIES} wiringPi)

4.编译

回到pc端和树莓派工作空间根目录,分别执行catkin_make命令,编译成功

5.运行

pc端打开三个终端,分别按顺序输入以下命令

roscore? ? ? ? //启动ros

rosrun ros_image voicepub //启动voicepub节点发布语音识别消息

rosrun ros_image voicesub? ?//启动voicesub节点订阅语音识别消息并发布命令给树莓派

树莓派打开一个终端输入

rosrun mycamera keycmd? ? ? ? //启动keycmd节点接收命令

6.结果


总结

小车还是很好玩的,小车底盘的程序大家可以自己写,我的不好我就不贴出来了哈哈哈

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-07-11 16:38:36  更:2021-07-11 16:38:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 2:41:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码