在机械臂控制系统的开发过程中,夹取物体的稳定性是一个常见但棘手的问题。我在开发基于ROS1的机械臂仿真系统时,遇到了一个典型的夹取失败场景:当夹爪闭合时,有时会刚好夹到物体的棱角位置,导致无法建立稳定的接触面,最终导致夹取失败。
这个bug的具体表现是:在Gazebo仿真环境中,当机械臂执行夹取动作时,大约有30%的概率会出现夹爪与物体接触不良的情况。通过观察仿真画面和日志数据,我发现问题主要出现在以下两种场景:
最初我尝试的解决方案是让四个手指先后闭合,通过时间差来推动物体进行位姿矫正。这个方法在一定程度上改善了夹取成功率,但带来了新的问题:
经过深入分析,我认为问题的本质在于:
为了从根本上解决问题,我决定在机械臂的末端执行器上增加一个旋转自由度。具体实现是在gripper_link下新增一个旋转关节gripper_roll和一个过渡link gripper_roll_link,将四个手指的挂载点从gripper_link转移到gripper_roll_link上。
xml复制<joint name="gripper_roll" type="revolute">
<parent link="gripper_link"/>
<child link="gripper_roll_link"/>
<origin xyz="0 0 -0.2" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit effort="10" velocity="3.0" lower="-3.14" upper="3.14"/>
<dynamics damping="0.05" friction="0.02"/>
</joint>
<link name="gripper_roll_link">
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder length="0.01" radius="0.02"/>
</geometry>
</collision>
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<cylinder length="0.01" radius="0.02"/>
</geometry>
<material name="dark">
<color rgba="0.0 0.0 0.0 1.0"/>
</material>
</visual>
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.05"/>
<inertia ixx="1e-4" ixy="0.0" ixz="0.0" iyy="1e-4" iyz="0.0" izz="1e-4"/>
</inertial>
</link>
为了控制新增的旋转关节,需要在ROS控制系统中进行相应配置:
yaml复制gripper_roll_position_controller:
type: position_controllers/JointPositionController
joint: gripper_roll
pid: {p: 50.0, i: 0.0, d: 2.0}
xml复制<transmission name="gripper_roll_transmission">
<type>transmission_interface/SimpleTransmission</type>
<joint name="gripper_roll">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="gripper_roll_motor">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
xml复制<node name="controller_spawner" pkg="controller_manager" type="spawner" output="screen"
args="joint_state_controller rotation1_position_controller rotation2_position_controller gripper_position_controller gripper_roll_position_controller finger1_position_controller finger2_position_controller finger3_position_controller finger4_position_controller"/>
为了实现夹爪与物体的自动对齐,我开发了两个关键函数:
get_gripper_roll_yaw():获取当前夹爪相对于世界坐标系的偏航角align_gripper_roll():根据当前偏航角调整夹爪方向python复制def align_gripper_roll(self):
"""
对齐夹爪朝向:获取当前 yaw 角,然后旋转夹爪使其回到初始朝向(相对于世界坐标系为 0)
"""
yaw = self.get_gripper_roll_yaw()
if yaw is not None:
rospy.loginfo("当前 gripper_roll yaw 角: %.3f rad (%.1f 度)" % (yaw, np.degrees(yaw)))
self.gripper_roll_pub.publish(Float64(-yaw))
rospy.loginfo("旋转夹爪以对齐初始朝向")
else:
rospy.loginfo("无法获取 gripper_roll yaw 角")
def get_gripper_roll_yaw(self):
"""
获取 gripper_roll_link 在世界坐标系中的 yaw 角(弧度)
通过正向运动学计算:yaw = rotation1 + rotation2 + gripper_roll
返回:
float: yaw 角度值(弧度),如果未获取到则返回 None
"""
if self.current_joint_state is None:
rospy.logwarn("尚未接收到关节状态信息")
return None
try:
# 获取各关节角度
rotation1_idx = self.current_joint_state.name.index('rotation1')
rotation2_idx = self.current_joint_state.name.index('rotation2')
gripper_roll_idx = self.current_joint_state.name.index('gripper_roll')
rotation1 = self.current_joint_state.position[rotation1_idx]
rotation2 = self.current_joint_state.position[rotation2_idx]
gripper_roll = self.current_joint_state.position[gripper_roll_idx]
# 计算 gripper_roll_link 的世界 yaw 角
world_yaw = rotation1 + rotation2 + gripper_roll
return world_yaw
except ValueError:
rospy.logwarn("未找到所需关节")
return None
except IndexError:
rospy.logwarn("关节状态数据不完整")
return None
为了确保每次任务执行时夹爪都处于已知状态,我添加了复位功能:
python复制def arm_reset(self):
"""手臂复位到初始位置"""
rospy.loginfo("手臂复位到初始位置")
self.move_arm_simple(0.0, 0.0, 0.0, duration=3.0)
# 直接将 gripper_roll 复位到 0
rospy.loginfo("复位 gripper_roll 到初始角度")
self.gripper_roll_pub.publish(Float64(0.0))
rospy.sleep(1.0)
self.open_gripper(duration=1.5)
rospy.loginfo("手臂复位完成\n")
为了验证改进效果,我设计了以下测试场景:
测试结果对比如下:
| 指标 | 改进前 | 改进后 |
|---|---|---|
| 夹取成功率 | 68% | 95% |
| 平均执行时间 | 4.2s | 3.8s |
| 物体位移误差 | ±0.03m | ±0.01m |
| 系统稳定性 | 一般 | 优秀 |
在实际使用中,可能会遇到以下问题:
关节超限报警
<limit>标签中的参数设置,适当增大范围对齐精度不足
复位时抖动明显
<limit>中的velocity值,增加damping参数通过这次问题解决,我总结了以下几点经验:
机械结构设计:在末端执行器设计中,预留足够的自由度可以大大提高系统的适应性。虽然增加了复杂度,但带来的性能提升是显著的。
控制策略优化:简单的时序控制(如先后闭合手指)虽然能解决部分问题,但往往不是最优解。基于物理模型的解决方案通常更可靠。
系统复位机制:良好的复位机制可以确保每次任务执行时系统都处于已知状态,这对长期稳定运行至关重要。
对于未来可能的扩展,我有以下思考: