赞
踩
最近几天在学习语音识别、语音合成,应用到ROS当中实现一些简单的案例,下面是使用语音来控制turtle小乌龟走设定图形路线的案例。
ROS小乌龟走设定图形路线(键盘控制+Python代码实现)_笨小古的博客-CSDN博客
在上面代码的基础上加了一个语音控制函数 :
- # 接收到语音命令后发布速度指令
- def get_voice(data):
- voice_text=data.data
- rospy.loginfo("语音已接收")
-
- if voice_text == "走圆圈。":
- name = 'circle'
- rospy.loginfo("I said:: %s",voice_text)
- Turtlerun(name)
- elif voice_text == "走方形。":
- name = 'squ'
- rospy.loginfo("I said:: %s",voice_text)
- Turtlerun(name)
- elif voice_text == "走漏斗形。":
- name = 'hourglass'
- rospy.loginfo("I said:: %s",voice_text)
- Turtlerun(name)
- elif voice_text == "走60度三角形。":
- name = 'tri60'
- rospy.loginfo("I said:: %s",voice_text)
- Turtlerun(name)
- elif voice_text == "走直角三角形。":
- name = 'tri90'
- rospy.loginfo("I said:: %s",voice_text)
- Turtlerun(name)
完整代码:
- #!/usr/bin/env python3
- # coding: utf-8
-
- import math
- import rospy
- import sys
- from turtlesim.msg import Pose # Pose数据类型包含乌龟的坐标和角度
- from geometry_msgs.msg import Twist # Twist数据类型包含线速度和角速度
- from std_msgs.msg import String
-
-
- class Turtle:
- def __init__(self, name, graph):
- rospy.init_node(name) # 初始化节点
- rospy.Subscriber('/turtle1/pose', Pose, self.control) # 实例化订阅者,参数为订阅的话题名,消息类型,回调函数
- self.pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10) # 实例化发布者,参数为发布的话题名,消息类型,队列长度
- self._graph = graph
- self.size = 5 # 图形的大小(3-5)
- self.kp1 = 6 # 走直线的比例控制参数
- self.kp2 = 4 # 转角度的比例控制参数
- self.kd = 0 # 走直线的微分控制参数
- self.aim = 0.025 # 直走的误差值
- self.aim_rotate = 0.005 # 转弯的误差值
- self.vel_cmd = Twist() # 实例化Twist消息类型的消息
- self.point = {} # 储存所走路径目标点
- self.x = None # 乌龟目前所在x坐标
- self.y = None # 乌龟目前所在y坐标
- self.theta = None # 乌龟目前角度
- self.goal = 0 # 乌龟下一个要到达的目标点
- self.error = None # 现在距离目标点的误差值
- self.flag = 0 # flag和lock实现走直线和转角度的互锁,执行完一个才能执行另一个
- self.lock = 0
- self.key = 0
- self.theta_list = []
- self.speed = 4 # 控制旋转时的方向,顺or逆
-
- def control(self, pose):
- """订阅的回调函数"""
- self.choose_graph(self._graph, pose) # 设定要走什么形状
-
- def choose_graph(self, graph, pose):
- """根据传入的图形设定目标点,获取当前位置,控制运动"""
- self.set_goal_points(pose, self.size)
- rospy.loginfo(self.point['squ'][0][0])
- self.get_present_point(pose)
- self.go_graph(graph, self.kp1, self.kp2, self.kd, self.aim)
-
- def set_goal_points(self, pose, size=5):
- """设定不同形状的目标点,坐标和角度"""
- if self.key == 0: # 只设定一次
- self.point = {
- 'squ': [
- [pose.x + size, pose.y, math.pi / 2],
- [pose.x + size, pose.y + size, math.pi],
- [pose.x, pose.y + size, - math.pi / 2],
- [pose.x, pose.y, 0],
- ],
-
- 'squ2': [
- [pose.x + size, pose.y, -math.pi / 2],
- [pose.x + size, pose.y - size, -math.pi],
- [pose.x, pose.y - size, math.pi / 2],
- [pose.x, pose.y, 0],
- ],
-
- 'squ3': [
- [pose.x, pose.y, math.pi / 2],
- [pose.x, pose.y + size, 0],
- [pose.x + size, pose.y + size, -math.pi / 2],
- [pose.x + size, pose.y, -math.pi]
- ],
-
- 'rec': [
- [pose.x + 5, pose.y, math.pi / 2],
- [pose.x + 5, pose.y + 3, math.pi],
- [pose.x, pose.y + 3, math.pi / 2],
- [pose.x, pose.y, 0],
- ],
-
- 'tri_60': [
- [pose.x + size, pose.y, math.pi * 2 / 3],
- [pose.x + size / 2, pose.y + (size / 2 * math.tan(math.pi / 3)), - math.pi * 2 / 3],
- [pose.x, pose.y, 0]
- ],
-
- 'tri_60_2': [
- [pose.x + size, pose.y, -math.pi * 2 / 3],
- [pose.x + size / 2, pose.y - (size / 2 * math.tan(math.pi / 3)), math.pi * 2 / 3],
- [pose.x, pose.y, 0]
- ],
-
- 'tri_60_3': [
- [pose.x, pose.y, -math.pi / 3],
- [pose.x + size / 2, pose.y - (size / 2 * math.tan(math.pi / 3)), -math.pi],
- [pose.x - size / 2, pose.y - (size / 2 * math.tan(math.pi / 3)), math.pi / 3]
- ],
-
- 'hourglass': [
- [pose.x, pose.y, -math.pi / 3],
- [pose.x + 3 / 2, pose.y - (3 / 2 * math.tan(math.pi / 3)), -math.pi],
- [pose.x - 3 / 2, pose.y - (3 / 2 * math.tan(math.pi / 3)), math.pi / 3],
- [pose.x + 3 / 2, pose.y + (3 / 2 * math.tan(math.pi / 3)), math.pi],
- [pose.x - 3 / 2, pose.y + (3 / 2 * math.tan(math.pi / 3)), -math.pi / 3]
- ],
-
- 'tri_90': [
- [pose.x + size, pose.y, math.pi / 2],
- [pose.x + size, pose.y + size, -3 * math.pi / 4],
- [pose.x, pose.y, 0]
- ]
- }
- self.key += 1
-
- def get_present_point(self, pose):
- """获取当前坐标和角度"""
- self.x = pose.x
- self.y = pose.y
- self.theta = pose.theta
-
- def go_graph(self, graph, kp1=4, kp2=4, kd=15, aim=0.025):
- """控制运动"""
- self.go_line(graph, kp1, kd, aim)
- self.rotate(graph, kp2)
-
- def set_goal(self, graph):
- """设定下一个目标点"""
- if self.lock == 1:
- print(self.goal)
- self.goal = self.goal + 1 if self.goal != len(self.point[graph]) - 1 else 0
-
- def go_line(self, graph, kp=2, kd=15, aim=0.08, ):
- """控制走直线"""
- if self.lock == 0: # 和旋转互锁
- if self.flag == 0: # 第一次进函数执行一次初始化
- self.error = self.size # 初始误差为设定的图形大小
- self.flag += 1 # 保证初始化只执行一次
- last_error = self.error # 上一次的误差
- self.error = math.sqrt(
- (self.x - self.point[graph][self.goal][0]) ** 2 + (
- self.y - self.point[graph][self.goal][1]) ** 2) # 计算现在的误差
- if abs(self.error) > aim: # 误差大于设定精度时前进
- self.vel_cmd.linear.x = kp * self.error + kd * (self.error - last_error) # pd控制计算当前速度
- rospy.loginfo('mode:going')
- rospy.loginfo('goal:{},error:{},speed:{}'.format(self.goal, self.error, self.vel_cmd.linear.x))
- else:
- self.vel_cmd.linear.x = 0
- self.lock = 1 # 解锁旋转
- self.pub.publish(self.vel_cmd) # 发布乌龟速度
-
- def rotate(self, graph, kp=2):
- """控制旋转"""
- if self.lock == 1: # 和直走互锁
- if self.flag == 1: # 第一次进函数执行一次初始化函数
- self.set_goal(graph)
- theta1 = self.theta + 2 * math.pi if self.theta < 0 else self.theta # 将现在的角度换算成0-2π
- theta2 = theta1 + math.pi if theta1 <= math.pi else theta1 - math.pi # 和现在的角度差π的角
- target = self.point[graph][self.goal - 1][2] + 2 * math.pi if self.point[graph][self.goal - 1][2] < 0 \
- else self.point[graph][self.goal - 1][2] # 将目标角度换算到0-2π
- self.speed = kp if theta1 < target < theta2 or theta1 > theta2 and not (theta1 > target > theta2) \
- else -kp # 判断顺时针转还是逆时针转距离最短
- self.flag += 1 # 保证只执行一次初始化
- self.error = abs(self.theta - self.point[graph][self.goal - 1][2]) # 计算当前误差
- if self.error > self.aim_rotate: # 误差大于0.01时保持旋转
- self.vel_cmd.angular.z = self.speed * (self.error + 0.1) # p控制计算旋转速度
- rospy.loginfo('mode:rotating')
- rospy.loginfo('goal:{},error:{},speed:{}'.format(self.goal, self.error, self.vel_cmd.angular.z))
- else:
- self.vel_cmd.angular.z = 0
- self.flag = 0
- self.lock = 0
- self.pub.publish(self.vel_cmd) # 发布乌龟速度
-
- # 接收到语音命令后发布速度指令
- def get_voice(data):
- voice_text=data.data
- rospy.loginfo("语音已接收")
-
- if voice_text == "走方形。" or "squ":
- name = 'squ'
- rospy.loginfo("I said:: %s",voice_text)
- Turtle('voice_teleop',name)
- # Turtlerun.goal = Turtlerun.goals['squ']
- elif voice_text == "走漏斗形。":
- name = 'hourglass'
- rospy.loginfo("I said:: %s",voice_text)
- Turtle('voice_teleop',name)
- # Turtlerun.goal = Turtlerun.goals['hourglass']
- elif voice_text == "走60度三角形。":
- name = 'tri60'
- rospy.loginfo("I said:: %s",voice_text)
- Turtle('voice_teleop',name)
- # Turtlerun.goal = Turtlerun.goals['tri60']
- elif voice_text == "走直角三角形。":
- name = 'tri90'
- rospy.loginfo("I said:: %s",voice_text)
- Turtle('voice_teleop',name)
-
- if __name__ == '__main__':
- a = input("选择控制方式:1.命令行;2.语音控制\n")
- a = int(a)
-
-
- if a == 1:
- graph_list = ['circle', 'squ', 'tri60', 'tri90', 'hourglass']
- # print("input")
- # print("-------",sys.argv)
- if len(sys.argv) > 1 and sys.argv[1] in graph_list: # 实现rosrun xx yy.py squ直接跑对应图形
- name = sys.argv[1]
- # print("input finish")
- else:
- name = input(
- 'Please input graph name(circle squ tri60 tri90 hourglass): ') # 没在命令输对图形名称时提示输入
- try:
- name = str(name)
- Turtle('voice_graph',name)
- rospy.spin()
- except rospy.ROSInterruptException:
- pass
-
- else:
- rospy.init_node('voice_teleop')
- while not rospy.is_shutdown():
- rospy.loginfo("Starting voice Teleop")
- # 订阅语音识别的输出字符
- rospy.Subscriber("/voiceWords", String, get_voice)
- rospy.spin()
这里的语音功能包使用的是科大讯飞的,需要到科大讯飞网站注册帐号(需要APPID),讯飞开放平台-以语音交互为核心的人工智能开放平台
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。