ROS高效进阶第五章 -- 机器人语音交互之ros集成科大讯飞星火认知大模型,实现聊天机器人

机器人语音交互之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
robot_talker
voice_creator

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

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值