前文中记录了随机移动机器人的开发过程,本文内容为Turtlebot3 Gazebo仿真环境下Teleop-bot 键盘操控移动机器人,主要包含以下几个部分:

1、键盘驱动(按键驱动发布keys话题)

2、运动生成器(订阅keys话题发布cmd_vel话题)

3、速度斜坡曲线

4、参数服务器

6、rviz 机器人、传感器和算法 3D可视化系统使用

1、键盘驱动(按键驱动发布keys话题)

代码及注释如下:

#!/usr/bin/env python
#-*- coding:utf-8   -*-

import sys
import select 
import tty
import termios 
import rospy
from std_msgs.msg import String

key_pub=rospy.Publisher('keys',String,queue_size=1)
rospy.init_node('keyboard_driver')
rate=rospy.Rate(100)

# 保存原来属性
old_attr=termios.tcgetattr(sys.stdin)
# 设置为单字符响应模式
tty.setcbreak(sys.stdin.fileno())
print "Publishing keystrokes. Press Ctrl+C to exit..."

while not rospy.is_shutdown():
    if select.select([sys.stdin],[],[],0)[0]==[sys.stdin]:
        # 发布按键
        key_pub.publish(sys.stdin.read(1))
    rate.sleep()
#恢复属性
termios.tcsetattr(sys.stdin,termios.TCSADRAIN,old_attr)

可以通过rostopic echo命令查看keys 话题发布的消息:

wsc@wsc-pc:~/wanderbot_ws$ rostopic echo keys
data: "a"
---
data: "a"
---
data: "a"
---
data: "z"
---
data: "s"

2、运动生成器(订阅keys话题发布cmd_vel话题)

运动生成器代码及注释如下:

#!/usr/bin/env python
#-*- coding:utf-8   -*-

import rospy
from std_msgs.msg import String
from geometry_msgs.msg import Twist

# 定义按键定义
key_mapping={
    'w':[0,1],
    'x':[0,-1],
    'a':[-1,0],
    'd':[1,0], 
    's':[0,0]   
}

# 接收keys话题回调函数
def keys_callback(msg,twist_pub):
    if len(msg.data)==0 or (not key_mapping.has_key(msg.data[0])):
        return
    vels=key_mapping[msg.data[0]]
    t=Twist()
    t.angular.z=vels[0]
    t.linear.x=vels[1]
    twist_pub.publish(t)


if  __name__ == '__main__':
    rospy.init_node('keys_to_twist')
    twist_pub=rospy.Publisher('cmd_vel',Twist,queue_size=1)
    keys_sub=rospy.Subscriber('keys',String,keys_callback,twist_pub)
    rospy.spin()

运行rqt_plot 命令显示发布的话题数据:

3、参数服务器

上面我们按下键盘给出了固定的速度大小,我们可以使用ROS的参数系统来决定线速度和角速度,另外,上面的程序仅在按键时发布一条消息,我们可以利用rospy.Rate(10)改进代码使得程序以10HZ的频率稳定地发布话题。改进后的代码及注释如下:

#!/usr/bin/env python
#-*- coding:utf-8   -*-

import rospy
from std_msgs.msg import String
from geometry_msgs.msg import Twist

# 定义按键定义
key_mapping={
    'w':[0,1],
    'x':[0,-1],
    'a':[-1,0],
    'd':[1,0], 
    's':[0,0]   
}
g_last_twist=None

# 速度因子参数
g_vel_scals=[0.1,0.1]

# 接收keys话题回调函数
def keys_callback(msg,twist_pub):
    global g_last_twist,g_vel_scals
    if len(msg.data)==0 or (not key_mapping.has_key(msg.data[0])):
        return
    vels=key_mapping[msg.data[0]]
    g_last_twist=Twist()
    g_last_twist.angular.z=vels[0]*g_vel_scals[0]
    g_last_twist.linear.x=vels[1]*g_vel_scals[0]
    twist_pub.publish(g_last_twist)


if  __name__ == '__main__':
    rospy.init_node('keys_to_twist')
    twist_pub=rospy.Publisher('cmd_vel',Twist,queue_size=1)
    keys_sub=rospy.Subscriber('keys',String,keys_callback,twist_pub)
    g_last_twist=Twist()

    # 判断是否有linear_scale参数 
    if rospy.has_param('~linear_scale'):
        g_vel_scals[1]=rospy.get_param('~linear_scale')
    else:
        rospy.logwarn("linear_scale not provided,using %.1f"%g_vel_scals[1])

    # 判断是否有angular_scale参数 
    if rospy.has_param('~angular_scale'):
        g_vel_scals[0]=rospy.get_param('~angular_scale')
    else:
        rospy.logwarn("angular_scale not provided,using %.1f"%g_vel_scals[0])

    rate=rospy.Rate(10)
    while not rospy.is_shutdown():
        twist_pub.publish(g_last_twist)
        rate.sleep()

