当前位置: 首页 > article >正文

ROS机器人建模与仿真设计——模型控制理论

之前我们学习了如何使用URDF来描述一个机器人,现在就开始学习如何让这个机器人控制跑起来。

首先,先把那个圆柱体补全成一个差速结构的小车。

下面开始编辑URDF文件,添加其他link和joint,每次添加后,需要重新编译代码,重启RVIZ2。

1. 添加imu

编写URDF,描述一个位于中心正上方2cm,长宽高均为2cm的正方体。

 <!-- imu link -->
 <link name="imu_link">
      <visual>
      <origin xyz="0 0 0.0" rpy="0 0 0"/>
      <geometry>
            <box size="0.02 0.02 0.02"/>
      </geometry>
    </visual>
  </link>

  <!-- imu joint -->
  <joint name="imu_joint" type="fixed">
      <parent link="base_link" />
      <child link="imu_link" />
      <origin xyz="0 0 0.02" />
  </joint>

给base_link添加一条材料属性,让它变成蓝色且透明度为%50:

  <!-- base link -->
  <link name="base_link">
      <visual>
      <origin xyz="0 0 0.0" rpy="0 0 0"/>
      <geometry>
        <cylinder length="0.12" radius="0.10"/>
      </geometry>
      <material name="blue">
        <color rgba="0.1 0.1 1.0 0.5" /> 
    </material>
    </visual>
  </link>

2. 添加轮子
2.1 主动轮
2.1.1 编写link

描述:

名称: right_wheel_link

​ 外形:宽为4cm,直径为6.4cm,几何形状是个圆柱体,黑色,透明度0.5

​ 位姿:圆柱体默认朝上的,需要让其绕x轴旋转pi/2

<origin xyz="0 0 0" rpy="1.57079 0 0"/>

完整的URDF描述:

  <link name="right_wheel_link">
      <visual>
        <origin xyz="0 0 0" rpy="1.57079 0 0"/>
        <geometry>
          <cylinder length="0.04" radius="0.032"/>
        </geometry>
          <material name="black">
            <color rgba="0.0 0.0 0.0 0.5" /> 
          </material>
      </visual>
  </link>
2.1.2 编写joint

主要设置originaxis值的设置。前者表示轮子与主体的相对位置。后者表示轮子运动的轴。

origin描述:

​ 因为base_link的高度是0.12,原点为base_link该圆柱体的几何中心。

  • z表示child相对parent的z轴上的关系,想将轮子固定在机器人的下表面,所以origin的z向下偏移0.12/2=0.06m(向下符号为负)

  • y表示child相对parent的y轴上的关系,base_link的半径是0.10,所以我们让轮子的y轴向负方向偏移0.10m(向左符号为负)

  • x表示child相对parent的x轴上的关系,向后偏移则是x轴向后进行偏移,我们用个差不多的值0.02m(向后符号为负)

axis描述:

关节类型设置为continuous(旋转关节,可以绕单轴无限旋转),轮子的旋转轴为Y轴,即"0 1 0"

完整的Joint代码:

<!-- right_wheel joint--> 
<joint name="right_wheel_joint" type="continuous">
      <parent link="base_link" />
      <child link="right_wheel_link" />
      <origin xyz="-0.02 -0.10 -0.06" />
      <axis xyz="0 1 0" />
  </joint>=

到此为止,主动轮的建模就完成了,至于左轮,和右轮对称,完整代码:

  <!-- right_wheel link-->
  <link name="right_wheel_link">
      <visual>
        <origin xyz="0 0 0" rpy="1.57079 0 0"/>
        <geometry>
          <cylinder length="0.04" radius="0.032"/>
        </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
      </visual>
  </link>

  <!-- right_wheel joint-->
  <joint name="right_wheel_joint" type="continuous">
      <parent link="base_link" />
      <child link="right_wheel_link" />
      <origin xyz="-0.02 -0.10 -0.06" />
      <axis xyz="0 1 0" />
  </joint>

  <!-- left_wheel link-->
  <link name="left_wheel_link">
    <visual>
      <origin xyz="0 0 0" rpy="1.57079 0 0"/>
      <geometry>
        <cylinder length="0.04" radius="0.032"/>
      </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
    </visual>
  </link>
  
  <!-- left_wheel joint-->
  <joint name="left_wheel_joint" type="continuous">
      <parent link="base_link" />
      <child link="left_wheel_link" />
      <origin xyz="-0.02 0.10 -0.06" />
      <axis xyz="0 1 0" />
  </joint>
