机器人语音交互之ros集成科大讯飞星火认知大模型,实现聊天机器人
1 背景和资料
本文是机器人语音交互交互第二篇,我们将基于上一篇ROS高效进阶第五章 – 机器人语音交互之ros集成科大讯飞中文语音库,实现语音控制机器人小车的 robot_voice 样例,集成科大讯飞星火大模型,做一个语音聊天机器人。这里提醒,本文的测试环境是ubuntu20.04 + ros noetic。
本文参考资料如下:
(1)《ROS机器人开发实践》胡春旭 第8章
(2)开发者如何使用讯飞星火认知大模型API?
(3)ROS高效进阶第五章 – 机器人语音交互之ros集成科大讯飞中文语音库,实现语音控制机器人小车
2 robot_voice 之聊天机器人
(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]
3 总结
本文的样例托管在本人的github上:robot_voice