文章目录

  • 目录

    文章目录

    前言

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

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

    1.修改环境变量

    2.数据通信

    三、科大讯飞sdk下载

    四、树莓派和STM32串口通信

    五、opencv实现人脸识别

    1.pc端接收图像topic数据

    2.树莓派端发布图像topic

    六、键盘控制小车

    1.PC端键盘控制发布

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

    六、PC语音控制树莓派

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

    2.树莓派端接收指令

    3.修改工作空间的CMakeList

    (1)PC端

    (2)树莓派端

    4.编译

    5.运行

    6.结果

    总结


前言

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

本文需要一些ros基础,源码分享https://gitee.com/sy_run/myroscar


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

一、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;//接受标志置1memset(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_noderos::NodeHandle nh;                      // 定义ros句柄image_transport::ImageTransport it(nh);  //  类似ROS句柄image_transport::Publisher image_pub = it.advertise("image_compressed", 1);   // 发布话题名/cameraImageros::Rate loop_rate(50);   // 设置刷新频率,Hzcv::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);          // 读取当前图像到imageRawcv::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    //pstruct 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,设备号ttyAMA0ros::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 4096static 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");elseROS_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::stringstd::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 COMPONENTScv_bridgeimage_transportroscpprospysensor_msgsstd_msgs
)
find_package(OpenCV REQUIRED)
find_package(Boost)catkin_package(INCLUDE_DIRS include#LIBRARIES ros_imageCATKIN_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 COMPONENTScv_bridgeimage_transportroscpprospysensor_msgsstd_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.结果


总结

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

基于ROS的语音控制机器人(一):基本功能的实现相关推荐

  1. 一种基于蓝牙和语音控制的智能家居系统【100010378】

    2018年大学生电子设计竞赛设计报告 一种基于蓝牙和语音控制的智能家居系统 摘要:本项目使用 STM32F407 作为主控芯片,通过蓝牙和语音控制实现安全.便宜的智能家居系统.实现了手机实现家电控制: ...

  2. 基于SPCE061A的语音控制小车设计

    目 录 第一章 前言--------------------------1 第二章 语音控制小车设计要求-----------------2 2.1 功能设计要求------------------- ...

  3. 无线控制模块c语言编程,基于STM32F103ZET6无线语音控制小车设计与实现.doc

    基于STM32F103ZET6无线语音控制小车设计与实现 基于STM32F103ZET6无线语音控制小车设计与实现 摘要:本文以STM32F103ZET6单片机作为控制核心,通过LD3320语音识别模 ...

  4. ROS+科大讯飞语音识别控制机器人

    科大讯飞语音识别控制实际机器人运动. 本文将ros与语音识别想结合进行开发.进行以下步骤, 1.创作ros工作空间 2.安装mpalyer播放器 sudo apt-get install mplaye ...

  5. 语音控制机器人小车运动科大讯飞SDK

    在前面文章小车实现语音识别的基础之上,对小车实现语音控制运动 修改CMakeLists.txt文件 在末尾加上以下代码: add_executable(sub_word src/sub_word.cp ...

  6. 语音控制Office,这个功能一定要体验

    面向 Beta 频道的 Windows 用户,今天微软发布了全新的 Office Insider Preview Build 14228.20044(Beta Channel).本次更新在修复了一系列 ...

  7. #基于ROS的编队控制

    前言 研究生新生,着手研究编队控制.本文为一个记录:在ROS自带的小乌龟环境下实现基于领航者的三角形编队控制. 提示:以下是本篇文章正文内容,下面案例可供参考 一.基于领航者-跟随者的编队控制方法实现 ...

  8. 【论文笔记】基于 ROS 的送餐机器人自主抓取实现

    目录 摘要 关键词 1 主要任务 2 六自由度机械臂的二次开发 2.1 用户向控制板发送数据 CMD_SERVO_MOVE参数指令 2.2 控制板向用户返回数据 3 基于 Tsai-Lenz 算法的自 ...

  9. 基于树莓派的语音对话机器人

    第一部分代码 arecord -D "plughw:1" -f S16_LE -r 16000 -d 3 /home/pi/Desktop/voice.wav 第二部分代码 # c ...

最新文章

  1. mysql 转成树_mysql整形转换的坑
  2. 求两个数组的最长重复子数组 Maximum Length of Repeated Subarray
  3. linux chmod命令使用
  4. html 正则表达式验证金额,js金额校验,js正则表达式,包含正负,小数点后两位...
  5. Android开发:1-1、UI编程基础----基本介绍
  6. 年轻人逃离大城市之后的下一站选哪儿?用数据来为你揭晓
  7. PMON和SMON的功能
  8. 1. HTML DOM Document 对象
  9. 云科技网络验证源码_黑科技网络验证软件 1.52.0.0免费版
  10. Web版的各种聊天工具
  11. AUTOCAD——线宽设置
  12. linux开机自动启动sh脚本
  13. bootstrap-select 通过拼音搜索汉字下拉框方法
  14. 《AWR Adaptive Weighting Regression for 3D Hand Pose Estimation》研读与实践
  15. QT项目负责人必须掌握的Ui设计师功能——Promote to !
  16. win10 桌面体验 服务器,windows server 2012 R2 安装桌面体验
  17. 《老梁四大名著情商课》笔记- 别慌,情商是可以提升的
  18. Microsoft Visual Studio 注册码
  19. UI设计这个专业现在如何,未来就业前景都有哪些不错的选择
  20. 联考事业单位计算机类面试,2018年5.26事业单位联考E类常见面试题(下)

热门文章

  1. HCIA/HCIP使用eNSP模拟VRRP配置实验(接入层 汇聚层 核心层 VLAN OSPF VRRP STP DHCP的综合应用)
  2. 新概念英语(第一册)复习(原文及全文翻译)——Lesson 61 - Lesson 90
  3. python爬取网易云音乐生成王力宏歌曲词云
  4. Halcon图像修复
  5. 医院影像图像科室工作站PACS系统 DICOM 三维图像后处理与重建
  6. 百里挑一:ICLR 2021杰出论文奖出炉!
  7. 【Unity】【Android】问题记录
  8. 51单片机精确延时设计
  9. Kalman滤波算法原理(Matlab/C/C++)
  10. ApacheCN 公众号文章汇总 2019.9