2.2 支撑轮

支撑轮子固定在机器人的前方

描述:

小球的直径为0.032m与左右轮子半径相同;设置为半径为0.016m的球体;

主体的圆柱体高度0.12m;设置球体向下偏移0.016+0.06=0.076m,向下值为负

再把支撑轮向前移动0.06m

完整URDF代码:

 <link name="caster_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
          <sphere radius="0.016"/>
      </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
    </visual>
  </link>
    
  <joint name="caster_joint" type="fixed">
      <parent link="base_link" />
      <child link="caster_link" />
      <origin xyz="0.06 0.0 -0.076" />
  </joint>
2.3 运行

完整代码:

<?xml version="1.0"?>
<robot name="fishbot">
    
  <!-- base link -->
  <link name="base_link">
      <visual>
      <origin xyz="0 0 0.0" rpy="0 0 0"/>
      <geometry>
        <cylinder length="0.12" radius="0.10"/>
      </geometry>
      <material name="blue">
        <color rgba="0.1 0.1 1.0 0.5" /> 
    </material>
    </visual>
  </link>
    
  <!-- laser link -->
  <link name="laser_link">
      <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <cylinder length="0.02" radius="0.02"/>
      </geometry>
      <material name="black">
          <color rgba="0.0 0.0 0.0 0.8" /> 
      </material>
    </visual>
  </link>为
    
  <!-- laser joint -->
    <joint name="laser_joint" type="fixed">
        <parent link="base_link" />
        <child link="laser_link" />
        <origin xyz="0 0 0.075" />
    </joint>


    <!-- imu link -->
  <link name="imu_link">
      <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.02 0.02 0.02"/>
      </geometry>
      <material name="red">
          <color rgba="1.0 0.1 0.1 0.8" /> 
      </material>
    </visual>
  </link>

  <!-- imu joint -->
  <joint name="imu_joint" type="fixed">
    <parent link="base_link" />
    <child link="imu_link" />
    <origin xyz="0 0 0.02" />
  </joint>

  
  <!-- right_wheel link-->
  <link name="right_wheel_link">
      <visual>
        <origin xyz="0 0 0" rpy="1.57079 0 0"/>
        <geometry>
          <cylinder length="0.04" radius="0.032"/>
        </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
      </visual>
  </link>

  <!-- right_wheel joint-->
  <joint name="right_wheel_joint" type="continuous">
      <parent link="base_link" />
      <child link="right_wheel_link" />
      <origin xyz="-0.02 -0.10 -0.06" />
      <axis xyz="0 1 0" />
  </joint>

  <!-- left_wheel link-->
  <link name="left_wheel_link">
    <visual>
      <origin xyz="0 0 0" rpy="1.57079 0 0"/>
      <geometry>
        <cylinder length="0.04" radius="0.032"/>
      </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
    </visual>
  </link>
  
  <!-- left_wheel joint-->
  <joint name="left_wheel_joint" type="continuous">
      <parent link="base_link" />
      <child link="left_wheel_link" />
      <origin xyz="-0.02 0.10 -0.06" />
      <axis xyz="0 1 0" />
  </joint>

  <!-- caster link -->
  <link name="caster_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
          <sphere radius="0.016"/>
      </geometry>
        <material name="black">
          <color rgba="0.0 0.0 0.0 0.5" /> 
        </material>
    </visual>
  </link>

  <!-- caster joint -->
  <joint name="caster_joint" type="fixed">
      <parent link="base_link" />
      <child link="caster_link" />
      <origin xyz="0.06 0.0 -0.076" />
  </joint>

  
</robot>

通过joint state的滑条可以控制两个轮子的运动。

2.4 让车轮着地

由于fixed-frame选择的是base_link,所以地面实际上是在主体圆柱体的几何中心上,也就是轮子是在地下的。

通过在添加一个虚拟link,作为base_link的父link,定义相对关系Joint,将base_link抬到地面上。

虚拟link的描述:

虚拟节点不需要形状等参数描述。

