Moveit2 automaticaddison mycobot_ros2 代码讲解
2026/5/11 12:27:00 网站建设 项目流程

github地址https://github.com/automaticaddison/mycobot_ros2/tree/jazzy

一.mycobot_moveit_config

1.moveit2基本控制

在mycobot_moveit_config下面创建config/mycobot_280

initial_positions.yaml 定义了机械臂所有关节的初始位置

joint_limits.yaml定义每个关节的物理限制:包括角度范围、最大速度、最大加速度等。

kinematics.yaml配置运动学求解器的参数,比如使用哪种逆运动学算法、求解器的超时时间、尝试次数等。

moveit_controllers.yaml 指定使用了哪些控制器

mycobot_280.srdf 语义机器人描述格式,urdf的补充

pilz_cartesian_limits.yaml专门为Pilz 工业运动规划器设置的笛卡尔空间限制,比如末端执行器的最大线速度、加速度、圆弧运动的约束等。

ros2_controllers.yaml/ros2_controllers_template.yaml ros2控制器的指定配置文件

ompl_planning.yaml配置OMPL(Open Motion Planning Library)运动规划器的参数

pilz_industrial_motion_planner_planning.yamlPilz 工业规划器的专属配置,支持工业场景中常用的直线(LIN)、圆弧(CIRC)、点到点(PTP)运动指令,定义这些运动模式的默认参数和约束。

stomp_planning.yaml配置STOMP(Stochastic Trajectory Optimization for Motion Planning)轨迹优化器的参数,用于对初步规划出的路径进行平滑优化,让机械臂的运动更平稳、减少冲击。

2.setup_assistant

必须加上这个助手文件 这个

moveit_setup_assistant_config: #指定urdf文件 URDF: package: mycobot_description relative_path: urdf/robots/mycobot_280.urdf.xacro SRDF: #指定srdf语义文件 relative_path: config/mycobot_280/mycobot_280.srdf CONFIG: author_name: AutomaticAddison author_email: addison@todo.com generated_timestamp: 1734048000

二.执行脚本

执行脚本的路径位于mycobot_bringup/scripts/mycobot_280_gazebo_and_moveit.sh

我将带领大家从运行脚本逆向理解整体的流程,我会写入每行脚本代码的解释

#!/bin/bash # Single script to launch the mycobot with Gazebo, RViz, and MoveIt 2 #当执行crtl+c的时候,会执行这个函数,关闭所有 ROS/Gazebo 程序 cleanup() { echo "Cleaning up..." sleep 5.0 pkill -9 -f "ros2|gazebo|gz|nav2|amcl|bt_navigator|nav_to_pose|rviz2|assisted_teleop|cmd_vel_relay|robot_state_publisher|joint_state_publisher|move_to_free|mqtt|autodock|cliff_detection|moveit|move_group|basic_navigator" } # Set up cleanup trap #捕获crtl+c的退出信号 trap 'cleanup' SIGINT SIGTERM echo "Launching Gazebo simulation..." #运行gazebo的launch文件 ros2 launch mycobot_gazebo mycobot.gazebo.launch.py \ load_controllers:=true \ world_file:=pick_and_place_demo.world \ use_camera:=true \ use_rviz:=false \ use_robot_state_pub:=true \ use_sim_time:=true \ x:=0.0 \ y:=0.0 \ z:=0.03 \ roll:=0.0 \ pitch:=0.0 \ yaw:=0.0 & sleep 15 #启动 MoveIt2 运动规划 ros2 launch mycobot_moveit_config move_group.launch.py & echo "Adjusting camera position..." #. 自动调整 Gazebo 相机视角 gz service -s /gui/move_to/pose --reqtype gz.msgs.GUICamera --reptype gz.msgs.Boolean --timeout 2000 --req "pose: {position: {x: 1.36, y: -0.58, z: 0.95} orientation: {x: -0.26, y: 0.1, z: 0.89, w: 0.35}}" # Keep the script running until Ctrl+C #保持脚本运行,直到 Ctrl+C wait

三.解析mycobot_gazebo/launch/mycobot.gazebo.launch.py

