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

从简单夹爪到灵巧手的运动映射:原理、实现与机器人抓取技能迁移

1. 项目概述:从“爪子”到“手”的智能转换

最近在折腾一个挺有意思的开源项目,叫frostmute/claw2manus。光看名字,你可能会有点摸不着头脑,这“爪子”(Claw)和“手”(Manus)之间,到底要转换个啥?其实,这是一个典型的计算机视觉与机器人学交叉领域的项目,核心目标是将一种特定形态的机械夹爪(通常是二指或三指的“爪子”形态)的运动意图或抓取姿态,映射到一种更接近人手的多指灵巧手(Manipulator)上。

简单来说,就是让一个结构相对简单、成本较低的夹爪,能够“指挥”一个结构复杂、自由度更高的灵巧手,完成同样的抓取或操作任务。这背后的需求非常实际:在工业自动化、仓储物流乃至未来的服务机器人场景中,灵巧手能完成更精细的操作,但其高昂的成本和复杂的控制算法一直是大规模部署的瓶颈。claw2manus这类项目,试图通过“运动映射”或“技能迁移”的方式,用成熟、廉价的夹爪作为“老师”,来训练或控制灵巧手这个“学生”,从而降低后者的应用门槛。

我第一次接触这个项目时,直觉上觉得它解决了一个“桥接”问题。我们有很多针对简单夹爪优化的抓取规划算法、示教数据甚至是成熟的硬件产品,而灵巧手的研究往往集中在实验室里,缺乏大量真实的、面向任务的数据。claw2manus就像一座桥梁,把两个生态连接起来,让前者的积累能为后者所用。这对于快速验证灵巧手在特定场景下的可行性,或者利用现有夹爪控制器直接驱动灵巧手进行初步作业,都具有很高的实用价值。

2. 核心原理与方案设计拆解

2.1 问题定义与核心挑战

要实现从“爪”到“手”的转换,我们首先要明确“转换”的具体内容。通常,这可以分为几个层次:

  1. 姿态映射:给定一个二指夹爪的张开宽度(或角度),如何确定一个五指灵巧手每个手指关节的角度?这并非一一对应,一个标量要映射到十几个甚至几十个自由度上,是一个典型的欠约束问题。
  2. 力映射:夹爪的夹持力如何分配到灵巧手的多个指尖?不同的抓取类型(捏取、抓握、侧捏)力的分配策略完全不同。
  3. 技能迁移:夹爪执行一整套抓取-移动-放置的动作序列(轨迹),灵巧手如何复现这一系列动作,并保持操作的稳定性和精度?

frostmute/claw2manus项目需要解决的核心挑战就在于这种“异构映射”。夹爪的运动空间是低维的,而灵巧手的运动空间是高维且存在大量冗余自由度的。直接线性映射行不通,必须引入先验知识或学习机制。

2.2 主流技术路线分析

根据项目仓库的代码结构和相关论文(如基于此项目思路的延伸研究),我推断claw2manus很可能采用了“示教学习+运动重定向”的混合方案。这不是简单的坐标变换,而是一个包含建模、学习和优化的流程。

方案一:基于运动捕捉的示教学习这是最直观的方法。用一个数据手套或光学动捕系统,记录人类操作员在操作目标物体时的手部姿态。同时,用夹爪去尝试执行相同的抓取任务(可能通过遥操作或简单的预设程序)。这样就获得了一批配对数据:(夹爪状态, 人手姿态)。然后,训练一个神经网络(如多层感知机MLP或循环神经网络RNN)来学习从夹爪状态到人手关节角的映射函数。

注意:这种方法的数据获取成本高,且严重依赖示教质量。如果夹爪的抓取方式与人手差异巨大(例如夹爪是平行夹持,而人手是包络抓握),映射关系会很难学习,导致灵巧手姿态不自然甚至无法稳定抓取。

方案二:基于任务空间对齐的运动重定向这种方法更注重“效果”而非“形态”。它不要求灵巧手完全模仿夹爪的内部关节运动,而是要求两者的“末端效应器”(对于夹爪是指尖,对于灵巧手也是各指尖)在任务空间(三维空间)中的运动轨迹尽可能一致。