只需要设置joint,即与base_link的关系,即base_link在base_footprint的正上方0.076m(车轮直径【0.016m】+主圆柱体高度/2【0.06m】 = 0.76m)

  <!-- Robot Footprint -->
  <link name="base_footprint"/>

  <joint name="base_joint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin xyz="0.0 0.0 0.076" rpy="0 0 0"/>
  </joint>

编译运行后,如下图所示,需要将Fixed Frame和Reference Frame更改为base_footprint。

然后可以看到,轮子现在处于地平面上。

3. 让车跑起来

运行后节点图

通过操控Joint_state_pubilsher_gui中的滑条,发布话题信息到Joint_states话题中,在传给robot_state_pubilsher最后通过robot_description话题与RVIZ2建立话题通信,在RVIZ2中对机器人的位姿进行更新并可视化。

如果要实现我们手动控制轮子,需要自己编写节点,取代joint_state_publisher发送关节位姿给robot_state_pubsher,robot_state_publisher再发送tf控制机器人的关节转动。

话题内容可以通过打印出来。

ros2 topic echo /joint_states

如下图所示,通过滑条控制轮子转动,并打印joint_states的话题信息。

内容如下:

---
header:
  stamp:
    sec: 1711443422
    nanosec: 148273725
  frame_id: ''
name:
- right_wheel_joint
- left_wheel_joint
position:
- 1.205743260447762
- 0.0
velocity: []
effort: []
---

大致实现步骤:

  1. 新建节点
  2. 创建发布者
  3. 编写发布逻辑
  4. 编译测试
3.1 新建节点
cd fishbot_ws
touch fishbot_description/fishbot_description/rotate_wheel.py

节点源码

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node


class RotateWheelNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info(f"node {name} init..")

def main(args=None):
    """
    ros2运行该节点的入口函数
    1. 导入库文件
    2. 初始化客户端库
    3. 新建节点
    4. spin循环节点
    5. 关闭客户端库
    """
    rclpy.init(args=args) # 初始化rclpy
    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

配置setup.py. 定义入口点

    entry_points={
        'console_scripts': [
            "rotate_wheel= fishbot_description.rotate_wheel:main"
        ],
    },

编译运行:

colcon build
source install/setup.bash
ros2 run fishbot_description rotate_wheel

执行节点:

3.2 创建发布者

创建发布者发布话题替代Joint_states话题。

首先需要直到Joint_states话题的类型:

通过如下命令查看

ros2 topic info /joint_states

返回结果:

Type: sensor_msgs/msg/JointState
Publisher count: 1
Subscription count: 1

可以看到该话题类型是 sensor_msgs/msg/JointState,我们进一步查询该类型,使用如下命令查询该类型:

ros2 interfaces show sensor_msgs/msg/JointState

返回结果:

# This is a message that holds data to describe the state of a set of torque controlled joints.
#
# The state of each joint (revolute or prismatic) is defined by:
#  * the position of the joint (rad or m),
#  * the velocity of the joint (rad/s or m/s) and
#  * the effort that is applied in the joint (Nm or N).
#
# Each joint is uniquely identified by its name
# The header specifies the time at which the joint states were recorded. All the joint states
# in one message have to be recorded at the same time.
#
# This message consists of a multiple arrays, one for each part of the joint state.
# The goal is to make each of the fields optional. When e.g. your joints have no
# effort associated with them, you can leave the effort array empty.
#
# All arrays in this message should have the same size, or be empty.
# This is the only way to uniquely associate the joint name with the correct
# states.

std_msgs/Header header

string[] name
float64[] position
float64[] velocity
float64[] effort

至此,明确了话题类型,可以开始编辑发布者代码:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
# 1.导入消息类型JointState
from sensor_msgs.msg import JointState


class RotateWheelNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info(f"node {name} init..")
        # 2. 创建并初始化发布者成员属性pub_joint_states
        self.pub_joint_states_ = self.create_publisher(JointState,"joint_states",10) # 缓存区域为10


def main(args=None):
    """
    ros2运行该节点的入口函数
    1. 导入库文件
    2. 初始化客户端库
    3. 新建节点
    4. spin循环节点
    5. 关闭客户端库
    """
    rclpy.init(args=args) # 初始化rclpy
    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

3.3 编写发布逻辑
3.3.1 多线程定频发布

现在有了发布者,需要发布者按照某个固定的速度发布话题信息去控制机器人。

为此,需要单独开一个线程,并使用多线程定频发布函数Rate。

import threading
from rclpy.node import Node

