机械臂直线插补matlab

时间: 2023-08-08 12:13:57 浏览: 33
在机械臂直线插补中,可以使用Matlab编写代码来实现。在引用[1]中的代码中,Compute_Equivalent_Axis_Angle函数将姿态由旋转矩阵形式表示转化为等效轴角表示。该函数接受两个参数,即源旋转矩阵和目标旋转矩阵,然后计算出等效轴角和旋转角度。等效轴角表示了旋转轴的方向和旋转角度。这个函数可以用于机械臂直线插补中的姿态规划。引用[3]中提到,在笛卡尔空间机械臂的末端轨迹规划中,可以使用等效轴角坐标系表示法来进行姿态规划。因此,可以使用引用[1]中的代码来实现机械臂直线插补的姿态规划。
相关问题

六自由机械臂实现笛卡尔直线插补Matlab代码

以下是一个简单的Matlab代码,用于实现六自由度机械臂的笛卡尔直线插补: ```matlab % 机械臂参数设置 L1 = 10; % 第一段臂长 L2 = 10; % 第二段臂长 L3 = 10; % 第三段臂长 L4 = 10; % 第四段臂长 L5 = 10; % 第五段臂长 L6 = 10; % 第六段臂长 % 坐标系原点位置 x0 = 0; y0 = 0; z0 = 0; % 目标位置 x = 50; y = 50; z = 50; % 插补步长 step = 0.1; % 计算路径 dist = sqrt((x-x0)^2 + (y-y0)^2 + (z-z0)^2); n = ceil(dist/step); x_traj = linspace(x0, x, n); y_traj = linspace(y0, y, n); z_traj = linspace(z0, z, n); % 初始化机械臂姿态 theta1 = 0; theta2 = 0; theta3 = 0; theta4 = 0; theta5 = 0; theta6 = 0; % 插补运动 for i=1:n % 计算当前位置的关节角度 [theta1,theta2,theta3,theta4,theta5,theta6] = ikine(x_traj(i), y_traj(i), z_traj(i), L1, L2, L3, L4, L5, L6); % 控制机械臂运动到目标位置 % ... end ``` 这个代码假设已经实现了一个逆运动学函数`ikine`,它用于计算机械臂关节角度,以实现给定的笛卡尔直线路径。你需要根据你的机械臂模型编写这个函数。

DDA直线插补matlab

DDA直线插补是一种逐点比较法直线插补方法,它是数字积分法插补器的一种。在MATLAB中,可以通过编写源代码实现DDA直线插补。具体实现方法是,通过累加器和被积函数寄存器来计算每个坐标方向的插补值,然后将插补值转换为进给脉冲,从而实现直线插补。DDA直线插补的优点是计算简单,速度快,但是精度相对较低。如果需要更高的精度,可以使用其他插补方法,如Bresenham算法等。

相关推荐

