赞
踩
本文是机器人语音交互交互第二篇,我们将基于上一篇ROS高效进阶第五章 – 机器人语音交互之ros集成科大讯飞中文语音库,实现语音控制机器人小车的 robot_voice 样例,集成科大讯飞星火大模型,做一个语音聊天机器人。这里提醒,本文的测试环境是ubuntu20.04 + ros noetic。
本文参考资料如下:
(1)《ROS机器人开发实践》胡春旭 第8章
(2)开发者如何使用讯飞星火认知大模型API?
(3)ROS高效进阶第五章 – 机器人语音交互之ros集成科大讯飞中文语音库,实现语音控制机器人小车
(1)下载星火大模型API接口库:具体可以参考开发者如何使用讯飞星火认知大模型API?,整个过程类似上一篇的下载科大讯飞语音库。下载后,得到专属的SDK,如我的:static_16919926417702339_AIKit_Spark_Linux_SDK_v2.4.zip,下面我们将把这套 sdk 集成到 robot_voice 样例中。
(2)聊天机器人的拓扑图如下:
voice_detector:负责语音识别,将语音转换为文字,并作为 client,通过 human_chatter 服务,发给 robot_talker 。
robot_talker:提供 human_chatter 服务 server,接收 voice_detector 发来的文字化的指令,并作为星火大模型接口,向后台询问,获取答复,并将答复内容通过 str2voice 服务发给 voice_creator。
voice_creator:作为 str2voice 服务server,接收 robot_talker 发来的星火大模型答复内容,合成语音文件并播放。
补充:关于ros的服务机制,可以参考本人ROS高效入门博客第二章的2.6节: ROS高效入门第二章 – 基本概念和常用命令学习,基于小乌龟样例
(3)安装环境
unzip static_16919926417702339_AIKit_Spark_Linux_SDK_v2.4.zip
sudo cp libs/x64/libaikit.so /usr/lib/
(4)创建相关文件
cd ~/catkin_ws/src/robot_voice
touch launch/robot_talker.launch
touch src/robot_talker.cpp
mkdir include/ifly_spark
请将 static_16919926417702339_AIKit_Spark_Linux_SDK_v2.4.zip 中的相关文件,移入指定目录,供编译使用,如下图:
(5)robot_talker.cpp
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <iostream> #include <fstream> #include <assert.h> #include <atomic> #include <ros/ros.h> #include <std_msgs/String.h> #include <robot_voice/StringToVoice.h> #include "ifly_spark/aikit_spark_api.h" class IflySpark { public: IflySpark() { ROS_INFO("IflySpark Constructor"); } ~IflySpark() { ROS_INFO("IflySpark Destructor"); } int SendRequest(std::string& ask_str) { // clear answer_str_ IflySpark::answer_str_ = ""; IflySpark::is_over_ = false; //请求参数配置 AIKIT::ChatParam* config = AIKIT::ChatParam::builder(); config->uid("xxxid") ->domain("generalv2") ->auditing("default") ->url("ws://spark-api.xf-yun.com/v2.1/chat"); int ret = AIKIT::AIKIT_AsyncChat(config, ask_str.c_str(), &usr_); if(ret != 0) { ROS_ERROR("AIKIT_AsyncChat failed:%d\n",ret); return ret; } return ret; } static void ChatOnToken(AIKIT::AIChat_Handle* handle, const int& completionTokens, const int& promptTokens, const int& totalTokens) { if(handle!=nullptr){ std::cout<<"chatID:"<<((UsrCtx*)handle->usrContext)->chatID<<", "; } std::cout<<"completionTokens:"<<completionTokens<<" promptTokens:"<<promptTokens<<" totalTokens:"<<totalTokens<<std::endl; IflySpark::is_over_ = true; } static void ChatOnOutput(AIKIT::AIChat_Handle* handle, const char* role, const char* content, const int& index) { if(handle!=nullptr){ std::cout<<"chatID:"<<((UsrCtx*)handle->usrContext)->chatID<<", "; } std::string slice_txt = content; IflySpark::answer_str_ += slice_txt; } static void ChatOnError(AIKIT::AIChat_Handle* handle, const int& err, const char* errDesc) { if(handle!=nullptr){ std::cout<<"chatID:"<<((UsrCtx*)handle->usrContext)->chatID<<", "; } printf("chatOnError: err:%d,desc:%s\n",err,errDesc); } void InitSDK() { AIKIT_InitParam initParam{}; initParam.appID = "bb839ccf"; initParam.apiKey = "ab95d52b4ea9f2a5dc15e77ae3f778fa"; initParam.apiSecret = "MTAxZDllZDY1OTVjYmIwYzg2NDBlZWQ0"; AIKIT::AIKIT_SetLogInfo(100,0,nullptr); int ret = AIKIT::AIKIT_Init(&initParam); if(ret != 0) { ROS_ERROR("AIKIT_Init failed:%d",ret); return ; } //异步回调注册 AIKIT::AIKIT_ChatCallback({ ChatOnOutput, ChatOnToken, ChatOnError }); } void UnInit() { //逆初始化SDK AIKIT::AIKIT_UnInit(); } static std::string get_answer_str() { while (IflySpark::is_over_ == false || IflySpark::answer_str_ == "") { sleep(1); } return answer_str_; } private: typedef struct UsrCtx { std::string chatID; } UsrCtx_t; // 设置chatID,用于用户动态控制会话轮次 UsrCtx_t usr_ = {"FistRound"}; static std::string answer_str_; static bool is_over_; }; std::string IflySpark::answer_str_ = ""; bool IflySpark::is_over_ = false; class RobotTalker { public: RobotTalker() { ROS_INFO("RobotTalker Constructor"); } ~RobotTalker() { robot_talker_.UnInit(); ROS_INFO("RobotTalker Destructor"); } int Init(ros::NodeHandle& nh) { robot_talker_.InitSDK(); client_ = nh.serviceClient<robot_voice::StringToVoice>("str2voice"); return 0; } void ToDownstream(const std::string& answer_txt) { robot_voice::StringToVoice::Request req; robot_voice::StringToVoice::Response resp; req.data = answer_txt; bool ok = client_.call(req, resp); if (ok) { printf("send str2voice service success: %s\n", req.data.c_str()); } else { printf("failed to send str2voice service\n"); } } bool ChatterCallbback(robot_voice::StringToVoice::Request &req, robot_voice::StringToVoice::Response &resp) { printf("i received: %s\n", req.data.c_str()); std::string voice_txt = req.data; std::string answer_txt = ""; // 向后台发送申请 robot_talker_.SendRequest(voice_txt); // 获取后台反馈信息,并发给下游 answer_txt = IflySpark::get_answer_str(); if (answer_txt != "") { ToDownstream(answer_txt); printf("answer_txt = %s\n", answer_txt.c_str()); } resp.success = true; return resp.success; } void Start(ros::NodeHandle& nh) { // 申明 human_chatter 服务 chatter_server_ = nh.advertiseService("human_chatter", &RobotTalker::ChatterCallbback, this); } private: ros::ServiceServer chatter_server_; ros::ServiceClient client_; IflySpark robot_talker_; }; int main(int argc, char* argv[]) { int ret = 0; ros::init(argc, argv, "robot_talker"); ros::NodeHandle nh; RobotTalker rc; rc.Init(nh); ROS_INFO("this is a robot talker base ifly spark\n"); rc.Start(nh); ros::spin(); return 0; }
(6)robot_talker.launch 和 CMakeLists.txt
robot_talker.launch
<launch> <node pkg="robot_voice" type="voice_creator" name="voice_creator" output="screen" /> <node pkg="robot_voice" type="robot_talker" name="robot_talker" launch-prefix="bash -c 'sleep 5; $0 $@'" output="screen" /> <node pkg="robot_voice" type="voice_detector" name="voice_detector" launch-prefix="bash -c 'sleep 7; $0 $@'" output="screen" /> </launch>
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2) project(robot_voice) add_compile_options(-std=c++11) find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs geometry_msgs message_generation ) add_service_files( FILES StringToVoice.srv ) generate_messages( DEPENDENCIES std_msgs ) catkin_package( CATKIN_DEPENDS message_runtime roscpp rospy std_msgs ) include_directories( include ${catkin_INCLUDE_DIRS} ) add_executable(voice_detector src/voice_detector.cpp ifly_voice/speech_recognizer.c ifly_voice/linuxrec.c) add_executable(robot_controller src/robot_controller.cpp) add_executable(robot_talker src/robot_talker.cpp) add_executable(voice_creator src/voice_creator.cpp) add_dependencies(voice_detector ${PROJECT_NAME}_generate_messages_cpp) target_link_libraries(voice_detector ${catkin_LIBRARIES} libmsc.so -ldl -lpthread -lm -lrt -lasound ) add_dependencies(robot_controller ${PROJECT_NAME}_generate_messages_cpp) target_link_libraries(robot_controller ${catkin_LIBRARIES} ) add_dependencies(robot_talker ${PROJECT_NAME}_generate_messages_cpp) target_link_libraries(robot_talker ${catkin_LIBRARIES} libaikit.so ) add_dependencies(voice_creator ${PROJECT_NAME}_generate_messages_cpp) target_link_libraries(voice_creator ${catkin_LIBRARIES} libmsc.so -ldl -pthread )
(7)编译并运行(运行时请注意电脑网络通畅!)
cd ~/catkin_ws/
catkin_make --source src/robot_voice
source devel/setup.bash
roslaunch robot_voice robot_talker.launch
高级聊天机器人
(8)在开发调试过程中,出现了如下系统问题,参考这个博客得到解决:/dev/sda1 contains a file system with errors, check forced [duplicate]
本文的样例托管在本人的github上:robot_voice
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。