class RotateWheelNode(Node):
    def __init__(self):
        # 创建一个Rate和线程
        self.pub_rate = self.create_rate(5) #5Hz
        # 创建线程
        self.thread_ = threading.Thread(target=self._thread_pub)
        self.thread_.start()

    def _thread_pub(self):
        while rclpy.ok():
            #做一些操作,使用rate保证循环频率
            self.pub_rate.sleep()
3.3.2 构造发布数据

通过刚才的接口查询命令:

ros2 interfaces show sensor_msgs/msg/JointState

可以知道joint_state的数据格式:

# 这是一个持有数据的信息,用于描述一组扭矩控制的关节的状态。
#
# 每个关节(渐进式或棱柱式)的状态由以下因素定义。
# #关节的位置(rad或m)。
# #关节的速度(弧度/秒或米/秒)和
# #在关节上施加的力(Nm或N)。
#
# 每个关节都由其名称来唯一标识
# 头部规定了记录关节状态的时间。所有的联合状态
# 必须是在同一时间记录的。
#
# 这个消息由多个数组组成,每个部分的联合状态都有一个数组。
# 目标是让每个字段都是可选的。例如,当你的关节没有
# 扭矩与它们相关,你可以让扭矩数组为空。
#
# 这个信息中的所有数组都应该有相同的大小,或者为空。
# 这是唯一能将关节名称与正确的
# 状态。

std_msgs/Header header #时间戳信息 和 frame_id

string[] name #关节名称数组
float64[] position #关节位置数组
float64[] velocity #关节速度数组
float64[] effort #扭矩数据,本实验暂时不用

设置header

self.joint_states.header.stamp = self.get_clock().now().to_msg()
self.joint_states.header.frame_id = ""

设置name

name是关节的名称,要与URDF中的定义的关节名称相同,根据前文的URDF定义有:

self.joint_states.name = ['left_wheel_joint','right_wheel_joint']

设置position

表示关节转动的角度值,因为关节类型为continuous,所以其值无上下限制,初始值赋值为0.0

# 初始化关节的位置
self.joint_states.position = [0.0,0.0]


# 轮子的位置通过速度对时间的积分求得
delta_time =  time.time()-last_update_time
# 更新位置
self.joint_states.position[0]  += delta_time*self.joint_states.velocity[0]
self.joint_states.position[1]  += delta_time*self.joint_states.velocity[1]

设置velocity

因为是采用速度控制,所以需要一个控制速度的函数接口:

def update_speed(self,speeds):
    self.joint_speeds = speeds

完整代码:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
# 1.导入消息类型JointState
from sensor_msgs.msg import JointState

import threading
import time

class RotateWheelNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info(f"node {name} init..")
        # 创建并初始化发布者成员属性pub_joint_states_
        self.joint_states_publisher_ = self.create_publisher(JointState,"joint_states", 10) 
        # 初始化数据
        self._init_joint_states()
        # 设置Rate频率
        self.pub_rate = self.create_rate(30)
        # 创建线程
        self.thread_ = threading.Thread(target=self._thread_pub)
        # 开始线程
        self.thread_.start()

    
    def _init_joint_states(self):
        # 初始化jonit_states话题对象
        self.joint_states = JointState()
        # 设置header
        self.joint_states.header.stamp = self.get_clock().now().to_msg()
        self.joint_states.header.frame_id = ""
        # 关节名称
        self.joint_states.name = ['left_wheel_joint','right_wheel_joint']
        # 关节的位置
        self.joint_states.position = [0.0,0.0]
        # 初始左右轮子的速度
        self.joint_speeds = [0.0,0.0]
        # 关节速度,通过joint_speed进行赋值
        self.joint_states.velocity = self.joint_speeds
        # 力 
        self.joint_states.effort = []

    # 设置左右关节速度
    def update_speed(self,speeds):
        self.joint_speeds = speeds

    # 创建线程
    def _thread_pub(self):
        last_update_time = time.time()
        while rclpy.ok():
            # 一个更新周期,即前面设置的rate:1/30 s
            delta_time =  time.time()-last_update_time
            last_update_time = time.time()
            # 更新位置
            self.joint_states.position[0]  += delta_time*self.joint_states.velocity[0]
            self.joint_states.position[1]  += delta_time*self.joint_states.velocity[1]
            # 更新速度
            self.joint_states.velocity = self.joint_speeds
            # 更新 header
            self.joint_states.header.stamp = self.get_clock().now().to_msg()
            # 发布关节数据
            self.joint_states_publisher_.publish(self.joint_states)
            self.pub_rate.sleep()