在MATLAB中实现四象限直线插补可以分为以下几个步骤: 1. 输入起点和终点坐标: x0 = 1; y0 = 2; % 起点坐标 x1 = 5; y1 = 7; % 终点坐标 2. 计算距离和斜率: dx = x1 - x0; dy = y1 - y0; % 计算x和y的差值 dist = sqrt(dx^2 + dy^2); % 计算距离 if dx == 0 % 斜率不存在 slope = Inf; else % 计算斜率 slope = dy/dx; end 3. 计算插补步长: v = 10; % 插补速度 Ts = 0.01; % 采样周期 step = v * Ts; % 计算插补步长 4. 计算插补方向和位置: x = x0; y = y0; % 初始化位置 delta_x = step/sqrt(1 + slope^2); % 计算x方向的位移量 delta_y = slope * delta_x; % 计算y方向的位移量 if dx < 0 % 确定x方向的正负 delta_x = -delta_x; delta_y = -delta_y; end 5. 实时更新位置: while dist > step % 当距离大于步长时,继续插补 x = x + delta_x; % 更新x坐标 y = y + delta_y; % 更新y坐标 plot(x, y, 'ro'); % 绘制插补路径 hold on; dist = dist - step; % 更新距离 end 完整的MATLAB代码如下: x0 = 1; y0 = 2; % 起点坐标 x1 = 5; y1 = 7; % 终点坐标 dx = x1 - x0; dy = y1 - y0; % 计算x和y的差值 dist = sqrt(dx^2 + dy^2); % 计算距离 if dx == 0 % 斜率不存在 slope = Inf; else % 计算斜率 slope = dy/dx; end v = 10; % 插补速度 Ts = 0.01; % 采样周期 step = v * Ts; % 计算插补步长 x = x0; y = y0; % 初始化位置 delta_x = step/sqrt(1 + slope^2); % 计算x方向的位移量 delta_y = slope * delta_x; % 计算y方向的位移量 if dx < 0 % 确定x方向的正负 delta_x = -delta_x; delta_y = -delta_y; end while dist > step % 当距离大于步长时,继续插补 x = x + delta_x; % 更新x坐标 y = y + delta_y; % 更新y坐标 plot(x, y, 'ro'); % 绘制插补路径 hold on; dist = dist - step; % 更新距离 end 注意,以上代码仅实现了简单的四象限直线插补,实际应用中需要考虑更多的因素,例如加速度、减速度等。
机械臂轨迹规划是指在机械臂操作中,通过确定机械臂末端执行器在空间中的运动路径,以实现从起始点到目标点的移动。在MATLAB中,可以使用不同的方法和工具进行机械臂轨迹规划。 其中一种常用的方法是直线规划。直线规划是指希望机械臂末端执行器在空间中距离较远的两点之间进行直线运动的轨迹规划方法。在MATLAB中,可以使用一些仿真程序和工具包来实现机械臂轨迹规划的仿真。 一个基于MATLAB的机械臂轨迹规划的仿真程序可以通过引用中提供的《机械臂轨迹规划之笛卡尔空间直线规划matlab仿真程序》来实现。该程序考虑了对位置的插补,通过确定起始点与目标点之间的直线运动路径,来实现机械臂的轨迹规划。 此外,还可以使用MATLAB中的Robotics Toolbox工具包来建立机械臂的数学模型,并进行轨迹规划。通过基于机器人学理论知识,利用标准D-H参数法建立关节型机器人的数学模型,并结合Robotics Toolbox工具包中的函数和工具,可以实现机械臂轨迹规划的仿真。相关的实验内容和实验结果可以参考引用和中提供的信息。 综上所述,机械臂轨迹规划可以通过MATLAB中的不同方法和工具来实现,包括直线规划和基于机器人学理论的数学模型建立。具体的实现方法可以根据实际需求和具体情况来选择和应用。123 #### 引用[.reference_title] - *1* [机械臂轨迹规划之笛卡尔空间直线规划matlab仿真程序](https://download.csdn.net/download/m0_53966219/87521937)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于MATLAB的关节型六轴机械臂轨迹规划仿真(2021实测完整代码)](https://blog.csdn.net/mustvvvics/article/details/117025390)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
机械臂圆弧插补算法通常采用三点法实现。具体步骤如下: 1. 确定圆弧路径的起点、终点和圆心坐标。 2. 将圆弧路径分成若干段,每一段的起点、终点分别为两个相邻的插补点,圆心为固定值。 3. 计算每一段圆弧路径的插补点坐标。 4. 根据插补点坐标进行插补。 下面详细介绍每一步的实现方法: 1. 确定圆弧路径的起点、终点和圆心坐标。 假设起点为 $P_0(x_0,y_0,z_0)$,终点为 $P_1(x_1,y_1,z_1)$,圆心为 $C(x_c,y_c,z_c)$,圆弧所在平面为 $E$,则有: $$\vec{n}=(\vec{P_0C}\times\vec{P_1C})/|\vec{P_0C}\times\vec{P_1C}|$$ $$E:\vec{n}\cdot(\vec{r}-\vec{C})=0$$ 其中 $\vec{r}(x,y,z)$ 表示圆弧上任意一点的坐标。 2. 将圆弧路径分成若干段,每一段的起点、终点分别为两个相邻的插补点,圆心为固定值。 假设将圆弧路径分成 $n$ 段,则有: $$\Delta\theta=2\pi/n$$ $$\theta_k=k\Delta\theta,k=0,1,2,\cdots,n$$ $$\vec{P_kC}=\vec{P_0C}\cos(\theta_k)-\vec{n}\times\vec{P_0C}\sin(\theta_k)$$ $$\vec{r_k}=\vec{P_kC}+\vec{C}$$ 3. 计算每一段圆弧路径的插补点坐标。 假设每一段圆弧路径的起点和终点为 $P_i$ 和 $P_{i+1}$,则有: $$\vec{P_{i+1}P_i}=\vec{r_{i+1}}-\vec{r_i}$$ $$\vec{P_{i+1}P_i}/\Delta t_i=\vec{v_i}$$ 其中 $\Delta t_i$ 表示每一段圆弧路径的时间长度。 4. 根据插补点坐标进行插补。 假设每一段圆弧路径的起点和终点为 $P_i=(x_i,y_i,z_i)$ 和 $P_{i+1}=(x_{i+1},y_{i+1},z_{i+1})$,则有: $$x(t)=x_i+(x_{i+1}-x_i)t/\Delta t_i$$ $$y(t)=y_i+(y_{i+1}-y_i)t/\Delta t_i$$ $$z(t)=z_i+(z_{i+1}-z_i)t/\Delta t_i$$ 其中 $t$ 表示当前插补时间,$t\in[0,\Delta t_i]$。
在MATLAB中,数据插补是指通过已知数据点之间的插值来估计未知数据点的值。MATLAB提供了interp1函数来进行数据插补操作。该函数的语法为y1=interp1(x,y,x1,'method'),其中x和y是已知数据点的向量,x1是需要插补的数据点的向量,'method'是插值方法,可以选择最邻近插值('nearest')、线性插值('linear')、三次样条插值('spline')或立方插值('pchip')。 对于数据插补后的可视化,可以使用plot函数绘制原始数据和插补值的波形图。例如,使用plot(x,t2pre,t(TF1),t2pre(TF1),'x')可以同时绘制原始数据和插补值,并使用title函数设置图标题为'插补后的数据波形'。同时,还可以使用xlabel和ylabel函数设置x轴和y轴的标签,使用legend函数添加图例,以便区分原始数据和插补值。最后,使用save函数可以将插补后的数据保存为MAT文件。例如,save('t2pre.mat','t2pre')可以将插补后的数据保存为名为't2pre.mat'的MAT文件。123 #### 引用[.reference_title] - *1* *2* *3* [MATLAB数据预处理——导入,插补,绘图](https://blog.csdn.net/liyangyang619/article/details/127153051)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
在Matlab中,可以使用多重插补方法对缺失值进行插补。多重插补是一种对缺失数据进行调整的方法,它通过填充一系列可能的数据集来弥补每个缺失数据值,反映了缺失数据的不确定性。在Matlab 2017a以后的版本中,更新了两个新的函数,可以方便地进行缺失值和异常值的插补。 对于缺失值插补,可以使用interp1和interp2等插值函数,但是在插值前需要使用find等函数对缺失位置进行检索以获得缺失值索引,稍显繁琐。而在更新后的版本中,可以直接使用新的插补函数进行插值操作,无需手动处理缺失位置。 对于异常值插补,可以使用filloutliers函数,语法为B = filloutliers(A,fillmethod,findmethod)。该函数可以根据指定的填充方法(fillmethod)和查找方法(findmethod)对异常值进行插补。 综上所述,Matlab提供了多种插补方法来处理缺失值和异常值,这些方法可以极大地简化数据处理的过程并提高效率。123 #### 引用[.reference_title] - *1* *3* [matlab缺失值插补以及异常值插补处理方法](https://blog.csdn.net/soloyuyang/article/details/81215789)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [缺失数据的多重插补方法](https://download.csdn.net/download/qq_21739901/8197937)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
逐点比较法是一种刀具插补方法,根据刀具位置和给定轨迹上的坐标值进行比较,决定下一步的进给方向。在这个Matlab代码中,通过输入起点和终点的坐标,以及步长,实现了第一象限的直线插补。代码中使用了循环和条件语句来进行比较和决策,以逼近给定的轨迹。在每一步中,根据当前位置和给定轨迹的相对位置,判断下一步的走向,并更新刀具的位置。同时,代码还进行了图像的绘制,以可视化插补过程。\[1\] 如果起始位置在给定轨迹的下方,下一步就向轨迹的上方走。如果起始位置在给定轨迹的内部,下一步就向轨迹的外面走。通过每走一步进行比较,并根据比较结果决定下一步的走向,从而逼近整体轮廓上的给定轨迹。\[2\] 以上是关于逐点比较法插补的Matlab代码的解释。这段代码实现了第一象限的直线插补,并通过图像展示了插补过程。 #### 引用[.reference_title] - *1* *2* [MATLAB实现数控加工中的逐点比较法直线插补(四个象限,动态显示)](https://blog.csdn.net/clear_lantern/article/details/128053064)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [逐点比较法直线插补MATLAB](https://blog.csdn.net/wang1290865309/article/details/127111490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
### 回答1: 欧姆龙CP1H直线插补是一种先进的运动控制技术,主要适用于工业自动化领域。CP1H是欧姆龙的一款高性能PLC控制器,具有强大的运动控制功能。 直线插补是指在多轴控制系统中,将多个轴的运动控制相互协调,实现直线路径的插补运动。欧姆龙CP1H直线插补模块可以实现多个轴之间的同步运动控制,使得多个轴能够按照预定的路径和速度进行直线插补运动。 CP1H直线插补具有以下特点和优势: 1. 高精度:CP1H直线插补模块采用高精度的脉冲输出,能够实现精确的位置控制,有效提高生产产品的精度和质量。 2. 高速运动:CP1H直线插补模块支持高速运动控制,能够满足工业生产中对于高速运动的需求,提高生产效率。 3. 灵活性:CP1H直线插补模块支持多轴的控制,能够实现多个轴之间的复杂运动轨迹,满足不同生产工艺的需求。 4. 便捷性:CP1H直线插补模块可以与欧姆龙的其他产品进行无缝连接,方便系统的组建和配套使用。 总之,欧姆龙CP1H直线插补技术在工业自动化领域具有广泛的应用前景,能够提高生产效率、精度和质量。通过该技术的应用,可以实现智能化、自动化的生产过程,进一步推动工业自动化的发展。 ### 回答2: 欧姆龙CP1H直线插补是一种高精度控制系统,用于控制直线运动和坐标转换。它具有快速的数据传输和处理能力,能够实现复杂的插补运动。 欧姆龙CP1H直线插补系统是由CP1H控制器和专用插补模块组成的。CP1H控制器是一种基于PLC的控制器,具有灵活的程序编写和操作,可与其他设备集成。插补模块是用来控制直线插补运动的关键组件,可以在多轴模式下进行插补运动。 欧姆龙CP1H直线插补系统可以实现多轴坐标插补运动,使得多个轴可以同时进行复杂的插补运动,达到高精度的控制效果。它支持多种插补模式,如直线插补、圆弧插补、螺旋线插补等,可以满足不同应用场景下的要求。 此外,欧姆龙CP1H直线插补系统还具有丰富的输入输出接口和通信接口,可以与其他设备进行数据交互和控制。它可以支持多种编程语言,如Ladder Diagram、Structured Text等,方便用户编写和调试控制程序。 总结来说,欧姆龙CP1H直线插补是一种高效、可靠的控制系统,适用于各种直线插补运动控制需求,具有良好的扩展性和兼容性,可以方便地与其他设备进行集成和通信。它的出现,为自动化控制领域带来了更多的选择和便利。 ### 回答3: 欧姆龙CP1H直线插补是一种先进的运动控制技术。它是由欧姆龙公司开发的一种高性能的PLC(可编程控制器)控制器。该技术可以实现非常精确的直线插补运动,可用于自动化生产线上的各种运动控制任务。 欧姆龙CP1H直线插补的工作原理是通过将离散的点按照预设的路径进行插补,从而实现连续的直线运动。它可以控制多个轴同时进行直线插补运动,达到多轴运动的协调和同步。 该技术在工业自动化领域有着广泛的应用。比如,在组装线上,可以使用欧姆龙CP1H直线插补来精确控制机械臂的运动,实现零件的自动组装。在数控机床上,可以使用该技术来控制刀具的移动,实现高精度的加工操作。在物流系统中,也可以利用该技术来控制货物的移动,提高仓储和物流的效率。 欧姆龙CP1H直线插补具有高精度、高速度和高可靠性的特点。它可以实现非常精确的运动控制,满足各种精密加工和自动化生产的需求。此外,它还具有友好的操作界面和灵活的编程功能,方便工程师进行程序开发和调试。 总结来说,欧姆龙CP1H直线插补技术是一种先进的运动控制技术,广泛应用于工业自动化领域。它能够实现高精度、高速度和高可靠性的直线插补运动控制,为各种自动化生产线提供了高效、稳定的解决方案。

