尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
python的机器人基础原理工业场景模拟第十九篇:机械臂仿真,零重力工况模拟,关闭重力项,输出仅由运动产生的关节力矩曲线。
《Python机器人基础原理》第十九篇机械臂仿真——零重力工况模拟关闭重力项输出仅由运动产生的关节力矩曲线1. 实际工业应用场景周一早晨某航天部件装配车间的地面测试大厅。一台3-DOF垂直关节机械臂基座偏航肘部俯仰末端旋转正在进行卫星天线展开机构的微重力装配模拟。机器人构型大臂长600 mm小臂长450 mm末端旋转轴位于小臂末端。连杆质量大臂5.2 kg质心距基座0.28 m小臂3.1 kg质心距肘部0.20 m末端执行器1.5 kg质心距肘部0.45 m距末端旋转轴线横向偏置0.02 m。关节额定扭矩基座偏航轴60 N·m肘部俯仰轴180 N·m末端旋转轴30 N·m。调试工程师小李盯着屏幕上的力矩曲线眉头紧锁。师傅这个Joint1的力矩曲线怎么有个周期性的小波动幅度大概±3 N·m小李指着示波器上基座偏航轴的力矩轨迹我们明明在地面模拟微重力工况把重力补偿关了。旁边的老周放下咖啡杯你关的是哪部分求解器里gravity_enabledFalseg直接设成0。前馈里也把重力项摘了。那波动不是重力。是惯性耦合。基座偏航轴虽然不扛重力但肘部加速的时候惯性会通过M₁₂传给Joint1。你算过纯运动产生的力矩吗不考虑重力只算M(q)q¨ C(q,q˙)q˙。还没。我直接看控制器的前馈输出里面混着重力项。我想单独把重力拿掉看纯运动力矩长什么样。对。你把动力学方程里的G(q)整个摘掉只留M和C。给定一段轨迹的(q, q˙, q¨)算出来M·q¨ C·q˙。在零重力下Joint1的力矩应该完全来自M₁₁自惯性和M₁₂耦合——你粗算一下M₁₂峰值约0.78 kg·m²五次多项式加速度峰值约0.50 rad/s²耦合贡献≈0.39 N·m加上M₁₁自项≈1.4 N·mJoint1总力矩应该在1~2 N·m量级。你看到的±3 N·m差不太多说明耦合确实存在。小李打开编辑器那我写个离线仿真零重力模式跑一段θ₁和θ₂同时反向运动的轨迹——让M₁₂耦合和科氏项的特征都出来。科氏项τ_C ∝ sinθ₂θ₂从0°→-60°的过程中sinθ₂先增后减图里应该能看到这个变化。好。末端旋转轴我就不大幅动了质点模型下它几乎无力矩正好验证轻载。本篇解决的问题在动力学求解器中关闭重力项gravity_enabledFalse, g0给定关节状态序列仅计算惯性项M(q)q¨和科氏/离心项C(q,q˙)q˙产生的关节力矩输出纯运动力矩曲线。用于微重力模拟、太空机械臂地面测试、重力补偿算法验证。⚠️ 声明本篇全部代码为课程离线仿真运行在普通PC上用于零重力工况下纯运动关节力矩的计算与可视化。不可直接用于真实机器人控制器、伺服驱动器或航天实验设备。真实航天任务需结合在轨参数辨识、零重力悬浮平台校准。代码作者不对直接使用本代码于真实设备造成的任何后果负责。2. 痛点分析课程模块 本篇对应的工程痛点第4章 机器人动力学建模 → 4.4 关节空间动力学方程 τ M(q)q¨ C(q,q˙)q˙ G(q) 零重力工况下G(q)≡0需验证纯运动力矩惯性科氏的计算正确性第5章 机器人关节驱动与控制 → 5.3 前馈补偿 微重力模拟时前馈仅需M·q¨des C·q˙des重力补偿模块必须关闭第6章 轨迹规划与控制 → 6.2 轨迹评价 评估无重力环境下轨迹的纯动力学可行性第9章 协作机器人 → 零力拖动示教 零重力模式下去除重力项后的力矩曲线是拖动示教阻抗控制的基础本篇代码解决 L2 离线动力学校核层。输入关节状态数组(q, q˙, q¨) 机器人参数 重力开关标志。输出各关节的τ_M、τ_C、τ_total无τ_G。代码不是设备实时控制器不做伺服闭环不做轨迹规划。它做的事情是在零重力假设下给定状态直接算纯运动力矩。不覆盖轨迹生成、实时控制、摩擦力建模、6轴完整动力学、零重力悬浮平台硬件接口。3. 核心逻辑讲解物理直觉你有没有在游泳池里推过一个大泡沫板水里有浮力抵消了重力你几乎感觉不到重量——但你要让泡沫板加速还是要用力。这就是惯性。你要让它转弯还是要用力。这就是科氏力。重力没了但运动本身的懒惰性还在。太空里的机械臂就是这样。国际空间站上的机械臂在微重力环境下重力几乎为零。但加速移动太阳能板的时候关节电机依然要出力——出的是惯性力和科氏力的力矩。本篇就是把重力项从动力学方程里整个拿掉只看纯运动产生的力矩。业务逻辑 → 代码映射输入机器人参数DH、质量、质心 关节状态序列(q, q˙, q¨) gravity_enabledFalse↓步骤1对每一组状态计算惯性矩阵 M(q)↓步骤2计算科氏力/离心力向量 C(q,q˙)·q˙↓步骤3重力项 → gravity_enabledFalse时G(q)返回全零向量↓步骤4τ_total M·q¨ C·q˙ 0↓步骤5汇总所有状态点的力矩统计峰值与硬件限位对比↓步骤6输出4张图表关键公式文字翻译惯性项 τ_M M(q)·q¨M是3×3矩阵。零重力下这是力矩的主要来源。M₁₁≈1.9 kg·m²基座自惯性M₁₂≈0.78 kg·m²耦合项来自m₂L₁com₂m₃L₁com₃M₂₂≈0.66 kg·m²肘部自惯性。末端旋转轴的I₃ m₃·r_offset²r_offset是质心到旋转轴线的横向距离本篇取0.02 mI₃≈0.0006 kg·m²——量级极小几乎无力矩。科氏力项 τ_C C(q,q˙)·q˙τ_C1 -h·sinθ₂·(2θ̇₁θ̇₂θ̇₂²)τ_C2 h·sinθ₂·θ̇₁²。h m₂L₁com₂ m₃L₁com₃ ≈ 0.78 kg·m²。科氏力矩与sinθ₂成正比——θ₂接近0°时科氏项接近零θ₂到-60°时sin(-60°)≈-0.866达到最大。重力项 τ_G 0本篇核心设定。g0或G(q)强制返回零。验证输出力矩中不含任何与姿态相关的常数项。单位力矩 N·m角度 rad角速度 rad/s角加速度 rad/s²。4. OOP代码实现技术栈严格限定numpy, scipy, matplotlib。不引入pybullet、roboticstoolbox等任何第三方机器人库。项目结构树zero_g_torque_sim/├── run_zero_g_sim.py├── requirements.txt├── dynamics/│ ├── __init__.py│ ├── models.py│ ├── trajectory.py│ ├── dynamics_solver.py│ └── visualize.py├── tests/│ ├── __init__.py│ └── test_zero_g_sim.py└── results/核心源码dynamics/models.py — dataclass数据模型3-DOF垂直关节机械臂数据模型零重力仿真专用支持重力开关构型说明基座偏航(θ1, 竖直轴) 肘部俯仰(θ2, 水平轴) 末端旋转(θ3)物理声明- θ1绕竖直轴旋转重力对其力矩恒为零无论gravity_enabled为何值- θ2为水平俯仰轴重力矩 -g·(m₂com₂m₃com₃)·cos(θ₂)注意是cos(θ₂)不是cos(θ₁θ₂)- θ3为末端旋转轴质点模型下重力矩为零- 零重力模式gravity_enabledFalse → G(q)返回全零- 末端旋转惯量I₃ m₃·r_offset²r_offset为质心到旋转轴线的横向距离质心坐标定义全局统一- com₁: 大臂质心沿连杆方向距基座关节的距离 (m)- com₂: 小臂质心沿连杆方向距肘部关节的距离 (m)- com₃: 末端负载质心沿小臂方向距肘部关节的距离 (m)- r_offset₃: 末端负载质心到末端旋转轴线的横向距离 (m)from dataclasses import dataclass, fieldfrom typing import List, Tuple, Optionalimport numpy as npdataclassclass JointLimit:单关节硬件限位min_val: floatmax_val: floatjoint_name: str torque_limit: float 0.0def __post_init__(self):if self.min_val self.max_val:raise ValueError(fJointLimit: min_val({self.min_val})必须严格小于fmax_val({self.max_val}))if not np.isfinite(self.min_val) or not np.isfinite(self.max_val):raise ValueError(JointLimit: 限位值必须为有限数值)def contains(self, value: float) - bool:return (self.min_val - 1e-10) value (self.max_val 1e-10)dataclassclass JointLimitSet:3-DOF机械臂关节限位集合limits: List[JointLimit]def __post_init__(self):if len(self.limits) ! 3:raise ValueError(f需要3个关节限位, 实际{len(self.limits)}个)classmethoddef default_3dof_arm(cls):默认3-DOF机械臂限位θ1: ±170° (基座偏航, 额定60 N·m)θ2: ±140° (肘部俯仰, 额定180 N·m)θ3: ±180° (末端旋转, 额定30 N·m)pi 3.141592653589793return cls(limits[JointLimit(-170 * pi / 180, 170 * pi / 180, joint1_base_yaw, 60.0),JointLimit(-140 * pi / 180, 140 * pi / 180, joint2_elbow_pitch, 180.0),JointLimit(-180 * pi / 180, 180 * pi / 180, joint3_end_rotate, 30.0),])def check_all_with_indices(self, joint_values: List[float]) - Tuple[bool, List[int]]:if len(joint_values) ! 3:raise ValueError(f需要3个关节值, 实际{len(joint_values)}个)all_pass Trueviolation_indices []for i, (val, limit) in enumerate(zip(joint_values, self.limits)):if not limit.contains(val):all_pass Falseviolation_indices.append(i)return all_pass, violation_indicesdef get_torque_limits(self) - List[float]:return [lim.torque_limit for lim in self.limits]dataclassclass LinkMassProps:连杆质量与质心属性mass: 质量 (kg)com_x: 质心沿连杆方向距近端关节的距离 (m)com_y, com_z: 垂直于连杆方向的质心偏移 (m)r_offset_end: 末端负载质心到末端旋转轴线的横向距离 (m)用于I₃计算mass: floatcom_x: floatcom_y: floatcom_z: floatlink_name: str max_com_x: Optional[float] Noner_offset_end: float 0.0 # 末端质心到旋转轴线的横向偏置def __post_init__(self):if self.mass 0:raise ValueError(fLinkMassProps: 质量不能为负, 实际{self.mass})for name, val in [(com_x, self.com_x), (com_y, self.com_y),(com_z, self.com_z), (r_offset_end, self.r_offset_end)]:if not np.isfinite(val):raise ValueError(fLinkMassProps.{name}必须为有限数值)if self.max_com_x is not None and self.com_x self.max_com_x:raise ValueError(fLinkMassProps({self.link_name}): 质心x{self.com_x}m f超出连杆长度上限{self.max_com_x}m)dataclassclass MassPropertySet:完整连杆质量属性集合link_props: List[LinkMassProps]link_lengths: List[float] field(default_factorylambda: [0.0, 0.0, 0.0])def __post_init__(self):if len(self.link_props) ! 3:raise ValueError(f需要3个连杆质量属性, 实际{len(self.link_props)}个)for i, (prop, length) in enumerate(zip(self.link_props, self.link_lengths)):if length 0 and prop.com_x length:raise ValueError(f连杆{i}({prop.link_name}): 质心{prop.com_x}m超出长度{length}m)classmethoddef default_3dof_arm(cls):3-DOF机械臂质量参数大臂: 5.2 kg, 质心距基座0.28 m (L10.60m)小臂: 3.1 kg, 质心距肘部0.20 m (L20.45m)末端: 执行器1.5 kg, 质心距肘部0.45 m, 横向偏置0.02 mlink_lengths [0.60, 0.45, 0.0]props [LinkMassProps(5.2, 0.28, 0.0, 0.0, link1_upper_arm, link_lengths[0]),LinkMassProps(3.1, 0.20, 0.0, 0.0, link2_forearm, link_lengths[1]),LinkMassProps(1.5, 0.45, 0.0, 0.0, link3_end_effector, None, 0.02),]return cls(link_propsprops, link_lengthslink_lengths)def copy_with_modified_com(self, link_index: int, new_com_x: float):返回修改后的质心属性副本new_props []for i, prop in enumerate(self.link_props):if i link_index:new_props.append(LinkMassProps(prop.mass, new_com_x, prop.com_y, prop.com_z,prop.link_name, prop.max_com_x, prop.r_offset_end))else:new_props.append(LinkMassProps(prop.mass, prop.com_x, prop.com_y, prop.com_z,prop.link_name, prop.max_com_x, prop.r_offset_end))return MassPropertySet(link_propsnew_props, link_lengthsself.link_lengths)def total_mass(self) - float:return sum(prop.mass for prop in self.link_props)dataclassclass DHParam:标准D-H参数theta: floatd: floata: floatalpha: floatdef __post_init__(self):for name, val in [(theta, self.theta), (d, self.d),(a, self.a), (alpha, self.alpha)]:if not np.isfinite(val):raise ValueError(fDHParam.{name}必须为有限数值)dataclassclass RobotParams:3-DOF机械臂完整参数大臂600mm, 小臂450mmgravity_enabled: 重力项计算开关。False时G(q)返回全零。g: 重力加速度值。gravity_enabledFalse时此值被忽略内部强制为0。links: List[DHParam]joint_names: List[str] field(default_factorylambda: [joint1_base_yaw, joint2_elbow_pitch, joint3_end_rotate])g: float 9.81gravity_enabled: bool Trueclassmethoddef default_3dof_arm(cls):links [DHParam(0.0, 0.0, 0.600, 0.0),DHParam(0.0, 0.0, 0.450, 0.0),DHParam(0.0, 0.0, 0.0, 0.0),]return cls(linkslinks)def zero_gravity(self):关闭重力返回新的RobotParams副本gravity_enabledFalse, g0new_links [DHParam(l.theta, l.d, l.a, l.alpha) for l in self.links]return RobotParams(linksnew_links, joint_namesself.joint_names,g0.0, gravity_enabledFalse)dynamics/trajectory.py — 关节状态序列生成器关节状态序列生成器生成平滑轨迹状态用于零重力力矩计算使用五次多项式插值生成连续的位置、速度、加速度轨迹设计θ₁和θ₂同时反向运动最大化M₁₂耦合和科氏项特征import numpy as npfrom dynamics.models import JointLimitSetclass JointStateSequence:关节状态序列存储多组(q, q˙, q¨)用于批量力矩计算def __init__(self, times, q_array, q_dot_array, q_ddot_array,limits: JointLimitSet None):self.times np.array(times, dtypefloat)self.q_array np.array(q_array, dtypefloat)self.q_dot_array np.array(q_dot_array, dtypefloat)self.q_ddot_array np.array(q_ddot_array, dtypefloat)self.limits limitsn len(times)if self.q_array.shape ! (n, 3):raise ValueError(fq_array形状应为({n}, 3), 实际{self.q_array.shape})if self.q_dot_array.shape ! (n, 3):raise ValueError(fq_dot_array形状应为({n}, 3))if self.q_ddot_array.shape ! (n, 3):raise ValueError(fq_ddot_array形状应为({n}, 3))# 有限值检查for name, arr in [(q_array, self.q_array),(q_dot_array, self.q_dot_array),(q_ddot_array, self.q_ddot_array)]:if not np.all(np.isfinite(arr)):raise ValueError(f{name}包含非有限值(NaN或Inf))# 限位校验——拼接所有超限关节的错误信息if limits is not None:for i, t in enumerate(times):rot_angles [self.q_array[i, 0], self.q_array[i, 1], self.q_array[i, 2]]all_pass, violations limits.check_all_with_indices(rot_angles)if not all_pass:err_msgs []for vi in violations:err_msgs.append(f时刻t{t:.3f}s: 关节{limits.limits[vi].joint_name} f值{rot_angles[vi]:.3f} 超出限位 f[{limits.limits[vi].min_val:.3f}, f{limits.limits[vi].max_val:.3f}])raise ValueError(.join(err_msgs))classmethoddef smooth_trajectory(cls, n_points100, duration2.0, limits: JointLimitSet None):生成平滑轨迹状态序列θ1: 0° → 90°正向θ2: 0° → -60°反向最大化科氏项sinθ₂变化θ3: 0° → 10°微小运动验证末端旋转轴轻载使用五次多项式插值确保位置、速度、加速度连续times np.linspace(0, duration, n_points)pi 3.141592653589793q1_start, q1_end 0.0, 90 * pi / 180q2_start, q2_end 0.0, -60 * pi / 180q3_start, q3_end 0.0, 10 * pi / 180q_array np.zeros((n_points, 3))q_dot_array np.zeros((n_points, 3))q_ddot_array np.zeros((n_points, 3))for i, t in enumerate(times):if duration 0:s_norm t / durationelse:s_norm 0.0# 五次多项式s 10t³ - 15t⁴ 6t⁵s 10*s_norm**3 - 15*s_norm**4 6*s_norm**5s_dot 30*s_norm**2 - 60*s_norm**3 30*s_norm**4s_ddot 60*s_norm - 180*s_norm**2 120*s_norm**3# 位置q_array[i, 0] q1_start s * (q1_end - q1_start)q_array[i, 1] q2_start s * (q2_end - q2_start)q_array[i, 2] q3_start s * (q3_end - q3_start)# 速度if duration 0:q_dot_array[i, 0] s_dot * (q1_end - q1_start) / durationq_dot_array[i, 1] s_dot * (q2_end - q2_start) / durationq_dot_array[i, 2] s_dot * (q3_end - q3_start) / duration# 加速度q_ddot_array[i, 0] s_ddot * (q1_end - q1_start) / (duration**2)q_ddot_array[i, 1] s_ddot * (q2_end - q2_start) / (duration**2)q_ddot_array[i, 2] s_ddot * (q3_end - q3_start) / (duration**2)return cls(times, q_array, q_dot_array, q_ddot_array, limits)def __len__(self):return len(self.times)dynamics/dynamics_solver.py — 动力学求解器核心业务类动力学求解器零重力工况下仅计算惯性项科氏项产生的关节力矩τ M(q)q¨ C(q,q˙)q˙ [G(q) if gravity_enabled else 0]3-DOF机械臂基座偏航(θ1) 肘部俯仰(θ2) 末端旋转(θ3)零重力模式gravity_enabledFalse → G(q)返回全零重力公式修正第十八篇遗留问题彻底了结- θ1为竖直偏航轴 → τ_G1恒为0无论gravity_enabled为何值- θ2为水平俯仰轴 → τ_G2 -g·(m₂·com₂ m₃·com₃)·cos(θ₂)注意是cos(θ₂)不是cos(θ₁θ₂)θ₁绕竖直轴转不改变俯仰角- θ3为末端旋转轴 → τ_G3 0末端旋转惯量修正- I₃ m₃·r_offset²r_offset为质心到旋转轴线的横向距离- 不再错误使用com₃沿臂方向距肘部距离作为回转半径注意每次修改质量/质心参数后必须创建新的DynamicsSolver实例import numpy as npfrom dynamics.models import RobotParams, MassPropertySetclass DynamicsSolver:基于拉格朗日方程的3-DOF机械臂动力学求解器支持零重力模式关闭重力项classmethoddef from_mass_props(cls, params: RobotParams, mass_props: MassPropertySet):工厂方法return cls(params, mass_props)def __init__(self, params: RobotParams, mass_props: MassPropertySet):self.params paramsself.mass_props mass_propsself.gravity_enabled params.gravity_enabled# g值仅在gravity_enabledTrue时使用False时内部强制为0self.g params.g if self.gravity_enabled else 0.0self.L1 params.links[0].aself.L2 params.links[1].aself.m1 mass_props.link_props[0].massself.m2 mass_props.link_props[1].massself.m3 mass_props.link_props[2].massself.com1 mass_props.link_props[0].com_xself.com2 mass_props.link_props[1].com_xself.com3 mass_props.link_props[2].com_xself.r_offset3 mass_props.link_props[2].r_offset_end# 科氏力耦合系数 h m₂·L₁·com₂ m₃·L₁·com₃self.h self.m2 * self.L1 * self.com2 self.m3 * self.L1 * self.com3# 末端旋转惯量I₃ m₃·r_offset²# r_offset是质心到末端旋转轴线的横向距离不是com₃self.I3 self.m3 * (self.r_offset3 ** 2)def inertia_matrix(self, theta2):计算3×3惯性矩阵 M(q)c2 np.cos(theta2)M11 (self.m1 * self.com1**2 self.m2 * (self.L1**2 self.com2**2 2*self.L1*self.com2*c2) self.m3 * (self.L1**2 self.com3**2 2*self.L1*self.com3*c2))M12 (self.m2 * (self.com2**2 self.L1*self.com2*c2) self.m3 * (self.com3**2 self.L1*self.com3*c2))M22 self.m2 * self.com2**2 self.m3 * self.com3**2M np.zeros((3, 3))M[0, 0] M11M[0, 1] M12M[1, 0] M12M[1, 1] M22M[2, 2] self.I3 # 末端旋转惯量使用横向偏置半径return Mdef coriolis_vector(self, theta2, theta1_dot, theta2_dot, theta3_dot):计算科氏力/离心力向量3维质点模型下θ3无速度耦合τ_C30s2 np.sin(theta2)tau_C1 -self.h * s2 * (2 * theta1_dot * theta2_dot theta2_dot**2)tau_C2 self.h * s2 * theta1_dot**2tau_C3 0.0 # 质点模型下末端旋转无科氏耦合return np.array([tau_C1, tau_C2, tau_C3], dtypefloat)def gravity_vector(self, theta1, theta2):计算重力项向量零重力模式返回全零正常模式修正后τ_G1 0竖直偏航轴恒为零τ_G2 -g·(m₂·com₂ m₃·com₃)·cos(θ₂) ← 修正cos(θ₂)不是cos(θ₁θ₂)τ_G3 0末端旋转轴if not self.gravity_enabled:return np.zeros(3, dtypefloat)# 正常重力模式修正后公式tau_G1 0.0 # 基座偏航轴竖直轴重力力矩恒为零# θ2为水平俯仰轴重力矩取决于θ₂不是θ₁θ₂tau_G2 -self.g * (self.m2 * self.com2 self.m3 * self.com3) * np.cos(theta2)tau_G3 0.0 # 末端旋转轴return np.array([tau_G1, tau_G2, tau_G3], dtypefloat)def compute_all_terms(self, q, q_dot, q_ddot):计算完整动力学方程的各项分解if len(q) 3 or len(q_dot) 3 or len(q_ddot) 3:raise ValueError(需要3个关节的角度、速度、加速度)theta1 q[0]theta2 q[1]theta3 q[2]theta1_dot q_dot[0]theta2_dot q_dot[1]theta3_dot q_dot[2]theta1_ddot q_ddot[0]theta2_ddot q_ddot[1]theta3_ddot q_ddot[2]M self.inertia_matrix(theta2)tau_M M np.array([theta1_ddot, theta2_ddot, theta3_ddot])tau_C self.coriolis_vector(theta2, theta1_dot, theta2_dot, theta3_dot)tau_G self.gravity_vector(theta1, theta2)tau_total tau_M tau_C tau_Greturn tau_M, tau_C, tau_G, tau_totaldef compute_sequence(self, state_sequence):对状态序列批量计算力矩n len(state_sequence)tau_M_hist np.zeros((n, 3))tau_C_hist np.zeros((n, 3))tau_G_hist np.zeros((n, 3))tau_total_hist np.zeros((n, 3))for i in range(n):tau_M, tau_C, tau_G, tau_total self.compute_all_terms(state_sequence.q_array[i],state_sequence.q_dot_array[i],state_sequence.q_ddot_array[i])tau_M_hist[i, :] tau_Mtau_C_hist[i, :] tau_Ctau_G_hist[i, :] tau_Gtau_total_hist[i, :] tau_totalpeaks {tau_M_peak: np.max(np.abs(tau_M_hist), axis0),tau_C_peak: np.max(np.abs(tau_C_hist), axis0),tau_G_peak: np.max(np.abs(tau_G_hist), axis0),tau_total_peak: np.max(np.abs(tau_total_hist), axis0),}return {peaks: peaks,tau_M_hist: tau_M_hist,tau_C_hist: tau_C_hist,tau_G_hist: tau_G_hist,tau_total_hist: tau_total_hist,}dynamics/visualize.py — 可视化模块零重力仿真结果可视化4张核心图表import numpy as npimport matplotlib.pyplot as pltplt.rcParams[font.sans-serif] [SimHei, DejaVu Sans]plt.rcParams[axes.unicode_minus] Falseclass Visualizer:def __init__(self, results_dirresults):self.results_dir results_dirimport osos.makedirs(results_dir, exist_okTrue)def plot_motion_only_torque(self, times, tau_M_hist, tau_C_hist, tau_total_hist):① 纯运动力矩曲线无重力项fig, axes plt.subplots(3, 1, figsize(10, 9), sharexTrue)jo利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
RELATED