def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
    node.update_speed([15.0,-15.0])
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

4. 关闭原发布者Joint_state_publisher_node

因为我们发布了自定义的joint_states话题,需要将原先的发布者关闭,直接修改下display_rviz2.launch.py文件,注释其joint_state_publisher节点。

ld.add_action(robot_state_publisher_node)
# ld.add_action(joint_state_publisher_node)
ld.add_action(rviz2_node)
5. 编译运行

方法一:

colcon build
source install/setup.bash

# 先运行rotate_wheel程序来启动rotate_fishbot_wheel节点
ros2 run fishbot_description  rotate_wheel 

在另外开一个终端launch:display_rviz2.launch.py

source install/setup.bash
ros2 launch fishbot_description display_rviz2.launch.py 

方法二:

直接更改launch文件,把我们创建的rotate_fishbot_wheel节点放到display_rviz2.launch.py 一起启动。

在display_rviz2.launch.py 代码中添加:

rotate_fishbot_wheel=Node(
    package='fishbot_description',  # Python节点所在的包名
    executable='rotate_wheel',  	# Python脚本名
    name='rotate_fishbot_wheel',  	# 节点的名称
    arguments=[urdf_model_path]
)
ld.add_action(rotate_fishbot_wheel)

完整display_rviz2.launch.py 代码:

import os
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
    package_name = 'fishbot_description'
    urdf_name = "fishbot_base.urdf"

    ld = LaunchDescription()
    pkg_share = FindPackageShare(package=package_name).find(package_name) 
    urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}')

    robot_state_publisher_node = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        arguments=[urdf_model_path]
        )

    joint_state_publisher_node = Node(
        package='joint_state_publisher_gui',
        executable='joint_state_publisher_gui',
        name='joint_state_publisher_gui',
        arguments=[urdf_model_path]
        )

    rviz2_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2',
        output='screen',
        )

    rotate_fishbot_wheel=Node(
        package='fishbot_description',  # 替换为你的Python节点所在的包名
        executable='rotate_wheel',  # 替换为你的Python脚本名
        name='rotate_fishbot_wheel',  # 节点的名称
        arguments=[urdf_model_path]
    )


    ld.add_action(rotate_fishbot_wheel)

    # ld.add_action(joint_state_publisher_node)
    ld.add_action(robot_state_publisher_node)
    ld.add_action(rviz2_node)

    return ld

然后直接编译启动就行:

colcon build 
source install/setup.bash
# 直接launch就行
ros2 launch fishbot_description display_rviz2.launch.py 

运行效果如下,可以看到左右轮子以相同速度相反方向匀速转动:

6. 通过参数控制轮子

前面通过程序给轮子赋了一个固定的速度运行,现在通过设置两个参数,在通过rqt工具控制轮子转速。

在init时定义左右轮转速参数

self.declare_parameter('left_wheel_speed', 0.0)  # 添加左轮速度参数,默认值为0.0
self.declare_parameter('right_wheel_speed', 0.0)  # 添加右轮速度参数,默认值为0.0

通过参数给joint_states赋值

joint_states.velocity = [self.get_parameter('left_wheel_speed').value, self.get_parameter('right_wheel_speed').value]

注释掉之前的速度赋值相关代码,完整代码如下:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
# 1.导入消息类型JointState
from sensor_msgs.msg import JointState
import threading
import time