具体操作是:

  1. 定义夹爪和灵巧手之间关键点的对应关系(例如,夹爪的左指尖对应灵巧手的拇指和食指指尖的“虚拟中心点”)。
  2. 当夹爪运动时,计算其指尖目标位置。
  3. 将这些目标位置作为约束,求解灵巧手的逆运动学(IK)问题,得到各关节角。这个过程可以表述为一个优化问题:在满足指尖位置约束的同时,最小化关节运动幅度、避免奇异点、模仿人手自然姿态等。

方案三:结合抓握类型分类的混合映射这是更工程化的思路。首先,根据夹爪的张开宽度、接近物体的角度等信息,实时判断它意图执行的抓取类型(例如:power_grasp强力抓握,pinch捏取,tripod三指捏等)。然后,为每一种抓取类型预定义一个灵巧手的“手势模板”以及一套从夹爪宽度到该模板参数(如握拳的紧密程度)的映射规则。实际运行时,先分类,再调用对应的映射规则生成手势。

claw2manus项目很可能采用了方案二和方案三的结合。从一些遗留的代码注释看,它包含了一个逆运动学求解模块和一个简单的抓取分类器。这种设计兼顾了通用性和实时性。

2.3 工具链与依赖选择

实现这样一个项目,工具链的选择至关重要:

  • 仿真环境MuJoCoPyBullet是首选。它们轻量、高效,且对接触力学和机器人控制有良好的支持,非常适合进行抓取算法的快速原型验证。claw2manus的示例脚本大量使用了 PyBullet,因为它Python接口友好,社区资源丰富。
  • 机器人模型:需要夹爪(如 Robotiq 2F-85)和灵巧手(如 Shadow Hand, Allegro Hand)的精确URDF或MJCF模型。这些模型通常可以从制造商或开源社区(如robotics-toolbox)获取。
  • 核心算法库
    • 逆运动学(IK)PyBullet内置了基于数值优化的IK求解器 (calculateInverseKinematics),但可能不够稳定。更专业的选择是TRAC-IKKDL,但集成稍复杂。对于灵巧手,由于其冗余性,通常需要自定义带约束的优化问题,使用SciPyCasADi进行求解。
    • 机器学习(如果采用学习方案)PyTorchTensorFlow是标准选择。对于映射网络,一个简单的全连接网络可能就足够。
  • 中间件ROS (Robot Operating System)几乎是机器人领域的实际标准。claw2manus很可能通过ROS话题来接收夹爪的状态(如/gripper/position),并发布灵巧手的关节控制指令(如/hand/joint_states)。这样能很好地与实际硬件或高级规划器集成。

3. 核心模块实现与实操解析

3.1 系统架构与数据流

一个典型的claw2manus系统运行时架构可以这样设计:

[夹爪硬件/仿真器] --(夹爪位置/力传感器数据)--> [抓取类型分类器] --(抓取类型标签)--> [映射控制器] | [用户/上层规划器] --(目标抓取命令)--> [映射控制器] --(灵巧手关节目标)--> [灵巧手底层控制器] --> [灵巧手硬件/仿真器] | [逆运动学求解器/神经网络模型]

数据流清晰表明了核心模块:分类器映射器(包含IK求解或模型推理)、控制器

3.2 抓取类型分类器实现

分类器不一定需要复杂的深度学习模型。对于结构已知的夹爪,可以根据其关节角简单设定阈值:

def classify_grasp(gripper_width, gripper_approach_angle): """ 简单的基于规则抓取分类器。 gripper_width: 夹爪张开宽度,归一化到[0,1],0为完全闭合,1为完全张开。 approach_angle: 夹爪接近物体的角度(相对于水平面)。 """ if gripper_width < 0.2: # 宽度很小,可能是捏取或已抓住物体 if abs(approach_angle) < 30: # 接近水平 return 'pinch' # 捏取 else: return 'power_grasp' # 强力抓握(从上方或侧方) elif gripper_width > 0.7: return 'open' # 张开状态 else: # 中等宽度,可能是准备抓握或三指捏 if gripper_approach_angle > 45: return 'tripod' # 三指捏,常用于握笔 else: return 'power_grasp_prepare' # 准备进行强力抓握

当然,更鲁棒的方法是收集数据训练一个简单的分类模型(如SVM或小型神经网络),输入可以包括一段时间内的夹爪宽度序列、腕部力传感器读数等,输出抓取类型概率。

