让可视化机器人动起来

前文《用RVIZ2创建一个可视化的机器人》讲述了如何用rviz通过xml创建一个虚拟的机器人。
美中不足的是,这个机器人无法进行运动。
这篇就如何运动,来写一个node,替代之前的信息发布来给虚拟机器人的joint发送一些指令,让轮子可以转动起来。

一、基本想法

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

在这里插入图片描述

然后,编写代码实现一个 ROS 2 节点,名为 RotateWheelNode,用于模拟机器人轮子的旋转。它通过发布 JointState 消息来更新和控制轮子的转速和位置。

二、创建一个节点,用来发布信息

1、在创建节点之前,可以先查看一下消息的类型

ros2 topic info /joint_states

使用 ros2 topic info /joint_states 命令时,会查询关于 /joint_states 主题的详细信息。这个命令会显示以下信息:

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

**发布者的数量(Publishers):**显示有多少个节点正在向 /joint_states 主题发布消息。
**订阅者的数量(Subscribers):**显示有多少个节点正在订阅 /joint_states 主题。
**消息类型(Type):**显示该主题用于传递的消息类型,对于 /joint_states 通常是 sensor_msgs/msg/JointState。

然后用命令输出该消息类型的所有字段及其数据类型。

ros2 interfaces show sensor_msgs/msg/JointState

消息定义
对于 sensor_msgs/msg/JointState,其定义通常包括以下字段:

std_msgs/Header header: 包含时间戳和坐标帧信息的标准消息头。
string[] name: 关节的名称数组。
double[] position: 每个关节的位置(通常是角度或线性位置)。
double[] velocity: 每个关节的速度。
double[] effort: 每个关节施加的力或扭矩。

知道了消息类型,就可以安排编写node了

先在package下新建rotate_wheel.py

#!/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 = ['joint_front_left','joint_front_right']
        # 关节的位置
        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()