1. 首先讲解这个 default_ros_gz_bridge_config_file_path路径下面的ros_gz_bridge.yaml文件,这个文件 完成的内容如下,说白了就是gazebo和ros2的消息格式不一样,互转一下

gz_topic_name: camera_head/camera_info

  • Gazebo 仿真里,相机发布的话题叫这个。

  • ros_topic_name: camera_head/depth/camera_info

  • 我要把它转发到 ROS2,并改名叫这个。

  • ros_type_name / gz_type_name

  • 两边消息类型不一样,自动帮你转换格式

  • direction: GZ_TO_ROS

  • 方向:Gazebo → ROS2(单向转发)

  • lazy: true

  • 只有 ROS2 有人订阅时才转发,省电、省性能

2.中间就是一堆声明配置参数的

3.下面是启动robot_state_publisher.launch.py

robot_state_publisher_cmd = IncludeLaunchDescription( PythonLaunchDescriptionSource([ os.path.join(pkg_share_description, 'launch', 'robot_state_publisher.launch.py') ]), launch_arguments={ 'jsp_gui': jsp_gui, 'use_camera': use_camera, 'use_gazebo': use_gazebo, 'use_rviz': use_rviz, 'use_sim_time': use_sim_time }.items(), condition=IfCondition(use_robot_state_pub) )

3.1解析robot_state_publisher.launch.py

1.先讲解一个函数

PathJoinSubstitution 和os.path.jion一样,为ros2专用的路径拼接的函数

default_urdf_model_path = PathJoinSubstitution(

[pkg_share_description, 'urdf', 'robots', urdf_filename])

default_rviz_config_path = PathJoinSubstitution(

[pkg_share_description, 'rviz', rviz_config_filename])

2.xacro -> urdf函数

相当于在终端执行了以下这样的命令,把xacro文件转换为urdf文件,然后把在终端输出的内容(
即urdf文件保存到robot_description_content变量里面)

xacro 你的文件.xacro use_camera:=true ...

robot_description_content = ParameterValue(Command([ 'xacro', ' ', urdf_model, ' ', 'robot_name:=', LaunchConfiguration('robot_name'), ' ', 'prefix:=', LaunchConfiguration('prefix'), ' ', 'add_world:=', LaunchConfiguration('add_world'), ' ', 'base_link:=', LaunchConfiguration('base_link'), ' ', 'base_type:=', LaunchConfiguration('base_type'), ' ', 'flange_link:=', LaunchConfiguration('flange_link'), ' ', 'gripper_type:=', LaunchConfiguration('gripper_type'), ' ', 'use_camera:=', LaunchConfiguration('use_camera'), ' ', 'use_gazebo:=', LaunchConfiguration('use_gazebo'), ' ', 'use_gripper:=', LaunchConfiguration('use_gripper') ]), value_type=str)

3.下面就是一堆启动节点的函数

start_robot_state_publisher_cmd = Node( package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', output='screen', parameters=[{ 'use_sim_time': use_sim_time, 'robot_description': robot_description_content}]) # Publish the joint state values for the non-fixed joints in the URDF file. start_joint_state_publisher_cmd = Node( package='joint_state_publisher', executable='joint_state_publisher', name='joint_state_publisher', parameters=[{'use_sim_time': use_sim_time}], condition=IfCondition(use_jsp)) # Depending on gui parameter, either launch joint_state_publisher or joint_state_publisher_gui start_joint_state_publisher_gui_cmd = Node( package='joint_state_publisher_gui', executable='joint_state_publisher_gui', name='joint_state_publisher_gui', parameters=[{'use_sim_time': use_sim_time}], condition=IfCondition(jsp_gui)) # Launch RViz start_rviz_cmd = Node( condition=IfCondition(use_rviz), package='rviz2', executable='rviz2', output='screen', arguments=['-d', rviz_config_file], parameters=[{'use_sim_time': use_sim_time}])

3.2解析load_ros2_controllers.launch.py

这个launch文件主要用于激活src/mycobot_ros2/mycobot_moveit_config/config/mycobot_280/ros2_controllers.yaml的各种控制器

start_arm_controller_cmd = ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active', 'arm_controller'], output='screen') # Start gripper action controller start_gripper_action_controller_cmd = ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active', 'gripper_action_controller'], output='screen') # Launch joint state broadcaster start_joint_state_broadcaster_cmd = ExecuteProcess( cmd=['ros2', 'control', 'load_controller', '--set-state', 'active', 'joint_state_broadcaster'], output='screen')

