通过Gazebo仿真学TurtleBot3(四)——简单的/cmd_vel控制

1. 通过命令发送/cmd_vel控制底盘移动

- 启动tb3仿真:

  启动:

$ roslaunch tb3_sim_bringup tb3_empty_world.launch

  开启rviz显示:

$ rosrun rviz rviz -d `rospack find tb3_sim_bringup`/rviz/demo.rviz
- 通过命令发布/cmd_vel话题控制底盘:
$ rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.1, y: 0, z: 0}, angular: {x: 0, y: 0, z: -0.5}}'

  tb3开始移动,效果如下:
这里写图片描述
  要想停止移动,首先按ctrl+c终止rostopic发送,再输入:

$ rostopic pub -1 /cmd_vel geometry_msgs/Twist '{}'

2. 通过node节点发送/cmd_vel控制底盘移动

- 停止之前的仿真:

  分别按ctrl+c停止之前的节点。

- 从新启动仿真

  启动:

$ roslaunch tb3_sim_bringup tb3_empty_world.launch

  开启rviz显示:

$ rosrun rviz rviz -d `rospack find tb3_sim_bringup`/rviz/demo.rviz

  运行timed_out_and_back.py开启一个新节点。

$ rosrun tb3_sim_nav timed_out_and_back.py

这里写图片描述

- 代码解析
#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist
from math import pi

class OutAndBack():
    def __init__(self):
        # Give the node a name
        rospy.init_node('out_and_back', anonymous=False)

        # Set rospy to execute a shutdown function when exiting       
        rospy.on_shutdown(self.shutdown)

        # Publisher to control the robot's speed
        self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=1)

        # How fast will we update the robot's movement?
        rate = 50

        # Set the equivalent ROS rate variable
        r = rospy.Rate(rate)

        # Set the forward linear speed to 0.2 meters per second 
        linear_speed = 0.2

        # Set the travel distance to 1.0 meters
        goal_distance = 1.0

        # How long should it take us to get there?
        linear_duration = goal_distance / linear_speed

        # Set the rotation speed to 1.0 radians per second
        angular_speed = 1.0

        # Set the rotation angle to Pi radians (180 degrees)
        goal_angle = pi

        # How long should it take to rotate?
        angular_duration = goal_angle / angular_speed

        # Loop through the two legs of the trip  
        for i in range(2):
            # Initialize the movement command
            move_cmd = Twist()

            # Set the forward speed
            move_cmd.linear.x = linear_speed

            # Move forward for a time to go the desired distance
            ticks = int(linear_duration * rate)

            for t in range(ticks):
                self.cmd_vel.publish(move_cmd)
                r.sleep()

            # Stop the robot before the rotation
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)

            # Now rotate left roughly 180 degrees  

            # Set the angular speed
            move_cmd.angular.z = angular_speed

            # Rotate for a time to go 180 degrees
            ticks = int(goal_angle * rate)

            for t in range(ticks):           
                self.cmd_vel.publish(move_cmd)
                r.sleep()

            # Stop the robot before the next leg
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)    

        # Stop the robot
        self.cmd_vel.publish(Twist())

    def shutdown(self):
        # Always stop the robot when shutting down the node.
        rospy.loginfo("Stopping the robot...")
        self.cmd_vel.publish(Twist())
        rospy.sleep(1)

if __name__ == '__main__':
    try:
        OutAndBack()
    except:
        rospy.loginfo("Out-and-Back node terminated.")

  该段代码主要实现前进一个指定量的距离,以及转动一个指定量的角度。具体实现思路是通过恒定线速度乘以时间来估计距离,通过恒定角速度乘以时间来估计转动角度。可以看出这是一种非常简单的开环控制,因此在物理机器人实际环境中的应用十分不可靠,但是仿真效果还行。要想实现精确的闭环控制还需要结合/odom话题来加以实现。

3. /odom与/cmd_vel控制的结合

- 停止之前的仿真:

  分别按ctrl+c停止之前的节点。

- 从新启动仿真

  启动:

$ roslaunch tb3_sim_bringup tb3_empty_world.launch

  开启rviz显示:

$ rosrun rviz rviz -d `rospack find tb3_sim_bringup`/rviz/demo.rviz

  运行odom_out_and_back.py开启一个新节点。

$ rosrun tb3_sim_nav odom_out_and_back.py

这里写图片描述

- 代码解析
#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist, Point, Quaternion
import tf
from transform_utils import quat_to_angle, normalize_angle
from math import radians, copysign, sqrt, pow, pi

class OutAndBack():
    def __init__(self):
        # Give the node a name
        rospy.init_node('out_and_back', anonymous=False)

        # Set rospy to execute a shutdown function when exiting       
        rospy.on_shutdown(self.shutdown)

        # Publisher to control the robot's speed
        self.cmd_vel = rospy.Publisher('/cmd_vel', Twist, queue_size=5)

        # How fast will we update the robot's movement?
        rate = 20

        # Set the equivalent ROS rate variable
        r = rospy.Rate(rate)

        # Set the forward linear speed to 0.15 meters per second 
        linear_speed = 0.15

        # Set the travel distance in meters
        goal_distance = 1.0

        # Set the rotation speed in radians per second
        angular_speed = 0.5

        # Set the angular tolerance in degrees converted to radians
        angular_tolerance = radians(1.0)

        # Set the rotation angle to Pi radians (180 degrees)
        goal_angle = pi

        # Initialize the tf listener
        self.tf_listener = tf.TransformListener()

        # Give tf some time to fill its buffer
        rospy.sleep(2)

        # Set the odom frame
        self.odom_frame = '/odom'

        # Find out if the robot uses /base_link or /base_footprint
        try:
            self.tf_listener.waitForTransform(self.odom_frame, '/base_footprint', rospy.Time(), rospy.Duration(1.0))
            self.base_frame = '/base_footprint'
        except (tf.Exception, tf.ConnectivityException, tf.LookupException):
            try:
                self.tf_listener.waitForTransform(self.odom_frame, '/base_link', rospy.Time(), rospy.Duration(1.0))
                self.base_frame = '/base_link'
            except (tf.Exception, tf.ConnectivityException, tf.LookupException):
                rospy.loginfo("Cannot find transform between /odom and /base_link or /base_footprint")
                rospy.signal_shutdown("tf Exception")  

        # Initialize the position variable as a Point type
        position = Point()

        # Loop once for each leg of the trip
        for i in range(2):
            # Initialize the movement command
            move_cmd = Twist()

            # Set the movement command to forward motion
            move_cmd.linear.x = linear_speed

            # Get the starting position values     
            (position, rotation) = self.get_odom()

            x_start = position.x
            y_start = position.y

            # Keep track of the distance traveled
            distance = 0

            # Enter the loop to move along a side
            while distance < goal_distance and not rospy.is_shutdown():
                # Publish the Twist message and sleep 1 cycle         
                self.cmd_vel.publish(move_cmd)

                r.sleep()

                # Get the current position
                (position, rotation) = self.get_odom()

                # Compute the Euclidean distance from the start
                distance = sqrt(pow((position.x - x_start), 2) + 
                                pow((position.y - y_start), 2))

            # Stop the robot before the rotation
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)

            # Set the movement command to a rotation
            move_cmd.angular.z = angular_speed

            # Track the last angle measured
            last_angle = rotation

            # Track how far we have turned
            turn_angle = 0

            while abs(turn_angle + angular_tolerance) < abs(goal_angle) and not rospy.is_shutdown():
                # Publish the Twist message and sleep 1 cycle         
                self.cmd_vel.publish(move_cmd)
                r.sleep()

                # Get the current rotation
                (position, rotation) = self.get_odom()

                # Compute the amount of rotation since the last loop
                delta_angle = normalize_angle(rotation - last_angle)

                # Add to the running total
                turn_angle += delta_angle
                last_angle = rotation

            # Stop the robot before the next leg
            move_cmd = Twist()
            self.cmd_vel.publish(move_cmd)
            rospy.sleep(1)

        # Stop the robot for good
        self.cmd_vel.publish(Twist())

    def get_odom(self):
        # Get the current transform between the odom and base frames
        try:
            (trans, rot)  = self.tf_listener.lookupTransform(self.odom_frame, self.base_frame, rospy.Time(0))
        except (tf.Exception, tf.ConnectivityException, tf.LookupException):
            rospy.loginfo("TF Exception")
            return

        return (Point(*trans), quat_to_angle(Quaternion(*rot)))

    def shutdown(self):
        # Always stop the robot when shutting down the node.
        rospy.loginfo("Stopping the robot...")
        self.cmd_vel.publish(Twist())
        rospy.sleep(1)