相关推荐

TCP SACK乱序级别reordering:重传不高但吞吐上不去的真凶

TCP SACK乱序级别reordering:重传不高但吞吐上不去的真凶

先把结论放在前面:如果你在排查 TCP 性能问题时发现“重传不高、但吞吐上不去”的现象,而且抓包里有大量 SACK 块,那很可能就是把你今天看到的这个标题——SACK 报文乱序级别 reordering——给忽略了。它不像丢包那样一眼就能看出来&#xff…

📅 2026/10/14 15:39:25
# 多屏显示系统分辨率异常怎么办?EDID、刷新率与信号兼容性排查方法

# 多屏显示系统分辨率异常怎么办?EDID、刷新率与信号兼容性排查方法

多屏显示系统分辨率异常怎么办?EDID、刷新率与信号兼容性排查方法 导读 在会议室、指挥中心、展厅和多屏控制系统中,经常会遇到显示器无法识别目标分辨率、刷新率自动降低、切换信号后黑屏或重新连接才能恢复等问题。这些现象不一定由显示器故障引起&…

📅 2026/10/14 15:39:25
3DMAX随机碳纳米结构RandomCarbonNanostructure建模工具开发教程

3DMAX随机碳纳米结构RandomCarbonNanostructure建模工具开发教程

一、工具目标与整体思路这个脚本的目标是:在 3ds Max 中随机生成一个类似碳纳米结构的三维模型,包括原子球和原子之间的键柱,并允许用户通过 UI 调整原子数、键长、键角、半径、随机种子等参数。它并不是严格意义上的物理化学模拟&#xff0c…