这里讲解一下为什么需要 激活joint_state_broadcaster这个控制器,因为之前已经有了关节状态发布节点

joint_state_broadcasterjoint_state_publisher
层级ROS 2 Control 框架内部独立ros2节点
数据来源真实硬件接口(编码器反馈)URDF 中的<joint>标签(静态定义)
用途
发布实际关节状态(真实反馈)
发布虚拟关节状态(无硬件时模拟)
运行条件需要硬件接口运行

不需要硬件,独立运行

典型场景真实机器人/仿真(Gazebo/Ignition)纯可视化(无控制器、无仿真)

3.3启动gazebo仿真

-r 表示启动后立即开始仿真 -v 4Verbosity 级别 4— 设置日志详细程度为最高级(Debug 级别),输出最详细的调试信息

start_gazebo_cmd = IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')), launch_arguments=[('gz_args', [' -r -v 4 ', world_path])])

3.4 启动ros_gz_bridge

就是我上面我讲的那些gz和ros的变换

start_gazebo_ros_bridge_cmd = Node( package='ros_gz_bridge', executable='parameter_bridge', parameters=[{ 'config_file': default_ros_gz_bridge_config_file_path, }], output='screen' )

3.5 启动图像专用bridge

上面那个bridge针对的是相机的参数,而这个bridge针对的是相机的数据

start_gazebo_ros_image_bridge_cmd = Node( package='ros_gz_image', executable='image_bridge', arguments=[ '/camera_head/depth_image', '/camera_head/image', ], remappings=[ ('/camera_head/depth_image', '/camera_head/depth/image_rect_raw'), ('/camera_head/image', '/camera_head/color/image_raw'), ], )

3.6把机器人放在gazebo里面显示

start_gazebo_ros_spawner_cmd = Node( package='ros_gz_sim', executable='create', output='screen', arguments=[ '-topic', '/robot_description', '-name', robot_name, '-allow_renaming', 'true', '-x', x, '-y', y, '-z', z, '-R', roll, '-P', pitch, '-Y', yaw ])

四. move_group.launch.py

启动moveit2相关的配置