if __name__ == '__main__':
    try:
        OutAndBack()
    except:
        rospy.loginfo("Out-and-Back node terminated.")

  该段代码通过提取gazebo给出的base_frame与odom_frame之间的tf关系,给出机器人的实时位置和姿态角,再通过与起始时刻位置姿态进行对比,得到前进距离和转过角度的实时信息,将其反馈并与/cmd_vel结合,就形成了闭环控制。闭环控制的好处是可以较好的低效地面摩擦、转动轴承摩擦等阻力对于精确控制带来的影响,因此控制精度会有较大提升。但实际物理世界使用过程中odom里程计测量并不完美,会有随时间增长的不可逆的误差累计,因此实际使用过程中的效果也不会很理想,当然通过标定可以降低这些误差,但不能完全消除。
  在gazebo仿真下,以上两个例子的效果区分并不明显,而且与实际物理机器人使用效果有较大不同,其原因是gazebo仿真中目前并没有细化考虑这些阻力和传感器误差。如果希望可以与真实机器人使用结果更为接近,可以在gazebo中加入较大的摩擦阻力和传感器误差。不过这并不是我们作为初学者的学习目的,因此不必过于纠结。

3. 控制命令组合

  最后,我们再尝试一下上面运行命令的组合效果,跑一个正方形的轨迹。这基本上是之前运动命令的直接叠加,因此我们不再进行代码的解析,读者可以自行查看代码。

- 停止之前的仿真:

  分别按ctrl+c停止之前的节点。

- 从新启动仿真

  启动:

$ roslaunch tb3_sim_bringup tb3_empty_world.launch

  开启rviz显示:

$ rosrun rviz rviz -d `rospack find tb3_sim_bringup`/rviz/demo.rviz

  运行nav_square.py开启一个新节点。

$ rosrun tb3_sim_nav nav_square.py

  运行效果:
这里写图片描述


参考资料:
[1] ROS官方wiki:http://wiki.ros.org/
[2] TurtleBot3电子手册:http://emanual.robotis.com/docs/en/platform/turtlebot3/overview/
[3] 《ROS by Example》 R.Patrick Goebel