3.3 逆运动学映射器详解

这是技术核心。我们以“任务空间对齐”方案为例,详细说明如何用优化方法实现映射。

假设夹爪是二指平行夹爪,我们定义其两个指尖的位置为 ( p_{g}^{left}, p_{g}^{right} )。灵巧手有 ( N ) 个手指,我们为这次抓取选择 ( M ) 个接触点目标(( M ) 通常等于夹爪指尖数,即2),例如选择拇指尖(( t_1 ))和食指尖(( t_2 ))。

步骤1:目标点生成当夹爪宽度为 ( w ) 时,我们可以根据夹爪的几何模型,计算出 ( p_{g}^{left}, p_{g}^{right} ) 在世界坐标系中的位置。然后,根据抓取分类结果,决定如何将这些点分配给灵巧手。

  • 对于pinch(捏取):直接将 ( p_{g}^{left} ) 作为拇指目标 ( p_{t}^{thumb} ),( p_{g}^{right} ) 作为食指目标 ( p_{t}^{index} )。
  • 对于power_grasp(强力抓握):可能需要将两个夹爪指尖位置解释为抓握物体的两个对立面,然后计算出灵巧手多个指尖(拇指、食指、中指等)应该放置的位置,形成一个包围圈。这可能需要更复杂的几何计算。

步骤2:构建优化问题我们的目标是找到灵巧手的关节角向量 ( q )(维度很高),使得选定的指尖位置 ( fk_i(q) )(前向运动学计算结果)尽可能接近目标位置 ( p_{t}^{i} ),同时满足其他约束。

优化问题可以形式化为: [ \min_{q} \sum_{i=1}^{M} | fk_i(q) - p_{t}^{i} |^2 + \lambda_1 | q - q_{neutral} |^2 + \lambda_2 \text{(关节限位惩罚)} + \lambda_3 \text{(碰撞惩罚)} ] 其中:

  • 第一项是位置误差,保证抓取点对准。
  • 第二项是姿态正则化,( q_{neutral} ) 是手部的自然放松姿态,这项使手部姿态看起来更自然,避免奇怪扭曲。
  • 第三项和第四项是物理约束,确保关节在角度范围内且手指不自碰撞。

步骤3:使用SciPy进行求解

import numpy as np from scipy.optimize import minimize from my_hand_model import forward_kinematics, joint_limits, neutral_pose def objective_function(q, target_positions, selected_fingertips): """ 优化目标函数。 q: 优化变量,灵巧手所有关节角。 target_positions: 列表,每个元素是一个目标点的三维坐标。 selected_fingertips: 列表,与target_positions对应,每个元素是指尖的索引。 """ pos_error = 0.0 # 计算位置误差 for i, fingertip_idx in enumerate(selected_fingertips): current_pos = forward_kinematics(q, fingertip_idx) pos_error += np.linalg.norm(current_pos - target_positions[i])**2 # 姿态自然度误差(偏离自然姿态的惩罚) pose_error = np.linalg.norm(q - neutral_pose)**2 # 关节限位惩罚(软约束) limit_penalty = 0.0 for j in range(len(q)): if q[j] < joint_limits[j][0]: limit_penalty += (joint_limits[j][0] - q[j])**2 elif q[j] > joint_limits[j][1]: limit_penalty += (q[j] - joint_limits[j][1])**2 # 总代价(加权和) total_cost = pos_error + 0.1 * pose_error + 1.0 * limit_penalty # 碰撞检测代价可以额外计算,这里省略 return total_cost # 初始化关节角为自然姿态 initial_guess = neutral_pose.copy() # 定义优化边界(关节限位) bounds = [(low, high) for low, high in joint_limits] # 假设我们要进行捏取,目标点是 target_thumb 和 target_index targets = [target_thumb, target_index] fingertips = [THUMB_TIP_INDEX, INDEX_TIP_INDEX] # 预定义的指尖索引 # 调用优化器 result = minimize(objective_function, initial_guess, args=(targets, fingertips), bounds=bounds, method='SLSQP', # 序列二次规划,适合带约束问题 options={'maxiter': 100, 'ftol': 1e-6}) optimized_joint_angles = result.x

这个优化过程在每次夹爪状态更新时都需要运行,因此对实时性要求高。在实际部署中,可能会使用更快的IK求解器,或者为常见抓取类型预计算一系列关节角,运行时进行插值。

