第 2.3 节: 基于 Python 的关节空间与任务空间控制
在机器人控制领域,我们通常关心两个主要的“空间”:关节空间(Joint Space)和任务空间(Task Space,也常称为操作空间 Operational Space)。关节空间描述了机器人各关节的角度或位置集合,是机器人硬件直接控制的量。而任务空间描述了机器人末端执行器(End-effector)在三维空间中的位置和姿态,这通常是我们希望机器人完成任务时的目标。
本节我们将回顾机器人正逆运动学的基础,探讨如何使用 Python 实现这些计算,并介绍基于 PID 控制器的关节控制和基本的任务空间控制概念及实践。
2.3.1 机器人正逆运动学回顾与 Python 实现
什么是正运动学 (Forward Kinematics - FK)?
正运动学解决的问题是:给定机器人所有关节的角度(或位置),计算出机器人末端执行器在基本坐标系中的位置和姿态。 简单来说,就是“我知道关节怎么动,我想知道爪子(或脚)在哪里”。
正运动学通常通过一系列齐次变换矩阵(Homogeneous Transformation Matrices)来描述,这些矩阵根据每个关节的运动和连杆的几何形状构建。连接基坐标系到末端执行器的所有变换矩阵的乘积就是末端执行器相对于基坐标系的位姿矩阵。
什么是逆运动学 (Inverse Kinematics - IK)?
逆运动学解决的问题是:给定机器人末端执行器在基本坐标系中期望的位置和姿态,计算出机器人所有关节需要设置的角度(或位置)。 简单来说,就是“我知道爪子(或脚)应该在哪里,我想知道每个关节应该怎么转”。
与正运动学不同,逆运动学通常更复杂,可能存在多个解、无解或奇异构型(Singularity)问题。常用的解决方法包括:
- 解析解 (Analytical Solutions): 对于结构简单的机器人(如许多串联机械臂),可以通过几何或代数方法直接推导出关节角度的公式。这是最快、最准确的方法,但只适用于特定构型。
- 数值解 (Numerical Solutions): 对于更复杂的机器人,通常使用迭代优化方法来寻找逆运动学解。这些方法通过不断调整关节角度,最小化当前末端执行器位姿与目标位姿之间的误差,直到误差足够小。常见的数值方法有牛顿法、梯度下降法等。
Python 实现:PyKDL vs SciPy
- PyKDL (Python Bindings for Orocos KDL): Orocos Kinematics and Dynamics Library (KDL) 是一个 C++ 库,PyKDL 提供了其 Python 接口。KDL 专门为机器人学设计,提供了丰富的功能,包括 FK, IK, Jacobian, Dynamics 等。如果你的机器人模型复杂且需要进行动力学计算,或者追求更高的性能,PyKDL 是一个很好的选择。安装可能稍微复杂一些。
- SciPy: SciPy 是一个强大的 Python 科学计算库,其中的
scipy.spatial.transform
模块可以方便地进行三维空间中的旋转和平移变换,scipy.optimize
模块提供了各种优化算法,非常适合用于求解数值逆运动学问题。SciPy 通常更容易安装和使用,对于理解数值 IK 的原理很有帮助。
在本节的实践中,考虑到易用性和通用性,我们将使用 SciPy 来实现简化的 G1 单腿的 FK 和数值 IK。
2.3.1.1 G1 单腿简化模型与运动学计算
我们以 Unitree G1 的一条腿为例进行简化。假设腿部有三个主要关节用于实现空间中的位置控制:髋关节俯仰 (Hip Pitch)、大腿关节俯仰 (Thigh Pitch) 和小腿关节俯仰 (Calf Pitch)。我们忽略髋关节的横滚/偏航以及脚踝关节,将模型简化为在侧向平面内运动的 3-DOF 机械腿。
模型参数 (示例):
- 髋关节到大腿关节的垂直偏移 (y 方向):
hip_offset_y
- 大腿长度:
thigh_length
- 小腿长度:
calf_length
Python
import numpy as np
from scipy.spatial.transform import Rotation as R
from scipy.optimize import minimize# 假设的 G1 单腿简化模型参数 (单位: 米)
# 注意:这些值是示例,可能与实际 G1 机器人参数不同
HIP_OFFSET_Y = 0.1 # 髋关节到大腿关节的垂直偏移
THIGH_LENGTH = 0.3 # 大腿长度
CALF_LENGTH = 0.3 # 小腿长度# 关节顺序:[Hip Pitch, Thigh Pitch, Calf Pitch] (弧度)
# 基本坐标系位于髋关节处def fk_leg(joint_angles):"""简化单腿的正运动学计算。假设关节顺序为 [hip_pitch, thigh_pitch, calf_pitch]。所有关节旋转轴平行于Y轴 (侧向),旋转绕自身关节原点进行。"""hip_pitch, thigh_pitch, calf_pitch = joint_angles# 1. 从基本坐标系到髋关节俯仰轴末端 (考虑髋关节偏移)# 这里简化处理,假设髋关节偏移只影响初始平移T0_hip = np.eye(4)T0_hip[1, 3] = -HIP_OFFSET_Y # 假设髋关节中心在基座下方 Y 轴负方向偏移# 2. 髋关节俯仰旋转 (绕Y轴)R_hip = R.from_euler('y', hip_pitch).as_matrix()T_hip_joint = np.eye(4)T_hip_joint[:3, :3] = R_hip# 关节旋转通常不带平移,平移是连杆的长度# 3. 髋关节俯仰轴末端到大腿关节 (大腿的根部)# 忽略了髋关节的其他自由度,直接连接到大腿# 我们将髋关节的起始点作为连杆1的开始,大腿根部作为连杆1的结束# simplified: let's assume hip pitch rotates the base of the thigh link# T0_thigh_base = T0_hip @ T_hip_joint # This isn't quite right for standard DH# Let's redefine: Base is origin. Hip pitch joint is at origin.# Link 1 is a point at (0, -HIP_OFFSET_Y, 0) after the hip pitch rotation?# This is getting complex. Let's use a simpler DH-like thinking for this example.# Assume Hip Pitch is joint 1 at origin (0,0,0), rotating link 1.# Link 1 ends at (0, -HIP_OFFSET_Y, 0) *after* rotation. Then thigh starts there.# Simpler approach: Base frame -> Joint 1 (Hip Pitch) -> Link 1 (Thigh) -> Joint 2 (Thigh Pitch) -> Link 2 (Calf) -> Joint 3 (Calf Pitch) -> Link 3 (Foot).# Let's refine the FK chain:# Frame 0 (Base)# Frame 1 (after Hip Pitch, at origin, rotating around Y)# Frame 2 (after translating along Link 1 - conceptual short link to thigh joint)# Frame 3 (after Thigh Pitch, at thigh joint)# Frame 4 (after translating along Link 2 - thigh length)# Frame 5 (after Calf Pitch, at calf joint)# Frame 6 (after translating along Link 3 - calf length, end-effector)# Simpler simplified chain for this example:# Start at a point (0, -HIP_OFFSET_Y, 0) relative to base. This is the effective base of the 2-link leg.# Joint 1 (relative Hip Pitch) rotates Link 1 (Thigh).# Joint 2 (relative Thigh Pitch) rotates Link 2 (Calf) relative to Link 1.# Joint 3 (relative Calf Pitch) rotates End-effector relative to Link 2.# The angles hip_pitch, thigh_pitch, calf_pitch are relative angles.# Let's use a common convention: angles are relative to the *previous* link's frame,# and link lengths are along the Z axis of the *current* frame before rotation,# or translation along X after rotation... DH parameters are the standard way.# Without strict DH, let's just use sequential transformations assuming rotations are around Y.T0_1 = np.eye(4) # Transformation from Frame 0 to Frame 1 (at Hip Pitch joint) - assume originR1 = R.from_euler('y', hip_pitch).as_matrix()T1_2 = np.eye(4)T1_2[1, 3] = -HIP_OFFSET_Y # Translation from Hip Pitch joint to Thigh Pitch joint base (conceptually)T0_2 = T0_1 @ T1_2 # This places the effective leg base after hip offset# Now consider the 2-link arm part starting from T0_2 origin# Joint 2 is Thigh Pitch, rotates around Y# Link 2 is Thigh Length# Joint 3 is Calf Pitch, rotates around Y# Link 3 is Calf LengthT2_3 = np.eye(4) # Transformation from Frame 2 to Frame 3 (at Thigh Pitch joint) - origin of the 2-link leg partR3 = R.from_euler('y', thigh_pitch).as_matrix()T3_4 = np.eye(4)T3_4[2, 3] = THIGH_LENGTH # Translate along the Z-axis of Frame 3 by thigh length (assuming Z is forward/downwards)T0_4 = T0_2 @ T2_3 @ T3_4 # Position after Thigh link# Now add the Calf part# Joint 3 is Calf Pitch, rotates around Y# Link 3 is Calf LengthT4_5 = np.eye(4) # Transformation from Frame 4 to Frame 5 (at Calf Pitch joint) - origin of the calf linkR5 = R.from_euler('y', calf_pitch).as_matrix()T5_6 = np.eye(4)T5_6[2, 3] = CALF_LENGTH # Translate along the Z-axis of Frame 5 by calf lengthT0_6 = T0_4 @ T4_5 @ T5_6 # Final end-effector position# The position is the last column of the transformation matrixend_effector_pos = T0_6[:3, 3]return end_effector_pos# Example usage of FK
test_angles = np.array([np.deg2rad(0), np.deg2rad(0), np.deg2rad(0)]) # Stretched leg
pos = fk_leg(test_angles)
print(f"FK for angles {np.rad2deg(test_angles)} deg: Position = {pos}")test_angles = np.array([np.deg2rad(30), np.deg2rad(-45), np.deg2rad(90)])
pos = fk_leg(test_angles)
print(f"FK for angles {np.rad2deg(test_angles)} deg: Position = {pos}")
注意: 上述 fk_leg
实现是基于一个简化的、假设的连杆连接方式和平移/旋转顺序。实际机器人的 DH 参数或URDF模型会提供精确的变换。这里主要是为了演示如何在 Python 中进行基于变换矩阵的 FK 计算。旋转是使用 scipy.spatial.transform.Rotation
完成的,平移直接修改齐次变换矩阵的第四列。
2.3.1.2 数值逆运动学实现
数值逆运动学通过优化方法找到使 FK 输出接近目标位置的关节角度。我们定义一个目标函数,该函数计算当前关节角度下末端执行器位置与目标位置之间的欧几里得距离的平方,然后使用优化器最小化这个函数。
Python
# Objective function for IK
def objective_ik(joint_angles, target_pos):"""IK 优化问题的目标函数:计算当前关节角度下的末端执行器位置与目标位置之间的距离平方。"""current_pos = fk_leg(joint_angles)return np.sum((current_pos - target_pos)**2)def ik_leg(target_pos, initial_guess_angles, joint_limits=None):"""使用 SciPy 优化器求解单腿的逆运动学。"""# bounds (optional): list of tuples (min_angle, max_angle) for each joint# example bounds for G1 leg (approximate, in radians):# Hip Pitch: -1.57 to 1.57 (-90 to 90 deg)# Thigh Pitch: -2.61 to 0.61 (-150 to 35 deg)# Calf Pitch: -2.53 to -0.61 (-145 to -35 deg) # Note: Angles often relative# Let's provide example bounds, convert degrees to radians# Assuming the angles are relative pitches as in the FKexample_bounds_deg = [(-90, 90), # Hip Pitch(-150, 35), # Thigh Pitch(-145, -35) # Calf Pitch (these might be relative angles or range)]example_bounds_rad = [(np.deg2rad(lower), np.deg2rad(upper)) for lower, upper in example_bounds_deg]# Use minimize from scipy.optimizeresult = minimize(objective_ik,initial_guess_angles, # 初始猜测值args=(target_pos,), # 传递给目标函数的额外参数method='SLSQP', # 选择一个优化方法,SLSQP是一个常用的选择bounds=joint_limits if joint_limits else example_bounds_rad, # 关节限制tol=1e-6 # 容忍度)if result.success:return result.x # 返回找到的关节角度else:print(f"Warning: IK failed to converge. Message: {result.message}")return None # 或返回初始猜测值,取决于需求# Example usage of IK
target_pos = np.array([0.0, -HIP_OFFSET_Y - 0.1, -0.5]) # Example target position
initial_angles = np.array([0, 0, 0]) # Start from stretched legik_solution = ik_leg(target_pos, initial_angles)if ik_solution is not None:print(f"\nTarget Position: {target_pos}")print(f"IK Solution (radians): {ik_solution}")print(f"IK Solution (degrees): {np.rad2deg(ik_solution)}")# Verify the FK of the IK solutionreached_pos = fk_leg(ik_solution)print(f"Reached Position with IK solution: {reached_pos}")print(f"Error distance: {np.linalg.norm(reached_pos - target_pos)}")
else:print("\nIK solution not found.")# Another IK example
target_pos_2 = np.array([0.0, -HIP_OFFSET_Y, -0.4]) # Different target
initial_angles_2 = np.deg2rad([10, -30, -60]) # Different initial guessik_solution_2 = ik_leg(target_pos_2, initial_angles_2)if ik_solution_2 is not None:print(f"\nTarget Position: {target_pos_2}")print(f"IK Solution 2 (radians): {ik_solution_2}")print(f"IK Solution 2 (degrees): {np.rad2deg(ik_solution_2)}")reached_pos_2 = fk_leg(ik_solution_2)print(f"Reached Position with IK solution 2: {reached_pos_2}")print(f"Error distance 2: {np.linalg.norm(reached_pos_2 - target_pos_2)}")
else:print("\nIK solution 2 not found.")
通过运行上述代码,我们可以看到如何使用 SciPy 的优化器求解给定目标位置的关节角度。需要注意的是,数值 IK 对初始猜测值敏感,且不能保证找到全局最优解或所有可能的解。关节限制 (bounds
) 对于找到符合物理约束的解至关重要。
2.3.2 基于 PID 控制器实现关节位置/速度/力矩控制
PID 控制器是一种经典且广泛应用于工业控制领域的反馈控制器。它根据系统误差(目标值与当前值之差)的比例 (P)、积分 (I) 和微分 (D) 来计算控制输出。
对于大多数具备关节力矩控制接口的机器人(如 Unitree G1),典型的控制架构是:位置环 (PID) -> 速度环 (PID) -> 力矩指令。外部位置指令通过位置环 PID 产生速度指令,速度指令通过速度环 PID 产生力矩指令,最终由电机的电流控制器产生力矩。但我们也可以直接使用一个 PID 控制器来输出力矩指令以达到目标位置或速度,这取决于系统的简化程度。
2.3.2.1 Python 实现一个通用的 PID 控制器类
我们可以创建一个 Python 类来实现一个可复用的 PID 控制器。
总结
本节我们回顾了机器人正逆运动学,并使用 SciPy 实现了简化的 G1 单腿 FK 和数值 IK。接着,我们实现了一个通用的 PID 控制器类,并在一个简化的关节位置控制仿真中进行了演示。最后,我们将 FK、IK 和 PID 控制器结合起来,展示了如何实现一个基于 IK 的基本任务空间位置控制。
在本节的实践中,我们将演示基于 IK 的基本任务空间位置控制,这是一种相对简单的实现方式,非常适合入门理解任务空间控制的概念。
2.4 Python 实践:G1 单腿的简单任务空间控制
我们将结合之前实现的 FK、IK 和 PID 控制器,编写一个脚本来控制简化的 G1 单腿模型达到一个指定的末端位置。
- 比例项 (P): 与当前误差成正比。误差越大,控制输出越大。提供即时响应。
- 积分项 (I): 与误差的累积(积分)成正比。用于消除稳态误差,即使误差很小,长时间累积的积分项也能产生足够的控制作用。
- 微分项 (D): 与误差的变化率(微分)成正比。用于预测误差的变化趋势,提供阻尼作用,减少超调和震荡
-
-
应用于机器人关节控制:
- 关节位置控制: 误差是目标关节角度与当前关节角度之差。PID 输出可以是关节力矩(电流)或关节速度指令,取决于机器人底层控制接口。
- 关节速度控制: 误差是目标关节速度与当前关节速度之差。PID 输出通常是关节力矩(电流)。
- 关节力矩控制: 误差是目标关节力矩与当前实际关节力矩之差(如果能测量)。这层控制通常在更底层,更常见的是将 PID 输出作为力矩指令。
- Python
import timeclass PIDController:"""一个简单的 PID 控制器类。"""def __init__(self, Kp, Ki, Kd, setpoint):self.Kp = Kpself.Ki = Kiself.Kd = Kdself.setpoint = setpoint # 目标值self.integral = 0self.previous_error = 0self.last_time = time.time() # 用于计算dtdef update(self, current_value):"""更新 PID 控制器并计算控制输出。current_value: 当前的系统值。"""current_time = time.time()dt = current_time - self.last_timeif dt <= 0: # 避免除以零或负dtreturn 0error = self.setpoint - current_value# Proportional termp_term = self.Kp * error# Integral termself.integral += error * dt# Optional: Integral windup prevention (clamping the integral term)# self.integral = max(min(self.integral, integral_max), integral_min) # Need to define integral_max/min# Derivative termderivative = (error - self.previous_error) / dtd_term = self.Kd * derivative# Update previous error and timeself.previous_error = errorself.last_time = current_time# Total outputoutput = p_term + self.integral * self.Ki + d_term# Optional: Output clamping# output = max(min(output, output_max), output_min) # Need to define output_max/minreturn outputdef set_setpoint(self, setpoint):"""设置新的目标值,并重置积分项以避免旧误差累积的影响。"""self.setpoint = setpointself.integral = 0 # Reset integral on setpoint changeself.previous_error = self.setpoint - self.setpoint # Reset previous error (should be 0)self.last_time = time.time() # Reset time for dt calculationdef reset(self):"""重置控制器状态。"""self.integral = 0self.previous_error = 0self.last_time = time.time()# Example usage of PID Controller (simulating a simple joint) if __name__ == "__main__":# Simulate a single joint trying to reach a target angletarget_angle = np.deg2rad(45) # Target angle in radiansinitial_angle = np.deg2rad(0) # Starting angle# PID gains (need tuning based on the system dynamics)# These are example gains, likely need adjustment for a real robot jointkp = 2.0ki = 0.1kd = 0.5pid = PIDController(kp, ki, kd, target_angle)# Simple simulation parameterssimulation_time = 5.0 # secondsdt = 0.01 # simulation step timecurrent_angle = initial_anglecurrent_velocity = 0.0 # Simple simulation doesn't have complex dynamicsprint(f"Starting simulation for joint position control...")print(f"Target angle: {np.rad2deg(target_angle):.2f} deg")# In a real robot, you would read sensor data (current_angle, current_velocity)# Here, we simulate the effect of the control output on the angle.# A very simple model: control output is like a velocity command.# In a real system, output would be torque/voltage, affecting acceleration.time_steps = int(simulation_time / dt)for i in range(time_steps):# Get control output from PIDcontrol_output = pid.update(current_angle)# --- Simulate joint dynamics (very simple approximation) ---# Assume control_output acts like a velocity command for simplicity# A more realistic simulation would use F=ma, integrate acceleration to velocity, etc.current_angle += control_output * dt# In a real system, you'd send `control_output` (e.g., as torque) to the robot API# and read the new `current_angle` from sensors in the next loop iteration.# ---------------------------------------------------------# Print or log current stateif i % 50 == 0: # Print less frequentlyprint(f"Time: {i*dt:.2f}s, Current Angle: {np.rad2deg(current_angle):.2f} deg, Error: {np.rad2deg(pid.setpoint - current_angle):.2f} deg, Control Output: {control_output:.2f}")print(f"Simulation finished. Final angle: {np.rad2deg(current_angle):.2f} deg")
上述代码提供了一个简单的 PID 控制器类及其在一个简化仿真环境中的使用示例。请注意,模拟关节动力学部分是高度简化的,仅用于演示 PID 控制器的基本工作方式。在实际机器人控制中,你需要与机器人 SDK 或控制接口交互,发送控制指令(如力矩、速度或位置指令,取决于接口),并读取真实的关节传感器数据。PID 增益 Kp,Ki,Kd 的选取(PID 调参)对于实现稳定和快速的控制至关重要,通常需要通过实验或更高级的方法来确定。
2.3.3 基本任务空间控制概念
任务空间控制的目标是直接控制机器人的末端执行器在笛卡尔空间(3D 位置和姿态)中的运动。与关节空间控制直接操作关节角度不同,任务空间控制需要一个额外的层来处理关节空间和任务空间之间的转换,通常依赖于正逆运动学和雅可比矩阵。
实现基本任务空间控制的常见方法:
-
基于逆运动学 (IK-based) 的方法:
- 原理: 给定一个目标任务空间位姿,使用逆运动学计算达到该位姿所需的关节角度。
- 控制: 然后使用关节空间控制器(如上面实现的 PID)来驱动机器人关节到达这些计算出的关节角度。
- 优点: 概念简单,易于实现。
- 缺点:
- 逆运动学可能没有解析解,需要数值求解,计算量可能较大。
- 逆运动学解可能不唯一,需要选择合适的解。
- 在运动过程中,如果频繁计算 IK,可能会导致运动不平滑,特别是在接近奇异点时。
- 这种方法本质上是在控制关节位置,任务空间的轨迹跟踪性能受 IK 求解频率和关节控制器性能限制。
-
基于雅可比 (Jacobian-based) 的方法:
- 原理: 利用机器人雅可比矩阵(Jacobian Matrix)来建立关节速度和任务空间速度之间的关系 (x˙=J(q)q˙,其中 x˙ 是任务空间速度,q˙ 是关节速度,J(q) 是雅可比矩阵,依赖于关节角度 q)。
- 控制: 通过计算期望的任务空间速度,并利用雅可比矩阵(或其伪逆)求解所需的关节速度,然后控制关节速度。也可以结合任务空间的误差直接计算所需的关节速度,例如使用任务空间 PID 控制器。
- 优点: 可以实现更平滑的运动,更适合轨迹跟踪,能够更好地处理奇异点(通过各种雅可比伪逆方法)。
- 缺点: 需要计算和更新雅可比矩阵,处理奇异点需要更复杂的算法。
- 简化模型和动力学: 这里的腿部模型和关节动力学是高度简化的。实际机器人具有复杂的动力学特性,需要更精确的模型和更鲁棒的控制器。
- IK 计算频率: 在每个时间步都计算 IK 可能计算量较大,且 IK 的解质量会影响控制效果。更高级的任务空间控制(如基于雅可比的方法)通常更高效。
- 无姿态控制: 这个例子只控制了末端执行器的位置,没有控制其姿态。在任务空间控制中,通常需要控制末端执行器的 6-DOF 位姿 (x, y, z, roll, pitch, yaw)。IK 和 Jacobian 方法都可以扩展到 6-DOF。
- 无碰撞检测和避障: 实际机器人控制需要考虑环境中障碍物,避免碰撞。
- 单腿 vs 双腿/全身: G1 是一个双足机器人,控制全身运动(包括平衡、步态生成)远比控制单腿复杂,需要考虑多个腿的协调、质心控制等。
- Python
import numpy as np import time from scipy.spatial.transform import Rotation as R from scipy.optimize import minimize # Already imported# Assuming the FK, IK, and PIDController definitions from previous sections are available # (Copy and paste the code for HIP_OFFSET_Y, THIGH_LENGTH, CALF_LENGTH, fk_leg, objective_ik, ik_leg, PIDController here)# --- Copy FK, IK, and PIDController definitions from above --- HIP_OFFSET_Y = 0.1 THIGH_LENGTH = 0.3 CALF_LENGTH = 0.3def fk_leg(joint_angles):hip_pitch, thigh_pitch, calf_pitch = joint_anglesT0_1 = np.eye(4)R1 = R.from_euler('y', hip_pitch).as_matrix()T1_2 = np.eye(4)T1_2[1, 3] = -HIP_OFFSET_YT0_2 = T0_1 @ T1_2T2_3 = np.eye(4)R3 = R.from_euler('y', thigh_pitch).as_matrix()T3_4 = np.eye(4)T3_4[2, 3] = THIGH_LENGTHT0_4 = T0_2 @ T2_3 @ T3_4T4_5 = np.eye(4)R5 = R.from_euler('y', calf_pitch).as_matrix()T5_6 = np.eye(4)T5_6[2, 3] = CALF_LENGTHT0_6 = T0_4 @ T4_5 @ T5_6return T0_6[:3, 3]def objective_ik(joint_angles, target_pos):current_pos = fk_leg(joint_angles)return np.sum((current_pos - target_pos)**2)def ik_leg(target_pos, initial_guess_angles, joint_limits=None):example_bounds_deg = [(-90, 90), (-150, 35), (-145, -35)]example_bounds_rad = [(np.deg2rad(lower), np.deg2rad(upper)) for lower, upper in example_bounds_deg]result = minimize(objective_ik,initial_guess_angles,args=(target_pos,),method='SLSQP',bounds=joint_limits if joint_limits else example_bounds_rad,tol=1e-6)if result.success:return result.xelse:# print(f"Warning: IK failed to converge. Message: {result.message}")return initial_guess_angles # Return last valid angles or initial guessclass PIDController:def __init__(self, Kp, Ki, Kd, setpoint):self.Kp = Kpself.Ki = Kiself.Kd = Kdself.setpoint = setpointself.integral = 0self.previous_error = 0self.last_time = time.time()def update(self, current_value):current_time = time.time()dt = current_time - self.last_timeif dt <= 0:dt = 1e-6 # Prevent division by zeroerror = self.setpoint - current_valuep_term = self.Kp * errorself.integral += error * dtderivative = (error - self.previous_error) / dtd_term = self.Kd * derivativeself.previous_error = errorself.last_time = current_timeoutput = p_term + self.integral * self.Ki + d_termreturn outputdef set_setpoint(self, setpoint):self.setpoint = setpointself.integral = 0self.previous_error = self.setpoint - self.setpointself.last_time = time.time()def reset(self):self.integral = 0self.previous_error = 0self.last_time = time.time() # --- End of copy-pasted definitions ---# Task Space Control Simulation Parameters target_task_pos = np.array([0.0, -HIP_OFFSET_Y - 0.1, -0.5]) # Target end-effector position # target_task_pos = np.array([0.0, -HIP_OFFSET_Y - 0.2, -0.4]) # Another targetsimulation_time = 10.0 # seconds dt = 0.01 # simulation step time# Initial state current_joint_angles = np.deg2rad([0, 0, 0]) # Start from stretched leg current_task_pos = fk_leg(current_joint_angles)# PID controllers for each joint (tuned for joint position control) # These gains will likely need tuning for your specific simulation/robot model # Example gains (arbitrary, need real tuning) joint_kp = 5.0 joint_ki = 0.2 joint_kd = 1.0pid_hip_pitch = PIDController(joint_kp, joint_ki, joint_kd, current_joint_angles[0]) pid_thigh_pitch = PIDController(joint_kp, joint_ki, joint_kd, current_joint_angles[1]) pid_calf_pitch = PIDController(joint_kp, joint_ki, joint_kd, current_joint_angles[2])print(f"Starting task space control simulation...") print(f"Initial Joint Angles (deg): {np.rad2deg(current_joint_angles)}") print(f"Initial Task Position: {current_task_pos}") print(f"Target Task Position: {target_task_pos}")time_steps = int(simulation_time / dt)# Simple simulation loop # In each step, we: # 1. Calculate the required joint angles using IK for the target task position. # 2. Set these angles as the setpoints for the joint PID controllers. # 3. Update the joint angles based on the PID outputs (simplified dynamics). # 4. Recalculate the current task position using FK.for i in range(time_steps):# --- Task Space Control Logic ---# Calculate target joint angles using IK for the desired task position# We use the current_joint_angles as the initial guess for IKdesired_joint_angles = ik_leg(target_task_pos, current_joint_angles)if desired_joint_angles is None:print(f"Time {i*dt:.2f}s: IK failed. Skipping control update.")# In a real system, you might have strategies to handle IK failurecontinue # Skip this step if IK fails# Set the new target angles for the joint PID controllerspid_hip_pitch.set_setpoint(desired_joint_angles[0])pid_thigh_pitch.set_setpoint(desired_joint_angles[1])pid_calf_pitch.set_setpoint(desired_joint_angles[2])# --- End Task Space Control Logic ---# --- Simulate Joint Control (using PID) and Robot Dynamics ---# Get control outputs for each joint PIDcontrol_hip = pid_hip_pitch.update(current_joint_angles[0])control_thigh = pid_thigh_pitch.update(current_joint_angles[1])control_calf = pid_calf_pitch.update(current_joint_angles[2])# Apply control outputs to simulate joint movement# Again, this is a very simple model. Control output is like a velocity command.# A real system would integrate torques to get acceleration, then velocity, then position.current_joint_angles[0] += control_hip * dtcurrent_joint_angles[1] += control_thigh * dtcurrent_joint_angles[2] += control_calf * dt# In a real system, you would send control_outputs (e.g., as torques/voltages)# to the robot's motor controllers via its API and read new joint angles.# ---------------------------------------------------------------# Update current task position based on new joint anglescurrent_task_pos = fk_leg(current_joint_angles)# Print or log current stateif i % 100 == 0: # Print less frequentlytask_error = np.linalg.norm(target_task_pos - current_task_pos)print(f"Time: {i*dt:.2f}s, Current Joint Angles (deg): [{np.rad2deg(current_joint_angles[0]):.1f}, {np.rad2deg(current_joint_angles[1]):.1f}, {np.rad2deg(current_joint_angles[2]):.1f}], Task Pos Error: {task_error:.4f} m")# Simple condition to stop early if target is reachedif np.linalg.norm(target_task_pos - current_task_pos) < 0.005: # Threshold 5mmprint(f"Target task position reached within threshold at time {i*dt:.2f}s.")breakprint(f"Simulation finished.") print(f"Final Joint Angles (deg): {np.rad2deg(current_joint_angles)}") print(f"Final Task Position: {current_task_pos}") task_error_final = np.linalg.norm(target_task_pos - current_task_pos) print(f"Final Task Position Error: {task_error_final:.4f} m")
运行这段代码,你可以观察到模拟的腿部关节角度如何变化,以及末端执行器位置如何逐渐接近目标位置。这个示例展示了基于 IK 的任务空间控制的基本思想:将任务空间的位姿目标通过 IK 转化为关节空间的位置目标,然后利用关节控制器驱动机器人到达这些关节位置。
局限性与改进:
相关文章:
第 2.3 节: 基于 Python 的关节空间与任务空间控制
在机器人控制领域,我们通常关心两个主要的“空间”:关节空间(Joint Space)和任务空间(Task Space,也常称为操作空间 Operational Space)。关节空间描述了机器人各关节的角度或位置集合ÿ…...
[更新完毕]2025东三省A题深圳杯A题数学建模挑战赛数模思路代码文章教学:热弹性物理参数估计
完整内容请看文章最下面的推广群 热弹性物理参数估计 摘要 随着现代电子设备向高性能、微型化方向发展,芯片封装结构面临着日益严峻的热机械可靠性挑战。BGA(球栅阵列)和QFN(四方扁平无引脚)作为两种主流封装形式&am…...
【大模型面试每日一题】Day 5:GQA vs MHA效率对比
【大模型面试每日一题】Day 5:GQA vs MHA效率对比 📌 题目重现 🌟🌟 面试官:最近一些研究(如LLaMA、Mixtral)采用Grouped-Query Attention(GQA)代替传统的Multi-Head A…...
【c语言】字符函数和字符串函数
目录 1.函数介绍 1.1 strlen 1.2 strcpy 1.3 strcat 1.4 strcmp 1.5 strncpy 1.6 strncat 1.7 strncmp 1.8 strstr 1.9 strtok 1.10 strerror 1.11 memcpy 1.12 memmove 1.13 memset 编辑 1.14 memcmp C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有…...
使用 MCP(模型上下文协议)和 Claude 在 Node.js 中构建聊天应用程序
大家好,这里是架构资源栈!点击上方关注,添加“星标”,一起学习大厂前沿架构! 使用 Node.js 中的 MCP(模型上下文协议)构建聊天应用程序 我最近开发了一个简单的聊天应用程序,允许 …...
B站Michale_ee——ESP32_IDF SDK——FreeRTOS_2 队列
一、通过队列传递三种类型数据(整型、结构体、指针) 1.队列简介 FreeRTOS中的队列本质就是一个先入先出的缓冲区(FIFO,First Input First Output) 2.API简介 (1)创建队列的API (…...
小米MiMo:7B模型逆袭AI大模型战场的技术密码
小米MiMo:7B模型逆袭AI大模型战场的技术密码 在大模型竞争愈发激烈的2025年4月30日,小米以一款名为 MiMo-7B 的开源模型强势突围,在数学推理与代码能力评测中表现亮眼,不仅与规模更大的模型正面对抗,甚至超越了 OpenA…...
Java关键字解析
Java关键字是编程语言中具有特殊含义的保留字,不能用作标识符(如变量名、类名等)。Java共有50多个关键字(不同版本略有差异),下面我将分类详细介绍这些关键字及其使用方式。 一、数据类型相关关键字 1. 基…...
YOLOv8模型训练过程
一,conda环境的创建就略过了 先进行库工具安装 pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple 二,在github上下载好模型,先预测一把 yolo predict modelyolov8n.pt sourceultralytics/assets/bus.jpg 如果无…...
1996-2022年全国31省ZF干预度数据/财政干预度数据(含原始数据+计算过程+结果)
1996-2022年全国31省ZF干预度数据/财政干预度数据(含原始数据计算过程结果) 1、时间:1996-2022年 2、来源:国家统计局和各省年鉴 3、指标:地方财政一般预算支出、地区生产总值(GDP)、ZF干预度…...
STM32移植U8G2
STM32 移植 U8G2 u8g2 (Universal 8bit Graphics Library version2 的缩写)是用于嵌入式设备的单色图形库,可以在单色屏幕中绘制 GUI。u8g2 内部附带了例如 SSD13xx,ST7xx 等很多 OLED,LCD 驱动。内置多种不同大小和风…...
26.电流信号的强抗干扰能力运用
电流信号强抗干扰能力运用 1. 电流型信号与电压型信号的传输抗干扰能力差异2. 电流型信号电路示例 1. 电流型信号与电压型信号的传输抗干扰能力差异 现场干扰以电场干扰为主,电压较高但是能量(电流I)较低。如果以能量信号作为传输媒介&#x…...
【 Node.js】 Node.js安装
下载 下载 | Node.js 中文网https://nodejs.cn/download/ 安装 双击安装包 点击Next 勾选使用许可协议,点击Next 选择安装位置 点击Next 点击Next 点击Install 点击Finish 完成安装 添加环境变量 编辑【系统变量】下的变量【Path】添加Node.js的安装路径--如果…...
经典算法 青蛙跳杯子
青蛙跳杯子 题目描述 桌子上有n行m列的杯子,每个杯子与相邻杯子之间的距离为1,已知青蛙的跳跃半径为d,青蛙现在在第一行第一列的杯子上,它跳到最后一行最后一列的杯子上,最少需要跳几次? 输入描述 输入…...
C语言-函数的递归和迭代
以下是我初学C语言的笔记记录,欢迎在评论区补充留言 一,函数的递归 大致有这么几个问题【我看完这堂课后,自己总结的几个问题】 * 问题 1,什么是函数的递归, 2,它是干什么用的,3,有什么条件吗…...
Linux安装部署Postgresql数据库
联网安装方案 Linux能在线安装依赖组件的前提下,可以快速安装部署PG数据库,安装过程使用root管理员帐号: 首先,使用如下命令自动下载Postgresql组件: # 在openEuler、Fedora或CentOS 8上,你可能会使用&a…...
学习与规划的融合Dyna-Q:python从零实现
🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…...
电子病历高质量语料库构建方法与架构项目(环境聆听与自动化文档生成篇)
电子病历高质量语料库的构建是一个复杂而系统的工程,涉及数据收集、清洗、标注、验证等多个环节。在项目实施过程中,"环境聆听"和"自动化文档生成"是两个关键支撑要素,前者确保项目能够适应不断变化的技术和业务环境,后者则保障项目过程的可追溯性和知…...
LegalOne:本土与国际视野融合的法律评级,大湾区律师及律师事务所榜单申报启动
在广东这片法律服务发展的热土上,从1979年全国首家法律服务机构诞生,到如今培育出4700家律所与8.7万律师,法律行业始终蓬勃向前。随着粤港澳大湾区建设推进,法律服务市场迈向双向融合的国际化新阶段,众多优秀律所和律师…...
突破传统!TTRL如何开启大模型无监督强化学习新篇章?
在大语言模型(LLMs)蓬勃发展的时代,如何让模型在无明确标签数据下有效学习成为关键难题。本文提出的Test-Time Reinforcement Learning(TTRL)给出了创新解法。它利用多数投票估计奖励,实现LLMs自我进化&…...
什么是:云边端一体化架构
什么是云边端一体化架构 文章目录 什么是云边端一体化架构云、边、端云计算边缘计算终端设备 云边端一体化协同云边端一体化架构协同的流程云边端一体化架构协同的应用云边端一体化架构协同的价值云边端一体化架构协同未来发展趋势 云、边、端 云(Cloud)…...
【2025域适应科研日报】
本笔记主要为了记录自己的科研日报,前段时间刚开始想写的初衷也是为了自己的思考不跑偏,但是有几天又没有坚持下来,看到一位学长的文章,发现这种形式还是很有必要的,所以自己也打算坚持记录下来,由于还正在…...
Linux从入门到精通:全面掌握基础命令与高效操作实战指南
引言 Linux 作为开发者、运维工程师及技术爱好者的核心工具,其命令行的高效性与灵活性无可替代。但对于新手而言,复杂的命令与文件结构往往令人困惑。本文基于官方文档与实践经验,系统梳理 Linux 基础命令、文件管理、目录操作、高级技巧 四大…...
如何提升个人的稳定性?
提升自我的稳定性是一个系统性工程,需要从内在认知、情绪管理、行为习惯到外在环境等多个维度进行优化。 以下是一些具体建议,帮助你逐步增强内心的稳定感: 一、内在认知调整 1. 建立清晰的自我认知 通过反思(如写日记、冥想…...
电机常用易混淆概念说明(伺服、舵机、多轮)
1. 概述 基础动力需求 :普通电机(如水泵、风扇)。 高精度控制 :优先伺服系统或伺服电机(如数控机床)。 微型化场景 :舵机(如遥控模型)。 移动底盘 :单舵轮成…...
短视频矩阵系统:源码搭建与定制化开发的深度剖析
在短视频行业蓬勃发展的当下,越来越多的企业和个人希望构建自己的短视频矩阵系统。而在搭建过程中,源码搭建和定制化开发是两种常见的选择,它们各有优劣,适用于不同的需求场景。本文将从多个维度深入探讨两者的区别,为…...
8.进程概念(四)
一、环境变量 1.基本概念 环境变量(environment variables)⼀般是指在操作系统中用来指定操作系统运行环境的⼀些参数。 如:我们在编写C/C代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成…...
Windows服务器提权实战:常见方法、场景与防御指南
在渗透测试中,权限提升(提权)是从低权限账户(如IIS、Apache运行账户)获取系统管理员(如SYSTEM)权限的关键步骤。本文将从实战角度解析Windows服务器提权的常见技术,并结合真…...
OpenGL-ES 学习(14) ----顶点指定和基本图元的绘制
目录 本节概述顶点的指定常量顶点属性和顶点数组顶点数组顶点属性的定义Shader 中声明顶点属性变量顶点属性的绑定 基本图元绘制基本图元三角形直线绘制图元的API 本节概述 绘制图形的第一步就是指定顶点坐标,可以每个顶点指定,也可以是用于所有顶点的常…...
spring-cloud-alibaba最新版本聚合项目创建
1. 创建聚合项目 修改 pom.xml spring-boot 当前最新版本是 3.4.5 但是 spring-cloud-alibaba 的最新版本是 2023.0.3.2,只适配到 spring-boot 3.2.4 还没有适配到 spring-boot 的 3.4.5 版本。 pom.xml 文件内容如下(可以直接复制): <?xml vers…...
网络分析/
三、网络分析(Network Analysis) 网络分析用于解决路径规划、资源分配等问题,广泛应用于交通规划、物流配送、紧急救援等领域。ArcPy 提供了强大的网络分析工具,如 MakeNetworkDataset、Solve 等。 (一)使用…...
Flutter PIP 插件 ---- 新增PipActivity,Android 11以下支持自动进入PIP Mode
接上文 Flutter PIP 插件 ---- Android 项目地址 PIP, pub.dev也已经同步发布 pip 0.0.3,你的加星和点赞,将是我继续改进最大的动力 开发文档 Add videos using picture-in-picture (PiP)介绍PIP功能从 Android 8.0 (API level 26) 引入&…...
权限提升—Linux提权内核溢出漏洞辅助项目
前言 今天开启Linux提权的篇章,主要是讲一下Linux的内核漏洞提权,利用方式和Windows系统漏洞提权差不多,也是网上的项目扫一下,然后根据漏洞编号去找exp即可。 信息收集 首先要说一下Linux用户的权限划分。 系统用户ÿ…...
超稳定性理论
为了更好的理解后面如何利用超稳定性理论来设计MRACS,本篇先对超稳定性理论做一个介绍。 1、理论介绍 在超稳定性理论中,核心的系统结构如下: 其包含一个线性的前向回路 G ( s ) G(s) G(s)和一个非线性的反馈回路 φ ( v ) \varphi (v) φ…...
治理和管理的区别
治理(Governance)与管理(Management)是两个在组织和社会运行中经常被提及的概念,它们虽然在某些方面有相似之处,但在内涵、范围、主体和目标等方面存在显著的区别。以下是它们的主要区别: 一、…...
业务流程BPM能力框架体系及华为中兴流程变革案例P83(83页PPT)(文末有下载方式)
资料解读:《业务流程 BPM 能力框架体系及华为中兴流程变革案例》 详细资料请看本解读文章的最后内容。 该文档围绕业务流程管理(BPM)能力框架体系展开,先阐述其定义、驱动因素与能力框架,再详细介绍战略规划、流程治理…...
如何通过日志在本地调试LangChain编写的程序?
LangSmith可以记录LangChain程序对LLM的调用,但它需要登陆LangSmith网站才能看到。有什么办法在本地就能看到详细的信息,以方便调试LangChain编写的程序吗? 使用LangChain提供的set_debug(True) 在Python代码中只需要导入set_debug这个方法…...
UE实用地编插件Physical Layout Tool
免费插件 https://www.fab.com/zh-cn/listings/a7fb6fcf-596f-48e9-83cc-f584aea316b1 可以通过物理模拟批量放置物体 不用再一个个摆放了 装饰环境从未如此简单,您不必再考虑对齐物体。 物理地放置物体,移动它们,在移动或在地图上放置物体…...
传感器的精度,灵敏度等概念介绍
文章目录 🏔️ 海拔高度传感器的四个核心指标1. 🎯 **精度(Accuracy)——“测得的高度准不准”**2. ⚡ **灵敏度(Sensitivity)——“高度微小变化有没有反应”**3. 🔍 **分辨率(Reso…...
前端八股 CSS 1
盒子模型 进行布局时将所有元素表示为一个个盒子box padding margin border content content:盒子内容 待显示的文本和图像 padding:内边距,内容和border之间的空间,不能为负数,受bkc影响 border:边框,…...
Transformer架构的解耦重组现象
技术演进图谱与技术成熟度曲线 (一)架构创新范式迭代 1.1 Transformer架构的解耦重组现象 以2025年Opt模型为例,其通过引入强化学习微调模块实现了传统单层堆叠架构向"感知-推理分离"模式的转型。实验数据显示,该架构…...
【Android】四大组件
目录 1. Activity 2. Service 3. BroadcastReceiver 4. ContentProvider 四大组件各自承担着不同的职责,彼此之间协同工作,共同为用户提供一个流畅的APP体验。 1. Activity 负责展示用户界面,就像App的一个个“页面”,用户通…...
贪心算法精解(Java实现):从理论到实战
一、贪心算法概述 贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法策略。它通过局部最优选择来达到全局最优解,具有高效、简洁的特点。 核心特点: 局部最优选择:每一步都做出当前看…...
基于BERT类的MRPC语义相似度检测(从0到-1系列)
基于BERT类的MRPC语义相似度检测(从0到-1系列) 介绍 BERT(Bidirectional Encoder Representations from Transformers)是由Google开发的一种预训练模型,它是一种基于Transformer机制的深度双向模型,可以对…...
mysql-窗口函数一
目录 一、感受一下分组与窗口函数的区别 二、滑动窗口(子窗口)大小的确认 2.1 分组函数下order by使用 2.2 窗口子句 2.3 执行流程 三、函数使用 窗口函数需要mysql的版本大于等于8才行,可以先检查一下自己的mysql版本是多少 select ve…...
HashMap,高效 哈希
java HashMap 有独特的设计。 哈希表数组的每个位置是一个哈希桶,里面由链表或红黑树实现。(> 8 或 < 6 的变化时,避免频繁切换) 容量(capacity): 哈希表中桶(bucket…...
PyTorch入门------训练图像分类器
前言 1. 操作步骤 2. 数据集 一、公共部分 1.加载并归一化 CIFAR10 2.定义卷积神经网络 二、训练、保存模型参数部分 train_and_save.py 3.定义损失函数和优化器 4.训练网络(使用 CPU 或者 GPU) 5.保存训练好的模型参数 三、加载模型参数、模型推理部分 load_and_infer.py 6…...
DeepSeek V3 架构创新:大规模MoE与辅助损失移除
DeepSeek 团队推出的全新 DeepSeek V3 模型版本,相比之前的 V2 版本,V3 的参数量从两千多亿一跃攀升到 6710 亿,近乎实现了参数规模的三倍增长。如此宏大的模型规模并不只是简单地堆砌参数,而是建立在稀疏混合专家(Mixture-of-Experts,MoE)结构之上。得益于 MoE 的稀疏激…...
MCP 多工具协作链路设计:打造真正的智能工作流
目录 [TOC] 🚀 MCP 多工具协作链路设计:打造真正的智能工作流 🌟 多工具协作链核心思想 🛠️ 设计示例:智能文档分析系统 📑 1. MCP Server 定义多工具 list_txt_files.py read_file_content.py su…...
某修改版软件,已突破限制!
聊一聊 现在很多输入法都带有广告。 用着用着,不是提示升级就是弹出资讯。 特别是忙的时候,很影响心情。 今天给大家分享一款干净的输入法软件。 希望能你喜欢。 软件介绍 Q拼音输入法 工具我们下载后,进行安装。 双击打开,…...