最新推荐

nvm管理多版本node.js

nvm管理多版本node.js,开发多个平台代码时易用于管理node.js

学科融合背景下“编程科学”教学活动设计与实践研究.pptx

学科融合背景下“编程科学”教学活动设计与实践研究.pptx

ELECTRA风格跨语言语言模型XLM-E预训练及性能优化

+v:mala2277获取更多论文×XLM-E:通过ELECTRA进行跨语言语言模型预训练ZewenChi,ShaohanHuangg,LiDong,ShumingMaSaksham Singhal,Payal Bajaj,XiaSong,Furu WeiMicrosoft Corporationhttps://github.com/microsoft/unilm摘要在本文中,我们介绍了ELECTRA风格的任务(克拉克等人。,2020b)到跨语言语言模型预训练。具体来说,我们提出了两个预训练任务,即多语言替换标记检测和翻译替换标记检测。此外,我们预训练模型,命名为XLM-E,在多语言和平行语料库。我们的模型在各种跨语言理解任务上的性能优于基线模型,并且计算成本更低。此外,分析表明,XLM-E倾向于获得更好的跨语言迁移性。76.676.476.276.075.875.675.475.275.0XLM-E(125K)加速130倍XLM-R+TLM(1.5M)XLM-R+TLM(1.2M)InfoXLMXLM-R+TLM(0.9M)XLM-E(90K)XLM-AlignXLM-R+TLM(0.6M)XLM-R+TLM(0.3M)XLM-E(45K)XLM-R0 20 40 60 80 100 120触发器(1e20)1介绍使�

docker持续集成的意义

Docker持续集成的意义在于可以通过自动化构建、测试和部署的方式,快速地将应用程序交付到生产环境中。Docker容器可以在任何环境中运行,因此可以确保在开发、测试和生产环境中使用相同的容器镜像,从而避免了由于环境差异导致的问题。此外,Docker还可以帮助开发人员更快地构建和测试应用程序,从而提高了开发效率。最后,Docker还可以帮助运维人员更轻松地管理和部署应用程序,从而降低了维护成本。 举个例子,假设你正在开发一个Web应用程序,并使用Docker进行持续集成。你可以使用Dockerfile定义应用程序的环境,并使用Docker Compose定义应用程序的服务。然后,你可以使用CI

红楼梦解析PPT模板:古典名著的现代解读.pptx

红楼梦解析PPT模板:古典名著的现代解读.pptx

大型语言模型应用于零镜头文本风格转换的方法简介

+v:mala2277获取更多论文一个使用大型语言模型进行任意文本样式转换的方法Emily Reif 1页 达芙妮伊波利托酒店1,2 * 袁安1 克里斯·卡利森-伯奇(Chris Callison-Burch)Jason Wei11Google Research2宾夕法尼亚大学{ereif,annyuan,andycoenen,jasonwei}@google.com{daphnei,ccb}@seas.upenn.edu摘要在本文中,我们利用大型语言模型(LM)进行零镜头文本风格转换。我们提出了一种激励方法,我们称之为增强零激发学习,它将风格迁移框架为句子重写任务,只需要自然语言的指导,而不需要模型微调或目标风格的示例。增强的零触发学习很简单,不仅在标准的风格迁移任务(如情感)上,而且在自然语言转换(如“使这个旋律成为旋律”或“插入隐喻”)上都表现出了1介绍语篇风格转换是指在保持语篇整体语义和结构的前提下,重新编写语篇,使其包含其他或替代的风格元素。虽然�

xpath爬虫亚马逊详情页

以下是使用XPath爬取亚马逊详情页的步骤: 1. 首先,使用requests库获取亚马逊详情页的HTML源代码。 2. 然后,使用lxml库的etree模块解析HTML源代码。 3. 接着,使用XPath表达式提取所需的数据。 4. 最后,将提取的数据保存到本地或者数据库中。 下面是一个简单的示例代码,以提取亚马逊商品名称为例: ```python import requests from lxml import etree # 设置请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x

基于Internet的数据安全上传软件设计.doc

基于Internet的数据安全上传软件设计.doc

无监督视频对象分割的层次特征对齐网络(HFAN)

+v:mala2255获取更多论文用于无监督视频对象分割的裴根生1,沈福民2(),姚亚洲1,谢国森1(),唐振民1,唐金辉11南京理工大学,中国yazhou. njust.edu.cn2电子科技大学,中国https://github.com/NUST-Machine-Intelligence-Laboratory/HFAN抽象的。 光流是一个容易构思和宝贵的线索,为推进无监督视频对象分割(UVOS)。以往的方法大多是在UVOS环境下直接提取和融合运动和外观特征来分割目标对象。然而,光流本质上是连续帧中所有像素的瞬时速度,从而使得运动特征与对应帧中的主要对象为了解决上述挑战,我们提出了一个简洁,实用,高效的外观和运动特征对齐架构,被称为层次特征对齐网络(HFAN)。具体而言,HFAN中的关键优点是顺序特征匹配(FAM)模块和特征匹配(FAT)模块,其用于分层地处理表观和运动特征FAM能够分别将外观和运动特征与主要对象语义表�

python的多线程似乎性能提高并不显著?

是的,Python的多线程对于计算密集型任务的性能提升并不显著,这是因为Python解释器中的全局解释器锁(GIL)的存在。GIL是一种机制,它确保在任何时候只有一个线程在执行Python字节码。这意味着,即使有多个线程,它们也不能同时执行Python字节码,从而限制了多线程的性能提升。因此,对于计算密集型任务,使用Python多线程并不会显著提高CPU利用率。 如果你需要处理计算密集型任务,可以考虑使用Python的多进程模块,例如multiprocessing。与多线程不同,多进程可以同时使用多个CPU核心,从而提高CPU利用率和性能。 另外,对于I/O密集型任务,Python的多线程