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

ROS2下MoveIt+Rviz+MuJoCo 三剑合璧!Panda 机械臂联动仿真!

视频讲解:

ROS2下MoveIt+Rviz+MuJoCo 三剑合璧!Panda 机械臂联动仿真!

仓库代码:GitHub - LitchiCheng/ros2_package

今天介绍下,ros2下使用moveit在Rviz和mujoco联合仿真,结合上一期视频《MuJoCo 仿真 Panda 机械臂关节空间运动|含完整代码》

首先创建一个ros2的package

ros2 pkg create --build-type ament_python joint_state_pkg --dependencies rclpy sensor_msgs

在src中创建joint_state_subscriber.py

import rclpy
from rclpy.node import Node
from sensor_msgs.msg import JointState
import mujoco
import numpy as np
import glfw

class JointStateSubscriber(Node):
    def __init__(self):
        super().__init__("joint_state_subscriber")
        self.subscription = self.create_subscription(
            JointState,
            "/joint_states",
            self.callback,
            10
        )
        
        # 加载模型
        self.model = mujoco.MjModel.from_xml_path('/home/dar/MuJoCoBin/mujoco_menagerie/franka_emika_panda/scene.xml')
        self.data = mujoco.MjData(self.model)

        # 打印所有 body 的 ID 和名称
        print("All bodies in the model:")
        for i in range(self.model.nbody):
            body_name = mujoco.mj_id2name(self.model, mujoco.mjtObj.mjOBJ_BODY, i)
            print(f"ID: {i}, Name: {body_name}")

        # 初始化 GLFW
        if not glfw.init():
            return

        self.window = glfw.create_window(1200, 900, 'Panda Arm Control', None, None)
        if not self.window:
            glfw.terminate()
            return

        glfw.make_context_current(self.window)

        # 设置鼠标滚轮回调函数
        glfw.set_scroll_callback(self.window, self.scroll_callback)

        # 初始化渲染器
        self.cam = mujoco.MjvCamera()
        self.opt = mujoco.MjvOption()
        mujoco.mjv_defaultCamera(self.cam)
        mujoco.mjv_defaultOption(self.opt)
        self.pert = mujoco.MjvPerturb()
        self.con = mujoco.MjrContext(self.model, mujoco.mjtFontScale.mjFONTSCALE_150.value)

        self.scene = mujoco.MjvScene(self.model, maxgeom=10000)

        # 找到末端执行器的 body id
        self.end_effector_id = mujoco.mj_name2id(self.model, mujoco.mjtObj.mjOBJ_BODY, 'hand')
        print(f"End effector ID: {self.end_effector_id}")
        if self.end_effector_id == -1:
            print("Warning: Could not find the end effector with the given name.")
            glfw.terminate()
            return

        # 初始关节角度
        self.initial_q = self.data.qpos[:7].copy()
        print(f"Initial joint positions: {self.initial_q}")

        self.timer1 = self.create_timer(
            0.01,  # 100ms周期
            self.timer_callback1
        )

    def scroll_callback(self, window, xoffset, yoffset):
        # 调整相机的缩放比例
        self.cam.distance *= 1 - 0.1 * yoffset

    def limit_angle(self, angle):
        while angle > np.pi:
            angle -= 2 * np.pi
        while angle < -np.pi:
            angle += 2 * np.pi
        return angle    

    def timer_callback1(self):
        if not glfw.window_should_close(self.window):
    
            # 获取当前末端执行器位置
            mujoco.mj_forward(self.model, self.data)
            self.end_effector_pos = self.data.body(self.end_effector_id).xpos

            self.initial_q[0] = self.initial_q[0] + 0.1
            self.initial_q[0] = self.limit_angle(self.initial_q[0])
            new_q = self.initial_q
            # 设置关节目标位置
            self.data.qpos[:7] = self.positions

            # 模拟一步
            mujoco.mj_step(self.model, self.data)

            # 更新渲染场景
            viewport = mujoco.MjrRect(0, 0, 1200, 900)
            mujoco.mjv_updateScene(self.model, self.data, self.opt, self.pert, self.cam, mujoco.mjtCatBit.mjCAT_ALL.value, self.scene)
            mujoco.mjr_render(viewport, self.scene, self.con)

            # 交换前后缓冲区
            glfw.swap_buffers(self.window)
            glfw.poll_events()

    def callback(self, msg: JointState):
        # 过滤Panda机械臂的7个关节(名称以panda_joint开头)
        panda_joints = [name for name in msg.name if "panda_joint" in name]
        self.positions = [msg.position[i] for i, name in enumerate(msg.name) if "panda_joint" in name]

        for name, pos in zip(panda_joints, self.positions):
            self.get_logger().info(f"{name}: {pos:.4f}")

def main(args=None):
    rclpy.init(args=args)
    node = JointStateSubscriber()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == "__main__":
    main()
    # 清理资源
    glfw.terminate()

修改setup.py

entry_points={
    'console_scripts': [
        'joint_state_subscriber = joint_state_pkg.joint_state_subscriber:main',
    ],
},

先运行panda rviz场景

ros2 launch moveit2_tutorials demo.launch.py

编译运行

colcon build --packages-select joint_state_subscriber
source install/setup.bash
ros2 run joint_state_pkg joint_state_subscriber

拖动Rviz末端的控制,点击planning中的plan和excute

可以看到mujoco中机械臂跟着一起动啦!


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

相关文章:

  • Box-Cox变换:让数据服从正态分布的数学魔法
  • [unity 点击事件] 区域响应点击事件,排除子节点区域,Raycast Target 应用
  • 简单描述一下,大型语言模型简史
  • An Easy Problem(信息学奥赛一本通-1223)
  • 计算机是如何工作的
  • 【Ratis】SlideWindow滑动窗口机制
  • 在C++ Qt中集成Halcon窗口并实现跨平台兼容和大图加载
  • IIS漏洞再现
  • conda install 和 pip install 的区别
  • 【HTML5游戏开发教程】零基础入门合成大西瓜游戏实战 | JS物理引擎+Canvas动画+完整源码详解
  • 详解Redis 核心特性与基础
  • C++相关
  • 2025高频面试算法总结篇【字符串】
  • 蓝桥杯算法题分享(二)
  • NO.55十六届蓝桥杯备战|排序|插入|选择|冒泡|堆|快速|归并(C++)
  • NLP高频面试题(十三)——什么是大模型幻觉,如何解决大模型幻觉
  • 数据结构-二叉链表存储的二叉树
  • 数字电路基础
  • jupyter 操作相关内容
  • ADB介绍