设置参数值方法 :rosrun teleop_bot  keys_to_twist_with_ramps.py  _linear_scale:=0.5 _angular_scale:=0.5

4、速度斜坡曲线

从第2步中发布的cmd_vel 话题数据图形显示可以看出,速度从1到-1是突变的,但是实际的物体不能瞬时启动、停止,机器人电机瞬间切到一个相差较大的速度时,可能会发生严重的后果。底层的机器人驱动器固件可能会对控制加速度进行平滑,但是最科学的办法还是在向机器人发布cmd_vel时就考虑到这一点,下面给出了改进的代码,限制了瞬时加速度。

#!/usr/bin/env python
#-*- coding:utf-8   -*-

import rospy
import math
from std_msgs.msg import String
from geometry_msgs.msg import Twist

# 定义按键定义
key_mapping={
    'w':[0,1],
    'x':[0,-1],
    'a':[-1,0],
    'd':[1,0], 
    's':[0,0]   
}

g_twist_pub=None

g_target_twist=None
g_last_twist=None

g_last_send_time=None

# 速度因子参数
g_vel_scals=[0.1,0.1]
g_vel_ramps=[1.0,1.0]

def fetch_params(name,default):
    # 判断是否有name参数 
    if rospy.has_param(name):
        return rospy.get_param(name)
    else:
        print "%s not provided,using %.1f"%(name,default)
        return default

def send_twist():
    global g_last_twist,g_target_twist,g_last_send_time,g_vel_ramps,g_twist_pub

    t_now=rospy.Time.now()
    g_last_twist=ramped_twist(g_last_twist,g_target_twist,g_last_send_time,t_now,g_vel_ramps)
    g_twist_pub.publish(g_last_twist)
    g_last_send_time=t_now
    

def ramped_vel(v_prev,v_target,t_prev,t_now,ramp_rate):
    step=(t_now-t_prev).to_sec()*ramp_rate
    if v_target>v_prev:
        sign=1
    else:
        sign=-1
    if (math.fabs(v_target-v_prev))>step:
        return v_prev+step*sign
    else:
        return v_target

def ramped_twist(prev,target,t_prev,t_now,ramps):
    tw=Twist()
    tw.angular.z=ramped_vel(prev.angular.z,target.angular.z,t_prev,t_now,ramps[0])
    tw.linear.x=ramped_vel(prev.linear.x,target.linear.x,t_prev,t_now,ramps[1])
    return tw

# 接收keys话题回调函数
def keys_callback(msg):
    global g_target_twist,g_vel_scals
    if len(msg.data)==0 or (not key_mapping.has_key(msg.data[0])):
        return
    vels=key_mapping[msg.data[0]]
    g_target_twist=Twist()
    g_target_twist.angular.z=vels[0]*g_vel_scals[0]
    g_target_twist.linear.x=vels[1]*g_vel_scals[0]


if  __name__ == '__main__':
    rospy.init_node('keys_to_twist')
    g_last_send_time=rospy.Time.now()
    g_twist_pub=rospy.Publisher('cmd_vel',Twist,queue_size=1)
    keys_sub=rospy.Subscriber('keys',String,keys_callback)
    
    g_last_twist=Twist()
    g_target_twist=Twist()

    g_vel_scals[1]=fetch_params('~linear_scale',g_vel_scals[1])
    g_vel_scals[0]=fetch_params('~angular_scale',g_vel_scals[0])

    g_vel_ramps[1]=fetch_params('~linear_accl',g_vel_ramps[1])
    g_vel_ramps[0]=fetch_params('~angular_accl',g_vel_ramps[0])

    rate=rospy.Rate(10)
    while not rospy.is_shutdown():
        send_twist()
        rate.sleep()

结果如下图,速度不再突变:

python 机械手图形模拟 python机械仿真_rirz

 

6、rviz 机器人、传感器和算法 3D可视化系统使用

rviz可以通过RoboWare Studio 菜单打开,也可以通过终端命令打开。

(1)选择参考坐标系,过程如下:

python 机械手图形模拟 python机械仿真_ROS_02

(2)添加机器人模型

python 机械手图形模拟 python机械仿真_Turtlebot3_03

(3)添加传感器

python 机械手图形模拟 python机械仿真_rirz_04

(4)转换视角观察

python 机械手图形模拟 python机械仿真_速度斜坡曲线_05

(5)在rviz中观察键盘驱动Turtlebot3运动

python 机械手图形模拟 python机械仿真_ROS_06