#!/usr/bin/env python3 """ Launch MoveIt 2 for the myCobot robotic arm. This script creates a ROS 2 launch file that starts the necessary nodes and services for controlling a myCobot robotic arm using MoveIt 2. It loads configuration files, starts the move_group node, and optionally launches RViz for visualization. :author: Addison Sears-Collins :date: December 13, 2024 """ import os from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, EmitEvent, RegisterEventHandler, OpaqueFunction from launch.conditions import IfCondition from launch.event_handlers import OnProcessExit from launch.events import Shutdown from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare from moveit_configs_utils import MoveItConfigsBuilder def generate_launch_description(): """ Generate a launch description for MoveIt 2 with myCobot robot. This function sets up the necessary configuration and nodes to launch MoveIt 2 for controlling a myCobot robotic arm. It includes setting up paths to config files, declaring launch arguments, configuring the move_group node, and optionally starting RViz. Returns: LaunchDescription: A complete launch description for the MoveIt 2 system """ # Constants for paths to different files and folders package_name_moveit_config = 'mycobot_moveit_config' # Launch configuration variables use_sim_time = LaunchConfiguration('use_sim_time') use_rviz = LaunchConfiguration('use_rviz') rviz_config_file = LaunchConfiguration('rviz_config_file') rviz_config_package = LaunchConfiguration('rviz_config_package') # Get the package share directory pkg_share_moveit_config_temp = FindPackageShare(package=package_name_moveit_config) # Declare the launch arguments declare_robot_name_cmd = DeclareLaunchArgument( name='robot_name', default_value='mycobot_280', description='Name of the robot to use') declare_use_sim_time_cmd = DeclareLaunchArgument( name='use_sim_time', default_value='true', description='Use simulation (Gazebo) clock if true') declare_use_rviz_cmd = DeclareLaunchArgument( name='use_rviz', default_value='true', description='Whether to start RViz') declare_rviz_config_file_cmd = DeclareLaunchArgument( name='rviz_config_file', default_value='move_group.rviz', description='RViz configuration file') declare_rviz_config_package_cmd = DeclareLaunchArgument( name='rviz_config_package', default_value=package_name_moveit_config, description='Package containing the RViz configuration file') def configure_setup(context): """Configure MoveIt and create nodes with proper string conversions.""" # Get the robot name as a string for use in MoveItConfigsBuilder robot_name_str = LaunchConfiguration('robot_name').perform(context) # Get package path pkg_share_moveit_config = pkg_share_moveit_config_temp.find(package_name_moveit_config) # Construct file paths using robot name string config_path = os.path.join(pkg_share_moveit_config, 'config', robot_name_str) # Define all config file paths initial_positions_file_path = os.path.join(config_path, 'initial_positions.yaml') joint_limits_file_path = os.path.join(config_path, 'joint_limits.yaml') kinematics_file_path = os.path.join(config_path, 'kinematics.yaml') moveit_controllers_file_path = os.path.join(config_path, 'moveit_controllers.yaml') srdf_model_path = os.path.join(config_path, f'{robot_name_str}.srdf') pilz_cartesian_limits_file_path = os.path.join(config_path, 'pilz_cartesian_limits.yaml') # Create MoveIt configuration moveit_config = ( MoveItConfigsBuilder(robot_name_str, package_name=package_name_moveit_config) .trajectory_execution(file_path=moveit_controllers_file_path) .robot_description_semantic(file_path=srdf_model_path) .joint_limits(file_path=joint_limits_file_path) .robot_description_kinematics(file_path=kinematics_file_path) .planning_pipelines( pipelines=["ompl", "pilz_industrial_motion_planner", "stomp"], default_planning_pipeline="ompl" ) .planning_scene_monitor( publish_robot_description=False, publish_robot_description_semantic=True, publish_planning_scene=True, ) .pilz_cartesian_limits(file_path=pilz_cartesian_limits_file_path) .to_moveit_configs() ) # MoveIt capabilities move_group_capabilities = {"capabilities": "move_group/ExecuteTaskSolutionCapability"} # Create move_group node start_move_group_node_cmd = Node( package="moveit_ros_move_group", executable="move_group", output="screen", parameters=[ moveit_config.to_dict(), {'use_sim_time': use_sim_time}, {'start_state': {'content': initial_positions_file_path}}, move_group_capabilities, ], ) # Create RViz node start_rviz_node_cmd = Node( condition=IfCondition(use_rviz), package="rviz2", executable="rviz2", arguments=[ "-d", [FindPackageShare(rviz_config_package), "/rviz/", rviz_config_file] ], output="screen", parameters=[ moveit_config.robot_description, moveit_config.robot_description_semantic, moveit_config.planning_pipelines, moveit_config.robot_description_kinematics, moveit_config.joint_limits, {'use_sim_time': use_sim_time} ], ) # RViz exit handler exit_event_handler = RegisterEventHandler( condition=IfCondition(use_rviz), event_handler=OnProcessExit( target_action=start_rviz_node_cmd, on_exit=EmitEvent(event=Shutdown(reason='rviz exited')), ), ) return [start_move_group_node_cmd, start_rviz_node_cmd, exit_event_handler] # Create the launch description ld = LaunchDescription() # Add the launch arguments ld.add_action(declare_robot_name_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_rviz_config_package_cmd) ld.add_action(declare_use_sim_time_cmd) ld.add_action(declare_use_rviz_cmd) # Add the setup and node creation ld.add_action(OpaqueFunction(function=configure_setup)) return ld

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询