
OpenClaw 是一个开源的机器人抓取仿真平台,基于 ROS (Robot Operating System) 和 Gazebo 仿真环境。本文将详细讲解如何在 Ubuntu 系统上完整搭建 OpenClaw 开发环境,并进行基础的抓取仿真测试。
在开始搭建之前,需要确保您的系统满足以下要求:
硬件配置:
软件环境:
如果您还没有安装 Ubuntu 系统,请按照以下步骤操作:
bash
# 1. 下载 Ubuntu 20.04 LTS 镜像
# 访问 https://ubuntu.com/download 下载 ISO 文件
# 2. 制作启动U盘(在 Windows 下使用 Rufus,在 Linux 下使用)
sudo dd if=ubuntu-20.04.6-desktop-amd64.iso of=/dev/sdb bs=4M status=progress
# 3. 重启电脑,从U盘启动,按照图形界面提示完成安装安装完 Ubuntu 后,进行基础配置:
bash
# 更新软件源
sudo apt update
sudo apt upgrade -y
# 安装基础开发工具
sudo apt install -y \
build-essential \
cmake \
git \
vim \
curl \
wget \
net-tools \
htop \
terminator # 推荐使用 Terminator 作为终端,支持分屏bash
# 步骤1:设置 sources.list
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
# 步骤2:添加密钥
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
# 步骤3:安装 ROS
sudo apt update
sudo apt install ros-noetic-desktop-full
# 步骤4:初始化 rosdep
sudo rosdep init
rosdep update
# 步骤5:设置环境变量
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
# 步骤6:安装依赖工具
sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstoolbash
# 测试 ROS 是否安装成功
# 终端1:启动 ROS 核心
roscore
# 终端2:运行小海龟仿真
rosrun turtlesim turtlesim_node
# 终端3:控制小海龟
rosrun turtlesim turtle_teleop_key
# 如果能正常显示和控制小海龟,说明 ROS 安装成功bash
# 安装 Gazebo 11(与 ROS Noetic 兼容)
sudo apt install -y \
gazebo11 \
libgazebo11-dev \
ros-noetic-gazebo-ros-pkgs \
ros-noetic-gazebo-ros-control \
ros-noetic-gazebo-plugins
# 验证 Gazebo 安装
gazebo --version
# 应显示 Gazebo multi-robot simulator, version 11.x.xCatkin 是 ROS 的官方构建系统,工作空间是开发 ROS 项目的目录结构:
text
openclaw_ws/ # 工作空间根目录
├── src/ # 源代码目录
│ ├── CMakeLists.txt # 顶层 CMake 配置(自动生成)
│ ├── openclaw/ # OpenClaw 主包
│ ├── openclaw_msgs/ # 消息定义包
│ ├── openclaw_description/# 机器人描述包
│ ├── openclaw_control/ # 控制器包
│ └── openclaw_gazebo/ # Gazebo 仿真包
├── build/ # 编译中间文件(自动生成)
├── devel/ # 开发文件(自动生成)
│ ├── setup.bash # 工作空间环境变量
│ └── lib/ # 编译生成的库文件
└── install/ # 安装文件(可选)bash
# 创建目录结构
mkdir -p ~/openclaw_ws/src
cd ~/openclaw_ws
# 初始化工作空间
catkin_make
# 首次编译后,会自动生成 build 和 devel 目录
ls -la
# 应该看到:build devel src
# 设置工作空间环境变量
echo "source ~/openclaw_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc
# 验证工作空间是否被识别
echo $ROS_PACKAGE_PATH
# 输出应包含:/home/用户名/openclaw_ws/src:/opt/ros/noetic/share由于 OpenClaw 可能是一个特定的项目,我们需要创建标准的 ROS 包结构:
bash
# 进入源代码目录
cd ~/openclaw_ws/src
# 创建 OpenClaw 相关的功能包
# 1. 创建主功能包
catkin_create_pkg openclaw \
roscpp \
rospy \
std_msgs \
geometry_msgs \
sensor_msgs \
control_msgs \
trajectory_msgs
# 2. 创建消息定义包
catkin_create_pkg openclaw_msgs \
std_msgs \
geometry_msgs \
message_generation \
message_runtime
# 3. 创建机器人描述包
catkin_create_pkg openclaw_description \
urdf \
xacro \
robot_state_publisher \
joint_state_publisher \
rviz
# 4. 创建控制器包
catkin_create_pkg openclaw_control \
roscpp \
controller_manager \
joint_trajectory_controller \
position_controllers \
velocity_controllers \
effort_controllers
# 5. 创建 Gazebo 仿真包
catkin_create_pkg openclaw_gazebo \
gazebo_ros \
gazebo_ros_control \
gazebo_plugins \
hector_gazebo_pluginsbash
# 如果 OpenClaw 有 GitHub 仓库,直接克隆
cd ~/openclaw_ws/src
# 克隆主仓库
git clone https://github.com/yourusername/openclaw.git
# 克隆其他依赖仓库
git clone https://github.com/yourusername/openclaw_msgs.git
git clone https://github.com/yourusername/openclaw_description.git
git clone https://github.com/yourusername/openclaw_control.git
git clone https://github.com/yourusername/openclaw_gazebo.git
git clone https://github.com/yourusername/openclaw_examples.gitURDF (Unified Robot Description Format) 是 ROS 中描述机器人模型的 XML 格式。我们将创建一个三指夹爪模型:
xml
<!-- ~/openclaw_ws/src/openclaw_description/urdf/claw.xacro -->
<?xml version="1.0"?>
<robot name="openclaw" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 定义可配置参数 -->
<xacro:property name="PI" value="3.1415926535897931"/>
<xacro:property name="base_width" value="0.1"/>
<xacro:property name="base_height" value="0.05"/>
<xacro:property name="finger_length" value="0.15"/>
<xacro:property name="finger_width" value="0.02"/>
<!-- 材料定义 -->
<material name="black">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
<material name="red">
<color rgba="1.0 0.0 0.0 1.0"/>
</material>
<material name="blue">
<color rgba="0.0 0.0 1.0 1.0"/>
</material>
<!-- 1. 基座链接 -->
<link name="base_link">
<visual>
<geometry>
<box size="${base_width} ${base_width} ${base_height}"/>
</geometry>
<origin rpy="0 0 0" xyz="0 0 0"/>
<material name="black"/>
</visual>
<collision>
<geometry>
<box size="${base_width} ${base_width} ${base_height}"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
</inertial>
</link>
<!-- 2. 手指链接 - 左指 -->
<link name="left_finger">
<visual>
<geometry>
<box size="${finger_width} ${finger_length} ${finger_width}"/>
</geometry>
<origin rpy="0 0 0" xyz="0 ${finger_length/2} 0"/>
<material name="red"/>
</visual>
<collision>
<geometry>
<box size="${finger_width} ${finger_length} ${finger_width}"/>
</geometry>
</collision>
<inertial>
<mass value="0.1"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- 3. 手指链接 - 右指 -->
<link name="right_finger">
<visual>
<geometry>
<box size="${finger_width} ${finger_length} ${finger_width}"/>
</geometry>
<origin rpy="0 0 0" xyz="0 ${finger_length/2} 0"/>
<material name="blue"/>
</visual>
<collision>
<geometry>
<box size="${finger_width} ${finger_length} ${finger_width}"/>
</geometry>
</collision>
<inertial>
<mass value="0.1"/>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
</inertial>
</link>
<!-- 4. 定义关节 - 左指关节 -->
<joint name="left_finger_joint" type="revolute">
<parent link="base_link"/>
<child link="left_finger"/>
<origin xyz="-${base_width/4} ${base_height/2} 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit effort="10" velocity="1" lower="-0.5" upper="0.5"/>
<dynamics damping="0.1" friction="0.01"/>
</joint>
<!-- 5. 定义关节 - 右指关节 -->
<joint name="right_finger_joint" type="revolute">
<parent link="base_link"/>
<child link="right_finger"/>
<origin xyz="${base_width/4} ${base_height/2} 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit effort="10" velocity="1" lower="-0.5" upper="0.5"/>
<dynamics damping="0.1" friction="0.01"/>
</joint>
<!-- 添加传动系统,用于 Gazebo 控制 -->
<transmission name="left_finger_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="left_finger_joint">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="left_finger_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="right_finger_trans">
<type>transmission_interface/SimpleTransmission</type>
<joint name="right_finger_joint">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="right_finger_motor">
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<!-- 添加 Gazebo 插件,用于仿真 -->
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/openclaw</robotNamespace>
</plugin>
</gazebo>
</robot>xml
<!-- ~/openclaw_ws/src/openclaw_description/launch/display.launch -->
<?xml version="1.0"?>
<launch>
<!-- 参数设置 -->
<arg name="model" default="$(find openclaw_description)/urdf/claw.xacro"/>
<arg name="gui" default="true"/>
<arg name="rvizconfig" default="$(find openclaw_description)/rviz/urdf.rviz"/>
<!-- 解析 URDF 模型 -->
<param name="robot_description"
command="$(find xacro)/xacro '$(arg model)'" />
<!-- 启动 robot_state_publisher,发布 tf 变换 -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher">
<param name="publish_frequency" value="50.0"/>
</node>
<!-- 启动 joint_state_publisher,发布关节状态 -->
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui"
type="joint_state_publisher_gui" if="$(arg gui)"/>
<node name="joint_state_publisher" pkg="joint_state_publisher"
type="joint_state_publisher" unless="$(arg gui)"/>
<!-- 启动 RViz 可视化 -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rvizconfig)"
required="true"/>
</launch>yaml
# ~/openclaw_ws/src/openclaw_control/config/control.yaml
openclaw:
# 关节控制器配置
joint_state_controller:
type: joint_state_controller/JointStateController
publish_rate: 50
# 左指位置控制器
left_finger_position_controller:
type: position_controllers/JointPositionController
joint: left_finger_joint
pid: {p: 100.0, i: 0.01, d: 10.0}
# 右指位置控制器
right_finger_position_controller:
type: position_controllers/JointPositionController
joint: right_finger_joint
pid: {p: 100.0, i: 0.01, d: 10.0}
# 夹爪轨迹控制器(用于同时控制两个手指)
gripper_controller:
type: position_controllers/JointTrajectoryController
joints:
- left_finger_joint
- right_finger_joint
constraints:
goal_time: 0.5
stopped_velocity_tolerance: 0.05
stop_trajectory_duration: 0.5
state_publish_rate: 25
action_monitor_rate: 10xml
<!-- ~/openclaw_ws/src/openclaw_control/launch/control.launch -->
<?xml version="1.0"?>
<launch>
<!-- 加载控制器配置 -->
<rosparam file="$(find openclaw_control)/config/control.yaml"
command="load"/>
<!-- 加载机器人描述 -->
<param name="robot_description"
command="$(find xacro)/xacro '$(find openclaw_description)/urdf/claw.xacro'"/>
<!-- 启动 controller_manager 和控制器 -->
<node name="controller_spawner" pkg="controller_manager"
type="spawner" output="screen"
args="
joint_state_controller
left_finger_position_controller
right_finger_position_controller
gripper_controller
"/>
<!-- 启动 robot_state_publisher -->
<node name="robot_state_publisher" pkg="robot_state_publisher"
type="robot_state_publisher"/>
<!-- 启动关节状态GUI(可选) -->
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui"
type="joint_state_publisher_gui"/>
</launch>xml
<!-- ~/openclaw_ws/src/openclaw_gazebo/worlds/grasping.world -->
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="grasping_world">
<!-- 全局光照 -->
<include>
<uri>model://sun</uri>
</include>
<!-- 地面 -->
<include>
<uri>model://ground_plane</uri>
</include>
<!-- 添加桌子 -->
<model name="table">
<pose>0 0 0 0 0 0</pose>
<static>true</static>
<link name="link">
<collision name="collision">
<geometry>
<box>
<size>1.0 1.0 0.1</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>1.0 1.0 0.1</size>
</box>
</geometry>
<material>
<ambient>0.5 0.5 0.5 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
</material>
</visual>
</link>
</model>
<!-- 添加被抓取物体 - 立方体 -->
<model name="cube">
<pose>0 0 0.05 0 0 0</pose>
<link name="link">
<inertial>
<mass>0.1</mass>
<inertia>
<ixx>0.0001</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>0.0001</iyy>
<iyz>0</iyz>
<izz>0.0001</izz>
</inertia>
</inertial>
<collision name="collision">
<geometry>
<box>
<size>0.05 0.05 0.05</size>
</box>
</geometry>
</collision>
<visual name="visual">
<geometry>
<box>
<size>0.05 0.05 0.05</size>
</box>
</geometry>
<material>
<ambient>1 0 0 1</ambient>
<diffuse>1 0 0 1</diffuse>
</material>
</visual>
</link>
</model>
<!-- 添加球体 -->
<model name="sphere">
<pose>0.2 0 0.05 0 0 0</pose>
<link name="link">
<inertial>
<mass>0.1</mass>
<inertia>
<ixx>0.0001</ixx>
<ixy>0</ixy>
<ixz>0</ixz>
<iyy>0.0001</iyy>
<iyz>0</iyz>
<izz>0.0001</izz>
</inertia>
</inertial>
<collision name="collision">
<geometry>
<sphere>
<radius>0.025</radius>
</sphere>
</geometry>
</collision>
<visual name="visual">
<geometry>
<sphere>
<radius>0.025</radius>
</sphere>
</geometry>
<material>
<ambient>0 1 0 1</ambient>
<diffuse>0 1 0 1</diffuse>
</material>
</visual>
</link>
</model>
</world>
</sdf>xml
<!-- ~/openclaw_ws/src/openclaw_gazebo/launch/gazebo.launch -->
<?xml version="1.0"?>
<launch>
<!-- 参数设置 -->
<arg name="paused" default="false"/>
<arg name="use_sim_time" default="true"/>
<arg name="gui" default="true"/>
<arg name="headless" default="false"/>
<arg name="debug" default="false"/>
<arg name="world" default="$(find openclaw_gazebo)/worlds/grasping.world"/>
<!-- 加载机器人描述 -->
<param name="robot_description"
command="$(find xacro)/xacro '$(find openclaw_description)/urdf/claw.xacro'"/>
<!-- 启动 Gazebo 服务器 -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="world_name" value="$(arg world)"/>
<arg name="paused" value="$(arg paused)"/>
<arg name="use_sim_time" value="$(arg use_sim_time)"/>
<arg name="gui" value="$(arg gui)"/>
<arg name="headless" value="$(arg headless)"/>
<arg name="debug" value="$(arg debug)"/>
</include>
<!-- 在 Gazebo 中生成机器人 -->
<node name="spawn_urdf" pkg="gazebo_ros" type="spawn_model"
args="-param robot_description -urdf -model openclaw -x 0 -y 0 -z 0.1"/>
<!-- 启动控制器 -->
<include file="$(find openclaw_control)/launch/control.launch"/>
</launch>python
#!/usr/bin/env python3
# ~/openclaw_ws/src/openclaw_examples/scripts/grasp_object.py
import rospy
import actionlib
from control_msgs.msg import FollowJointTrajectoryAction, FollowJointTrajectoryGoal
from trajectory_msgs.msg import JointTrajectoryPoint
from std_msgs.msg import Float64
import sys
import time
class OpenClawGraspController:
"""OpenClaw 抓取控制器"""
def __init__(self):
# 初始化 ROS 节点
rospy.init_node('openclaw_grasp_controller', anonymous=True)
# 定义关节名称
self.left_finger_joint = 'left_finger_joint'
self.right_finger_joint = 'right_finger_joint'
# 创建发布者(用于直接控制单个关节)
self.left_finger_pub = rospy.Publisher(
'/openclaw/left_finger_position_controller/command',
Float64,
queue_size=10
)
self.right_finger_pub = rospy.Publisher(
'/openclaw/right_finger_position_controller/command',
Float64,
queue_size=10
)
# 创建动作客户端(用于轨迹控制)
self.client = actionlib.SimpleActionClient(
'/openclaw/gripper_controller/follow_joint_trajectory',
FollowJointTrajectoryAction
)
# 等待服务器启动
rospy.loginfo("等待控制器服务器...")
self.client.wait_for_server()
rospy.loginfo("控制器服务器已连接")
# 等待一段时间确保所有组件就绪
rospy.sleep(1.0)
def open_gripper(self):
"""打开夹爪"""
rospy.loginfo("打开夹爪")
# 方法1:使用单个关节发布器
self.left_finger_pub.publish(-0.5) # 左指向外打开
self.right_finger_pub.publish(0.5) # 右指向外打开
# 等待动作完成
rospy.sleep(2.0)
def close_gripper(self):
"""闭合夹爪"""
rospy.loginfo("闭合夹爪")
# 方法1:使用单个关节发布器
self.left_finger_pub.publish(0.5) # 左指向内闭合
self.right_finger_pub.publish(-0.5) # 右指向内闭合
# 等待动作完成
rospy.sleep(2.0)
def grasp_object(self, force=0.3):
"""抓取物体(带力控制)"""
rospy.loginfo("开始抓取物体,力度: %.2f", force)
# 逐步闭合夹爪
steps = 10
for i in range(steps):
# 计算当前角度(从打开到闭合)
left_angle = -0.5 + (i / steps) * 1.0 # -0.5 到 0.5
right_angle = 0.5 - (i / steps) * 1.0 # 0.5 到 -0.5
# 发布关节角度
self.left_finger_pub.publish(left_angle)
self.right_finger_pub.publish(right_angle)
# 等待一小段时间
rospy.sleep(0.1)
rospy.loginfo("抓取完成")
def release_object(self):
"""释放物体"""
rospy.loginfo("释放物体")
self.open_gripper()
def trajectory_grasp(self):
"""使用轨迹控制进行抓取(更平滑的运动)"""
rospy.loginfo("使用轨迹控制进行抓取")
# 创建目标
goal = FollowJointTrajectoryGoal()
goal.trajectory.joint_names = [
self.left_finger_joint,
self.right_finger_joint
]
# 定义轨迹点
# 点1:打开位置
point1 = JointTrajectoryPoint()
point1.positions = [-0.5, 0.5] # [左指, 右指]
point1.time_from_start = rospy.Duration(1.0)
# 点2:闭合位置
point2 = JointTrajectoryPoint()
point2.positions = [0.5, -0.5]
point2.time_from_start = rospy.Duration(3.0)
# 添加点到轨迹
goal.trajectory.points.append(point1)
goal.trajectory.points.append(point2)
# 发送目标
self.client.send_goal(goal)
# 等待结果
self.client.wait_for_result()
# 获取结果
result = self.client.get_result()
rospy.loginfo("轨迹执行结果: %s", result)
def multi_stage_grasp(self):
"""多阶段抓取策略"""
rospy.loginfo("执行多阶段抓取")
# 阶段1:预抓取位置(稍微打开)
rospy.loginfo("阶段1:预抓取位置")
self.left_finger_pub.publish(-0.3)
self.right_finger_pub.publish(0.3)
rospy.sleep(1.0)
# 阶段2:缓慢接近
rospy.loginfo("阶段2:接近物体")
for angle in [-0.4, -0.2, 0.0, 0.2]:
self.left_finger_pub.publish(angle)
self.right_finger_pub.publish(-angle)
rospy.sleep(0.5)
# 阶段3:施加抓取力
rospy.loginfo("阶段3:施加抓取力")
self.left_finger_pub.publish(0.4)
self.right_finger_pub.publish(-0.4)
rospy.sleep(1.0)
# 阶段4:保持抓取
rospy.loginfo("阶段4:保持抓取")
rospy.sleep(2.0)
# 阶段5:释放
rospy.loginfo("阶段5:释放")
self.open_gripper()
def main():
"""主函数"""
try:
# 创建控制器实例
controller = OpenClawGraspController()
# 获取命令行参数
if len(sys.argv) > 1:
mode = sys.argv[1]
else:
mode = "simple"
rospy.loginfo("执行模式: %s", mode)
# 根据不同模式执行抓取
if mode == "simple":
# 简单抓取
controller.open_gripper()
rospy.sleep(1.0)
controller.close_gripper()
rospy.sleep(1.0)
controller.open_gripper()
elif mode == "grasp":
# 抓取物体
controller.open_gripper()
rospy.sleep(1.0)
controller.grasp_object(force=0.5)
rospy.sleep(2.0)
controller.release_object()
elif mode == "trajectory":
# 轨迹抓取
controller.trajectory_grasp()
elif mode == "multistage":
# 多阶段抓取
controller.multi_stage_grasp()
else:
rospy.loginfo("未知模式,使用默认模式")
controller.open_gripper()
rospy.sleep(1.0)
controller.close_gripper()
rospy.loginfo("抓取演示完成")
except rospy.ROSInterruptException:
rospy.loginfo("程序被中断")
except Exception as e:
rospy.logerr("发生错误: %s", str(e))
if __name__ == '__main__':
main()xml
<!-- ~/openclaw_ws/src/openclaw_examples/launch/demo.launch -->
<?xml version="1.0"?>
<launch>
<!-- 启动 Gazebo 仿真 -->
<include file="$(find openclaw_gazebo)/launch/gazebo.launch"/>
<!-- 启动抓取演示节点 -->
<node name="grasp_demo" pkg="openclaw_examples" type="grasp_object.py"
output="screen" required="true">
<!-- 可以传递参数 -->
<param name="grasp_force" value="0.5"/>
<param name="grasp_speed" value="0.1"/>
</node>
<!-- 可选:启动 RViz 可视化 -->
<node name="rviz" pkg="rviz" type="rviz"
args="-d $(find openclaw_description)/rviz/openclaw.rviz"/>
</launch>bash
# 返回工作空间根目录
cd ~/openclaw_ws
# 安装依赖
rosdep install --from-paths src --ignore-src -r -y
# 编译(使用4个线程)
catkin_make -j4
# 如果编译出错,可以尝试单线程编译以便查看详细错误
catkin_make -j1
# 编译特定包
catkin_make --only-pkg-with-deps openclaw_control
# 清理并重新编译
catkin_make clean
catkin_makebash
# 终端1:启动 Gazebo 仿真
source ~/openclaw_ws/devel/setup.bash
roslaunch openclaw_gazebo gazebo.launch
# 终端2:运行抓取演示
source ~/openclaw_ws/devel/setup.bash
rosrun openclaw_examples grasp_object.py simple
# 或者使用不同模式
rosrun openclaw_examples grasp_object.py grasp
rosrun openclaw_examples grasp_object.py trajectory
rosrun openclaw_examples grasp_object.py multistagebash
# 终端:启动 RViz
source ~/openclaw_ws/devel/setup.bash
rosrun rviz rviz
# 在 RViz 中配置:
# 1. 添加 RobotModel 显示机器人模型
# 2. 添加 TF 显示坐标变换
# 3. 添加 Image 显示摄像头图像(如果有)bash
# 查看所有节点
rosnode list
# 查看所有话题
rostopic list
# 查看所有服务
rosservice list
# 查看所有参数
rosparam list
# 查看特定话题内容
rostopic echo /openclaw/joint_states
# 查看特定话题发布频率
rostopic hz /openclaw/joint_states
# 查看话题带宽
rostopic bw /openclaw/joint_statesbash
# 启动 rqt 图形界面
rqt
# 常用插件:
# - rqt_graph:查看节点间关系
# - rqt_plot:实时绘制数据
# - rqt_console:查看日志
# - rqt_tf_tree:查看 TF 树
# 单独启动特定工具
rqt_graph
rqt_plot /openclaw/joint_states/position[0]bash
# 记录所有话题到 bag 文件
rosbag record -a -o grasp_demo.bag
# 记录特定话题
rosbag record /openclaw/joint_states /openclaw/gripper_controller/state
# 查看 bag 信息
rosbag info grasp_demo.bag
# 回放 bag 文件
rosbag play grasp