【ROS2机器人入门到实战】通过JointStates控制RVIZ2关节

8.4 控制移动机器人轮子运动

写在前面

  1. 当前平台文章汇总地址:ROS2机器人从入门到实战
  2. 获取完整教程及配套资料代码,请关注公众号<鱼香ROS>获取
  3. 教程配套机器人开发平台:两驱版| 四驱版
  4. 为方便交流,搭建了机器人技术问答社区:地址 fishros.org.cn

我是小鱼,本节我们来看看如何手动的发送joint_states来控制机器人轮子连续转动

在这里插入图片描述

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

在这里插入图片描述

1.新建节点

2.创建发布者

3.编写发布逻辑

4.编译测试

1.新建节点

方便起见,我们就在fishbot_describle包中新建节点(参考李四节点代码)

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

在这里插入图片描述

2.创建发布者

创建发布者之前,要知道robot_state_pubsher所订阅的话题类型是什么?

回忆前面章节中学习的内容,我们可以采用如下指令查看

ros2 topic info /joint_states
Type: sensor_msgs/msg/JointState
Publisher count: 1
Subscription count: 1

接着

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) 

3.编写发布逻辑

3.1 多线程定频发布Rate

创建好发布者,我们想让话题按照某个固定的速度进行发布,可以采用ROS2中的定时神器Rate,不清楚Rate的小伙伴可以看看小鱼的这篇文章:ROS中的定频神器你会用吗

为了能够一直循环使用rate,我们单独开一个线程用于发布joint_states话题数据,在ROS2程序中单独开线程进行话题发布的方法为:

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.2 构造发布数据

接着我们来构造发布的数据:

joint_states有一个头和四个数组需要赋值(可通过ros2 interface指令查询)

std_msgs/Header header #时间戳信息 和 frame_id
string[] name
float64[] position
float64[] velocity
float64[] effort

对应的含义为:

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

name是关节的名称,要与urdf中的定义的关节名称相同,根据我们的URDF定义有

self.joint_states.name = ['left_wheel_joint','right_wheel_joint']
3.2.2 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]
3.2.3 velocity

因为我们采用速度进行控制,所以对外提供一个速度更改接口。

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

3.3 完成后代码

#!/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()
        self.pub_rate = self.create_rate(30)
        self.thread_ = threading.Thread(target=self._thread_pub)
        self.thread_.start()

    
    def _init_joint_states(self):
        # 初始左右轮子的速度
        self.joint_speeds = [0.0,0.0]
        self.joint_states = JointState()
        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_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():
            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.编译测试

编译程序

colcon build --packages-select fishbot_description

此时运行关节数据发布节点

ros2 run fishbot_description rotate_wheel

测试之前还需要修改下display_rviz2.launch.py文件,注释其joint_state_publisher节点

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

先运行rviz和robot_state_publisher

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

观察此时rviz界面,可以看到轮子疯狂转动

在这里插入图片描述

5.课后练习

尝试将左右轮速度参数化,然后尝试采用rqt动态参数配置工具,实时控制轮子的转速。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ROS2编程基础课程文档 ROS 2(机器人操作系统2)是用于机器人应用的开源开发套件。ROS 2之目的是为各行各业的开发人员提供标准的软件平台,从研究和原型设计再到部署和生产。 ROS 2建立在ROS 1的成功基础之上,ROS 1目前已在世界各地的无数机器人应用中得到应用。 特色 缩短上市时间 ROS 2提供了开发应用程序所需的机器人工具,库和功能,可以将时间花在对业务非常重要的工作上。因为它 是开源的,所以可以灵活地决定在何处以及如何使用ROS 2,以及根据实际的需求自由定制,使用ROS 2 可以大幅度提升产品和算法研发速度! 专为生产而设计 凭借在建立ROS 1作为机器人研发的事实上的全球标准方面的十年经验,ROS 2从一开始就被建立在工业级 基础上并可用于生产,包括高可靠性和安全关键系统。 ROS 2的设计选择、开发实践和项目管理基于行业利 益相关者的要求。 多平台支持 ROS 2在Linux,Windows和macOS上得到支持和测试,允许无缝开发和部署机器人自动化,后端管理和 用户界面。分层支持模型允许端口到新平台,例如实时和嵌入式操作系统,以便在获得兴趣和投资时引入和推 广。 丰富的应用领域 与之前的ROS 1一样,ROS 2可用于各种机器人应用,从室内到室外、从家庭到汽车、水下到太空、从消费 到工业。 没有供应商锁定 ROS 2建立在一个抽象层上,使机器人库和应用程序与通信技术隔离开来。抽象底层是通信代码的多种实现, 包括开源和专有解决方案。在抽象顶层,核心库和用户应用程序是可移植的。 建立在开放标准之上 ROS 2中的默认通信方法使用IDL、DDS和DDS-I RTPS等行业标准,这些标准已广泛应用于从工厂到航空 航天的各种工业应用中。 开源许可证 ROS 2代码在Apache 2.0许可下获得许可,在3条款(或“新”)BSD许可下使用移植的ROS 1代码。这两个 许可证允许允许使用软件,而不会影响用户的知识产权。 全球社区 超过10年的ROS项目通过发展一个由数十万开发人员和用户组成的全球社区,为机器人技术创建了一个庞大 的生态系统,他们为这些软件做出贡献并进行了改进。 ROS 2由该社区开发并为该社区开发,他们将成为未 来的管理者。 行业支持 正如ROS 2技术指导委员会成员所证明的那样,对ROS 2的行业支持很强。除了开发顶级产品外,来自世界 各地的大大小小公司都在投入资源为ROS 2做出开源贡献。 与ROS1的互操作性 ROS 2包括到ROS 1的桥接器,处理两个系统之间的双向通信。如果有一个现有的ROS 1应用程序, 可 以通过桥接器开始尝试使用ROS 2,并根据要求和可用资源逐步移植应用程序。
URDF中的joint标签用来描述机器人模型中的关节属性和连接关系。在给出的引用中,我们可以看到一个关节的示例内容。该关节的名称为"joint_1",类型为continuous,它连接着名为"base_link"和"link_1"的两个链接,并且它的轴向量为(-1, 0, 0)。这些属性描述了关节的基本信息。 解决URDF文件中关节类型的问题可以通过安装ROSjoint_state_publisher_gui软件包来实现。通过执行命令`sudo apt install ros-noetic-joint-state-publisher-gui`即可安装该软件包。 关于URDF文件中revolute类型关节的使用,由于手动计算角度限制比较麻烦且Solidworks插件导出URDF文件时不太方便,所以可以先导出为continuous类型的关节,在URDF模型中再进行修改。可以参考官方文档对joint标签内容的归纳总结来修改关节的属性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [ROS中URDF模型的joint标签详解](https://blog.csdn.net/weixin_41420355/article/details/104310896)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [ROS学习第二十九节——URDF之joint](https://blog.csdn.net/qq_45685327/article/details/130308842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值