3.4 与ROS的集成

为了让映射器能实际工作,需要将其嵌入ROS节点。一个典型的节点结构如下:

#!/usr/bin/env python3 import rospy from sensor_msgs.msg import JointState from geometry_msgs.msg import PoseStamped from your_hand_control.msg import HandCommand class ClawToManusNode: def __init__(self): rospy.init_node('claw2manus_mapper') # 订阅夹爪状态 self.gripper_sub = rospy.Subscriber('/gripper/joint_states', JointState, self.gripper_callback) # 发布灵巧手命令 self.hand_pub = rospy.Publisher('/hand/joint_command', HandCommand, queue_size=10) self.current_gripper_width = 0.0 self.ik_solver = IKSolver() # 封装好的IK求解器 self.classifier = GraspClassifier() def gripper_callback(self, msg): # 从JointState中解析夹爪宽度 # 假设夹爪关节名是'gripper_finger_joint' try: idx = msg.name.index('gripper_finger_joint') self.current_gripper_width = msg.position[idx] except ValueError: rospy.logwarn("Gripper joint name not found in JointState message.") return # 1. 分类抓取类型 grasp_type = self.classifier.classify(self.current_gripper_width) # 2. 根据抓取类型和宽度,计算灵巧手各指尖目标位置 target_positions = self.generate_target_positions(self.current_gripper_width, grasp_type) # 3. 求解逆运动学,得到灵巧手关节角 hand_joint_angles = self.ik_solver.solve(target_positions, grasp_type) # 4. 发布控制命令 hand_cmd = HandCommand() hand_cmd.header.stamp = rospy.Time.now() hand_cmd.joint_names = ['hand_joint1', 'hand_joint2', ...] # 灵巧手所有关节名 hand_cmd.positions = hand_joint_angles.tolist() self.hand_pub.publish(hand_cmd) def generate_target_positions(self, width, grasp_type): # 根据几何关系,由夹爪宽度生成目标点 # 这里需要夹爪的详细几何参数 # 返回一个目标点坐标的列表 pass if __name__ == '__main__': node = ClawToManusNode() rospy.spin()

4. 实操部署与调优心得

4.1 仿真环境搭建与调试

在将算法部署到真实硬件前,必须在仿真中充分测试。我强烈建议使用PyBullet配合ROS Noetic搭建仿真测试环境。

  1. 加载模型:分别加载夹爪和灵巧手的URDF文件。确保它们的相对位置(如都安装在同一机械臂腕部)与实际场景一致。
  2. 创建地面和物体:在仿真中添加一个桌面和待抓取的物体(如方块、圆柱、球)。
  3. 实现映射节点:将上述ROS节点代码在仿真中运行。订阅仿真的夹爪关节状态,发布关节位置命令给仿真的灵巧手。
  4. 可视化调试:在RViz中同时显示夹爪和灵巧手的模型,并可视化计算出的目标点(可以用visualization_msgs/Marker显示为小球)。这是调试映射关系是否正确最有效的方法。如果目标点位置奇怪,或者灵巧手姿态诡异,都能一目了然。

实操心得:仿真中,可以先关闭重力,手动拖动夹爪,观察灵巧手的跟随情况。重点检查极端位置(全开、全闭)和抓取过渡过程是否平滑、自然。灵巧手的手指穿透或严重扭曲是常见问题。

4.2 参数调优与性能提升

映射效果的好坏,极大程度上依赖于优化问题中各项的权重参数(( \lambda_1, \lambda_2, \lambda_3 ))。

  • 位置误差权重:这是最重要的项,权重必须足够大,以确保指尖能到达目标位置附近。但过大可能导致优化困难,陷入局部最优。
  • 姿态正则化权重(( \lambda_1 )):这个参数决定了手部姿态的“自然”程度。调大它,手会更像放松状态;调小它,手会更“努力”地去够目标点,可能产生不自然的关节角度。通常从一个较小的值(如0.01)开始尝试。
  • 关节限位权重(( \lambda_2 )):必须设置得足够大,以确保优化结果绝不超出物理限位。可以设为一个大常数(如1000)。
  • 碰撞惩罚权重(( \lambda_3 )):计算开销较大。初期调试可以暂时关闭(设为0),等基本映射工作后再加入,并从小权重开始增加。