📅 2026/10/14 15:39:25
MORE NEWS

更多资讯

📰

ASP.NET MVC工作流源码深度解析与生产避坑指南

简介:这是一套基于ASP.NET MVC5开发的完整工作流管理系统源码,面向OA办公系统、CRM客户关系管理及HR人事管理等企业级应用开发者,尤其适合希望深入理解流程引擎设计与MVC架构实践的中高级.NET学习者。资源包含可视化流程设计器与表单设计器两…

📰

从95%到8%:国产AI芯片如何翻越CUDA生态大山

1. 从95%到8%:一个数字背后的产业变局95%到8%,这两个数字放在一起,任何一个在AI硬件领域摸爬滚打过的人都会心头一紧。前者是某国际GPU巨头在特定高端加速卡市场的长期占有率,后者是国产AI芯片在同一个统计口径下被反复引用的份额…

📰

分布式存储系统设计:一致性哈希、副本放置与Raft实践

1. 需求与架构设计:先搞清楚要解决的到底是谁的问题做存储系统的人常被问一个外行问题:“分布式存储就是多台机器一起存东西,对吧?”对,但也不全对。它真正的难点不在于“多台机器一起存”,而在于“多台机器…

📰

STM32驱动BH1750光照传感器实战:I²C寄存器配置与读数为0的六大根源

简介:本资源是一套面向STM32嵌入式开发者的BH1750光照强度传感器驱动库,适用于初学者入门实践与项目快速集成。资源聚焦环境光检测场景,解决STM32与GY-30模块(含BH1750芯片)的IC通信、模式配置、Lux值读取及错误处理等…

📰

【C++】 priority_queue知识点总结

一、基础概念priority_queue 叫优先队列,底层不是队列,是堆(大顶堆 / 小顶堆),属于容器适配器。适配器:不自己实现底层存储,复用别的容器(vector/deque)做底层&#xff0…

📰

Intel HD630非DCH驱动26.20.100.6573稳定适配指南

简介:本资源为Intel HD Graphics 530(HD630)集成显卡专用的非DCH架构驱动程序,版本号26.20.100.6573,专为Windows 10 64位系统优化,适用于搭载第六代Skylake处理器的台式机与笔记本用户,解决传统…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