<think>好的,用户现在问的是“cmd_vel”,我需要先回想一下这是什么。之前他们问过DWA,可能现在是在继续习机器人运动控制相关的内容。 首先,确定“cmd_vel”是什么。在ROS(机器人操作系统)中,cmd_vel是一个常见的话题名称,用于传递速度命令。通常这个话题发布的是geometry_msgs/Twist类型的消息,包含线速度和角速度。用户可能是在使用ROS进行机器人开发时遇到了这个术语,需要了解它的具体含义和用法。 接下来,用户可能的身份是什么呢?很可能是刚开始接触ROS或者机器人编程的生或开发者。他们可能在尝试让机器人移动时,发现需要发布到cmd_vel话题,但不太清楚这个消息的结构或如何正确使用它。也有可能他们在调试过程中遇到了问题,比如机器人没有按预期移动,需要了解cmd_vel的参数设置。 需要进一步分析用户的深层需求。他们可能不仅仅想知道cmd_vel的定义,而是希望知道如何实际应用它,比如如何发布消息、如何设置速度值,或者如何与其他节点(如传感器或导航栈)配合使用。此外,用户可能对ROS的基本架构不太熟悉,需要简要解释相关概念,比如话题、消息类型等。 在回答时,需要保持结构清晰,先解释cmd_vel的基本概念,然后介绍消息结构,接着给出使用示例,最后说明注意事项和常见问题。这样用户能逐步理解,从理论到实践都有所覆盖。同时,需要注意使用中文,避免专业术语过于晦涩,必要时举例说明,比如线速度的单位是米/秒,角速度是弧度/秒,这样用户能更直观地设置合适的数值。 另外,用户之前的问题是关于DWA算法的,DWA生成的最终控制指令可能就是通过cmd_vel话题发布的速度命令,所以可以适当关联之前的回答,说明DWA算法和cmd_vel的关系,帮助用户建立知识连接。 最后,检查是否有需要补充的信息,比如常见的工具如rostopic或rqt_plot,这些工具能帮助用户调试和可视化cmd_vel的数据,提升他们的实际操作能力。同时,提醒用户注意安全,避免设置过高的速度导致机器人失控,这也是实际开发中容易遇到的问题。</think>`cmd_vel` 是机器人领域(尤其是ROS机器人操作系统)中常见的术语,表示 **控制机器人运动的线速度和角速度指令**。以下是详细介绍: --- ### **1. 基本概念** - **名称含义**:`cmd_vel` 是 **Command Velocity** 的缩写,即“速度指令”。 - **作用**:通过发送线速度(前进/后退)和角速度(旋转)的数值,直接控制机器人底盘的运动。 - **应用场景**: - 机器人导航(如ROS中的移动机器人) - 手动遥控(如通过键盘、手柄控制机器人) - 与其他算法(如SLAM、路径规划)结合使用。 --- ### **2. 消息结构与数据类型** 在ROS中,`cmd_vel` 通常以 **话题(Topic)** 形式存在,消息类型为 `geometry_msgs/Twist`。其数据结构如下: ```plaintext geometry_msgs/Twist: linear: # 线速度(单位:米/秒,m/s) x: 0.0 # 前后方向速度(前进为正,后退为负) y: 0.0 # 左右平移速度(非全向轮机器人通常为0) z: 0.0 # 竖直方向速度(地面机器人通常为0) angular: # 角速度(单位:弧度/秒,rad/s) x: 0.0 # 绕X轴旋转速度(通常为0) y: 0.0 # 绕Y轴旋转速度(通常为0) z: 0.0 # 绕Z轴旋转速度(顺时针为正,逆时针为负) ``` **常见示例**: - 直行:`linear.x = 0.5`, 其他分量均为0 - 原地旋转:`angular.z = 1.0` - 弧形运动:`linear.x = 0.3` 且 `angular.z = 0.5` --- ### **3. 实际使用方式** #### **(1) 通过ROS命令行发送指令** ```bash # 发送直行指令(线速度0.5 m/s) rostopic pub /cmd_vel geometry_msgs/Twist "linear: x: 0.5 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0" ``` #### **(2) 在代码中发布消息(Python示例)** ```python import rospy from geometry_msgs.msg import Twist rospy.init_node(&#39;cmd_vel_publisher&#39;) pub = rospy.Publisher(&#39;/cmd_vel&#39;, Twist, queue_size=10) msg = Twist() msg.linear.x = 0.2 # 设置线速度 msg.angular.z = 0.5 # 设置角速度 pub.publish(msg) ``` --- ### **4. 注意事项** 1. **单位一致性**: - 线速度单位为 **米/秒(m/s)** - 角速度单位为 **弧度/秒(rad/s)**(例如:`angular.z = 1.0` 表示每秒旋转约57.3度)。 2. **物理限制**: - 需根据机器人硬件性能设置速度值(如电机最大转速)。 - 超出硬件限制可能导致指令失效或机器人失控。 3. **安全设计**: - 通常需要设计 **“急停”逻辑**,例如监听外部信号并发送 `linear.x=0, angular.z=0`。 4. **与底层驱动的对接**: - `cmd_vel` 指令会被底层控制器(如电机驱动节点)解析,并转换为电机转速。 --- ### **5. 调试工具** - **可视化**: - 使用 `rostopic echo /cmd_vel` 查看实时指令。 - 通过 `rqt_plot` 绘制速度曲线。 - **仿真测试**: 在Gazebo、RViz等仿真环境中验证指令效果。 如果需要更具体的代码示例或与其他模块(如导航栈)的交互方式,可以进一步说明需求!
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值