提升实时性技巧

  • 热启动:每次优化以上一次的解作为初始值。因为夹爪运动通常是连续的,关节角变化也是连续的,这能极大加速收敛。
  • 降低精度要求:对于实时控制,不需要极高的精度。将优化器的容差(ftol)设置为1e-31e-4,最大迭代次数(maxiter)设为20-50,往往能在速度和精度间取得良好平衡。
  • 预计算与查表:对于已知的、离散的夹爪宽度值,可以预先计算好对应的灵巧手关节角,运行时进行线性插值。这完全避免了在线优化,速度最快,但灵活性差。

4.3 从仿真到实物的迁移挑战

仿真到实物(Sim2Real)的差距在这个项目中尤为明显。

  1. 模型误差:仿真中的URDF模型质量、质量属性、摩擦系数等与实物有差异。这会导致仿真中稳定的抓取,在实物上可能失败。
  2. 控制延迟:仿真中的位置控制是理想的,实物灵巧手的底层电机控制存在延迟和跟踪误差。
  3. 传感器噪声:实物夹爪的宽度传感器、力传感器存在噪声,会影响分类和映射的稳定性。

应对策略

  • 在仿真中加入噪声:在训练或调试时,给夹爪的读数加入高斯噪声,给灵巧手的关节命令加入延迟,让算法对不完美情况更鲁棒。
  • 实物标定:实物部署前,必须进行精细标定。包括:夹爪张开宽度与读数的关系、灵巧手各关节的零位、灵巧手指尖与夹爪指尖的空间变换关系(手眼标定)。
  • 引入力反馈闭环:仅靠位置映射是不够的。在实物上,应订阅灵巧手本身的指尖力传感器读数。如果检测到某个指尖力过大(可能捏太紧)或过小(可能没碰到物体),应动态微调该手指的目标位置或关节刚度,形成力-位混合控制。这是实现稳定抓取的关键。

5. 常见问题排查与进阶思考

5.1 典型问题速查表

问题现象可能原因排查步骤与解决方案
灵巧手姿态扭曲、不自然1. 姿态正则化权重(λ1)太小。
2. 关节限位约束未生效或权重太低。
3. 目标点位置计算错误,导致无解或病态解。
1. 逐步增大λ1,观察姿态变化。
2. 检查bounds参数是否正确传入优化器,增大关节限位惩罚权重λ2
3. 在RViz中可视化目标点,检查其位置是否合理(例如,是否离手太远或要求手指穿透手掌)。
映射响应延迟大1. 优化求解器迭代次数过多或收敛慢。
2. ROS节点回调函数处理耗时过长。
3. 网络通信延迟。
1. 降低优化精度(ftol),减少maxiter,使用“热启动”。
2. 使用rospy.loginfo配合rospy.get_time()对回调函数进行性能分析,优化代码(如避免在回调内进行复杂计算,可考虑用线程)。
3. 检查网络配置,确保夹爪和主控计算机在同一低延迟网络。
抓取物体时灵巧手不稳定、发抖1. 纯位置控制,缺乏力控。
2. 映射关系过于刚性,未考虑物体形变和接触滑移。
3. 底层电机PID参数未调好。
1.最重要:引入力反馈。根据指尖力传感器读数,动态调整目标位置或控制模式(如切换到阻抗控制)。
2. 在映射函数中加入“柔顺性”,例如,使目标位置是夹爪宽度的平滑函数,并允许微小自适应调整。
3. 单独调试灵巧手的底层位置控制环,确保其能快速、无超调地跟踪关节指令。
特定抓取类型(如捏取)成功率低1. 分类器对该类型识别不准。
2. 该类型对应的预定义“手势模板”不合适。
3. 目标点生成逻辑有误。
1. 收集更多该抓取类型的数据,重新训练或调整分类规则阈值。
2. 观察失败案例,调整手势模板的关节角度(例如,捏取时拇指和食指的对立程度)。
3. 仔细检查捏取时,从夹爪宽度到拇指、食指目标点的几何换算公式。
仿真成功,实物失败1. Sim2Real差距(模型、摩擦、延迟)。
2. 未进行实物标定。
3. 实物传感器读数有偏差。
1. 在仿真中增加噪声和延迟进行鲁棒性训练。
2.必须执行手眼标定和关节零位标定。
3. 对实物夹爪的宽度传感器进行校准,确保读数准确。