class RotateWheelNode(Node):
    def __init__(self,name):
        super().__init__(name)
        self.get_logger().info(f"node {name} init..")

        # 定义左右轮子速度参数   
        self.declare_parameter('left_wheel_speed', 0.0)  # 添加左轮速度参数,默认值为0.0
        self.declare_parameter('right_wheel_speed', 0.0)  # 添加右轮速度参数,默认值为0.0
        
        # 创建并初始化发布者成员属性pub_joint_states_
        self.joint_states_publisher_ = self.create_publisher(JointState,"joint_states", 10) 
        # 初始化数据
        self._init_joint_states()
        # 设置Rate频率
        self.pub_rate = self.create_rate(30)
        # 创建线程
        self.thread_ = threading.Thread(target=self._thread_pub)
        # 开始线程
        self.thread_.start()

    def _init_joint_states(self):
        # 初始化jonit_states话题对象
        self.joint_states = JointState()
        # 设置header
        self.joint_states.header.stamp = self.get_clock().now().to_msg()
        self.joint_states.header.frame_id = ""
        # 关节名称
        self.joint_states.name = ['left_wheel_joint','right_wheel_joint']
        # 关节的位置
        self.joint_states.position = [0.0,0.0]
        # 初始左右轮子的速度
        self.joint_speeds = [0.0,0.0]

        # 关节速度,通过joint_speed进行赋值
        # self.joint_states.velocity = self.joint_speeds

        # 使用参数进行赋值,通过rqt工具设置参数的值以此控制轮子的速度
        self.joint_states.velocity = [self.get_parameter('left_wheel_speed').value, self.get_parameter('right_wheel_speed').value]

        # 力 
        self.joint_states.effort = []

    # 设置左右关节速度
    def update_speed(self,speeds):
        self.joint_speeds = speeds

    # 创建线程
    def _thread_pub(self):
        last_update_time = time.time()
        while rclpy.ok():
            # 一个更新周期,即前面设置的rate:1/30 s
            delta_time =  time.time()-last_update_time
            last_update_time = time.time()
            # 更新位置
            self.joint_states.position[0]  += delta_time*self.joint_states.velocity[0]
            self.joint_states.position[1]  += delta_time*self.joint_states.velocity[1]

            # 更新速度
            # self.joint_states.velocity = self.joint_speeds
            self.joint_states.velocity = [self.get_parameter('left_wheel_speed').value, self.get_parameter('right_wheel_speed').value]
            
            # 更新 header
            self.joint_states.header.stamp = self.get_clock().now().to_msg()
            # 发布关节数据
            self.joint_states_publisher_.publish(self.joint_states)
            self.pub_rate.sleep()

def main(args=None):
    rclpy.init(args=args) # 初始化rclpy
    node = RotateWheelNode("rotate_fishbot_wheel")  # 新建一个节点
    # node.update_speed([15.0,-15.0])
    rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
    rclpy.shutdown() # 关闭rclpy

编译运行

colcon build
source install/setup.bash
ros2 launch fishbot_description display_rviz2.launch.py 

在开一个终端,运行rqt工具

新的运行节点图,可见我们替换了joint_states话题的发布者。更换为了rotate_fishbot_wheel节点。

通过rqt界面左上角Plugins -> Configuration -> Dynamic Reconfigure,然后可以看到如下界面。选择rotate_fishbot_wheel节点,设置left_wheel_speed和right_wheel_speed两个参数的值就可以动态设置左右论转速。

上面将左轮速度设置为-20,可以看到如图,左轮旋转,右轮静止:


http://www.kler.cn/a/593720.html

相关文章:

  • 【文章写作】以数字素养筑基,绘治理现代化蓝图
  • Linux笔记之Ubuntu22.04安装IBus中文输入法教程
  • 大模型——Ollama-OCR 简明教程
  • 「JavaScript深入」Socket.IO:基于 WebSocket 的实时通信库
  • 在Orin上查看CUDA cuDNN TensorRT的版本
  • 进程管理笔记1-进程线程基础知识
  • QML开发入门1--安装QT6.8和新建第一个QtQuickApplication
  • 某公司制造业研发供应链生产数字化蓝图规划P140(140页PPT)(文末有下载方式)
  • 【NGINX代理附件上传服务配置优化】
  • python-websocket压力测试
  • 【Git学习笔记】深度理解Git的分布式版本控制系统及其管理
  • 【Python办公】提取Excel嵌入图片流程(代码前期步骤)
  • MySQL InnoDB大表DDL时出现唯一键冲突
  • 知识蒸馏: Distilling the Knowledge in a Neural Network(上)
  • SAME51J20A Curiosity Nano|支持Arduino开发,适用于物联网终端、工业控制及人机交互场景
  • 微信小程序:用户拒绝小程序获取当前位置后的处理办法
  • github上传本地文件到远程仓库(空仓库/已有文件的仓库)
  • 新型胶囊来助力!可无线监测上皮屏障
  • LS-NET-004-简单二层环路解决(华为锐捷思科)
  • 跨国生产制造企业:如何破解远距离数据传输难题?