赞
踩
用来记录一下我的ros学习过程,小车主要用到的有stm32和树莓派。
本文需要一些ros基础,源码分享https://gitee.com/sy_run/myroscar
提示:以下是本篇文章正文内容,下面案例可供参考
关于安装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和树莓派各自的ip地址,确定PC机和树莓派谁作为ros的master,本文以PC端作为ros的master为例。
pc端和树莓派都输入sudo ~/.bashrc,在最后一行填入
- export ROS_MASTER_URI=https://${你的master主机的ip地址}:11311 //声明master主机ip地址
- export ROS_HOSTNAME=${本机ip地址}
此时只需要在master主机启动roscore即可。
树莓派和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;
- }

1.登录科大讯飞官网www.xfyun.com,注册登录后创建应用。
2.点击左上角sdk下载,选择应用,平台和功能
3.点击sdk下载并复制到虚拟机下
4.进入samples目录下,选择32位或64位的脚本运行即可编译
5.cd ../../bin,执行可执行文件即可看到运行结果
关于树莓派串口的配置,网上有许多教程资源,在这里就略过不写;
树莓派和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

在PC端和树莓派分别创建ros工作空间,用来存放代码
- #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();
- }
-

- #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;
- }
- }

- #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);
- }

- #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;
- }

最后就是整合,通过语音去控制上述功能
将语音合成的语音放在工作空间新建的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;
- }

利用上文的按键控制订阅的代码,修改一下
- #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网络请求的数据。
添加下列语句
- 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})

- 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)

回到pc端和树莓派工作空间根目录,分别执行catkin_make命令,编译成功
pc端打开三个终端,分别按顺序输入以下命令
roscore //启动ros
rosrun ros_image voicepub //启动voicepub节点发布语音识别消息
rosrun ros_image voicesub //启动voicesub节点订阅语音识别消息并发布命令给树莓派
树莓派打开一个终端输入
rosrun mycamera keycmd //启动keycmd节点接收命令
小车还是很好玩的,小车底盘的程序大家可以自己写,我的不好我就不贴出来了哈哈哈
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。