5.2 项目局限性与进阶方向

claw2manus项目提供了一个强大的起点,但它本质上是一个开环运动重定向系统。它的性能上限受限于夹爪本身的能力和预设的映射规则。要让它真正可靠地工作,尤其是应对复杂的、未知的物体,还需要向以下几个方向演进:

  1. 闭环技能迁移:不仅仅是映射运动,还要映射“技能”。例如,夹爪通过力反馈学会了如何拧开瓶盖,这个过程中力与运动的复杂关系,需要被提取并迁移到灵巧手上。这需要结合模仿学习和强化学习。
  2. 多模态感知融合:除了夹爪状态,引入视觉(摄像头)、触觉(灵巧手皮肤传感器)信息。当视觉发现物体很滑时,可以调整映射策略,让灵巧手施加更大的包络力;当触觉感知到物体在滑动时,可以触发反射性的抓握调整。
  3. 自适应映射网络:用神经网络代替固定的优化器或规则。输入是夹爪的多步状态序列、当前视觉图像,输出是灵巧手的关节角序列。通过大量配对数据(夹爪操作视频+灵巧手操作视频)进行端到端训练,让网络自己学习最优的映射策略,可能能处理更复杂的对应关系。
  4. 人机协作接口:将claw2manus作为一个人机协作的接口。操作员通过一个简单的夹爪手柄(力反馈设备)来遥操作复杂的灵巧手,系统在中间进行智能的映射和 tremor filtering(震颤过滤),使得操作既直观又精准。

frostmute/claw2manus这个项目出发,我们看到的不仅是一个代码工具,更是一种解决机器人领域“旧技术赋能新技术”的思路。它提醒我们,在追求最前沿的灵巧操作时,不妨回头看看那些已经成熟、稳定的解决方案,思考如何让它们成为通往未来的阶梯。在实际操作中,耐心做好仿真调试、细致完成实物标定、大胆引入力反馈闭环,是让这座“桥梁”稳固可靠的关键。

http://www.jsqmd.com/news/786483/

相关文章:

  • 助睿ETL入门实验指导
  • 跨境电商提效必看:6款指纹浏览器RPA功能深度对比
  • OpenClaw(小龙虾 AI)完整安装使用教程
  • 用专业微光,吸引技术实习生主动奔赴
  • Portage开源项目:构建跨平台AI技能市场,实现技能一次编写处处运行
  • 如何获取最完整的 AVC 日志?
  • TopicGPT:大语言模型驱动的交互式主题建模框架
  • 长时间AEC(回声信号)录制需求
  • Python 爬虫高级实战:爬虫监控告警系统搭建
  • CANN/GE 流分配特性分析
  • Go语言微服务开发必备:gomcp核心工具集的设计哲学与实战应用
  • 基于Gemini大语言模型的自动化研究工具:从Agent原理到工程实践
  • Decantr:AI生成UI的设计智能治理工具,解决前端一致性难题
  • 复合工程:构建可组合系统的架构方法论与云原生实践
  • 空间智能筑基,领航世界级智慧强港
  • 大模型驱动的网络攻击:AI对抗AI,智能WAF的进化之路
  • 读论文前先画文献地图,别一上来就硬啃 30 篇
  • 基于LangChain与Streamlit的六合一聊天机器人项目实战解析
  • 当BMI遮住了警报:男性正常体重肥胖的深度科学综述
  • 无标无感定位,重构超级港口感知体系
  • 【新手流畅上手指南】2026 OpenClaw 安装指南 Windows 系统零代码部署
  • CANN/pto-isa PTO汇编规范
  • 基于MCP协议构建Cursor团队数据AI助手:从原理到实战部署
  • 从LIME到反事实解释:可解释AI的核心技术路径与应用实战
  • 告别熬夜改稿!百考通AI带你一步步通关本科毕业论文
  • Origin:本地优先AI知识伴侣,构建可编辑记忆与知识图谱
  • Helm-GCS:构建高并发安全的私有Helm仓库实战指南
  • Windows驱动存储清理完全指南:DriverStore Explorer新手快速入门
  • 硬件IP隐私保护验证:BlindMarket框架与SAT求解优化
  • HLS设计存在的问题