基于关节角度与肢体段长度的Python人体骨骼绘制方案咨询
高效绘制人体骨骼的方法(基于关节角度与肢体段长)
核心思路:正向运动学(FK)递推关节坐标
不用手动计算每个关节与X轴的夹角,从你设定的根节点(左髋,坐标原点(0,0))出发,通过正向运动学自动递推所有关节的绝对坐标:
- 以父关节为基准,根据肢体段长度和关节夹角,计算子关节相对于父关节的坐标偏移
- 累加偏移量得到子关节的绝对坐标
- 重复此过程遍历所有关节连接
步骤与Python实现
1. 明确关节映射与连接关系
先把关节编号对应到身体部位,定义父-子关节对(对应肢体段):
| 肢体段 | 父关节 | 子关节 | 长度(cm) |
|---|---|---|---|
| 髋间(hip) | 23(左髋) | 24(右髋) | 20.4 |
| 躯干(trunk) | 24(右髋) | 12(躯干上端) | 49.3 |
| 肩宽(shoulders) | 12(躯干上端) | 11(左肩) | 17.0(肩宽总长度34,左右各半) |
| 肩宽(shoulders) | 12(躯干上端) | 14(右肩) | 17.0 |
| 上臂(upper arm) | 11(左肩) | 13(左肘) | 29.41 |
| 前臂(forearm) | 13(左肘) | 15(左手) | 27.2 |
| 上臂(upper arm) | 14(右肩) | 16(右肘) | 29.41 |
| 前臂(forearm) | 16(右肘) | (对应手部节点) | 27.2 |
| 大腿(upper leg) | 23(左髋) | 25(左膝) | 42.33 |
| 小腿(lower leg) | 25(左膝) | 27(左脚) | 42.5 |
| 大腿(upper leg) | 24(右髋) | 26(右膝) | 42.33 |
| 小腿(lower leg) | 26(右膝) | 28(右脚) | 42.5 |
2. 代码实现(自动计算坐标+绘图)
使用numpy处理角度计算,matplotlib完成绘图:
import numpy as np import matplotlib.pyplot as plt # 输入你的数据 joint_angles = { (12, 14, 16): 168.54785591626595, (14, 12, 24): 172.77150681015084, (24, 12, 11): 54.37272304423055, (12, 11, 23): 117.01615512185863, (11, 23, 24): 108.6334963772736, (23, 11, 13): 45.915788777620314, (11, 13, 15): 177.83453548133326, (23, 24, 12): 79.97762545663724, (26, 24, 23): 163.4421588161629, (24, 23, 25): 145.8882059820918, (28, 26, 24): 103.70318526501839, (23, 25, 27): 178.10520631231438 } segment_lengths = { 'hip': 20.4, 'trunk': 49.3, 'shoulders': 34.0, 'forearm': 27.2, 'upper arm': 29.409999999999997, 'upper leg': 42.33, 'lower leg': 42.5 } # 初始化关节坐标,左髋(23)为原点 joint_coords = {23: np.array([0.0, 0.0])} # 1. 计算右髋(24):左髋到右髋沿X轴正方向 joint_coords[24] = joint_coords[23] + np.array([segment_lengths['hip'], 0.0]) # 2. 计算躯干上端(12):从右髋(24)出发 angle_trunk = np.deg2rad(180 - joint_angles[(23,24,12)]) dx_trunk = segment_lengths['trunk'] * np.cos(angle_trunk) dy_trunk = segment_lengths['trunk'] * np.sin(angle_trunk) joint_coords[12] = joint_coords[24] + np.array([dx_trunk, dy_trunk]) # 3. 计算左肩(11):从躯干上端(12)出发 vec_12_24 = joint_coords[24] - joint_coords[12] angle_12_24 = np.arctan2(vec_12_24[1], vec_12_24[0]) angle_12_11 = angle_12_24 + np.deg2rad(joint_angles[(24,12,11)]) dx_shoulder_left = 17.0 * np.cos(angle_12_11) dy_shoulder_left = 17.0 * np.sin(angle_12_11) joint_coords[11] = joint_coords[12] + np.array([dx_shoulder_left, dy_shoulder_left]) # 4. 计算右肩(14):从躯干上端(12)出发 angle_12_14 = angle_12_24 - np.deg2rad(180 - joint_angles[(14,12,24)]) dx_shoulder_right = 17.0 * np.cos(angle_12_14) dy_shoulder_right = 17.0 * np.sin(angle_12_14) joint_coords[14] = joint_coords[12] + np.array([dx_shoulder_right, dy_shoulder_right]) # 5. 计算左肘(13):从左肩(11)出发 vec_11_23 = joint_coords[23] - joint_coords[11] angle_11_23 = np.arctan2(vec_11_23[1], vec_11_23[0]) angle_11_13 = angle_11_23 + np.deg2rad(joint_angles[(23,11,13)]) dx_upper_arm_left = segment_lengths['upper arm'] * np.cos(angle_11_13) dy_upper_arm_left = segment_lengths['upper arm'] * np.sin(angle_11_13) joint_coords[13] = joint_coords[11] + np.array([dx_upper_arm_left, dy_upper_arm_left]) # 6. 计算左手(15):从左肘(13)出发 vec_13_11 = joint_coords[11] - joint_coords[13] angle_13_11 = np.arctan2(vec_13_11[1], vec_13_11[0]) angle_13_15 = angle_13_11 + np.deg2rad(180 - joint_angles[(11,13,15)]) dx_forearm_left = segment_lengths['forearm'] * np.cos(angle_13_15) dy_forearm_left = segment_lengths['forearm'] * np.sin(angle_13_15) joint_coords[15] = joint_coords[13] + np.array([dx_forearm_left, dy_forearm_left]) # 7. 计算左膝(25):从左髋(23)出发 vec_23_24 = joint_coords[24] - joint_coords[23] angle_23_24 = np.arctan2(vec_23_24[1], vec_23_24[0]) angle_23_25 = angle_23_24 + np.deg2rad(joint_angles[(24,23,25)]) dx_upper_leg_left = segment_lengths['upper leg'] * np.cos(angle_23_25) dy_upper_leg_left = segment_lengths['upper leg'] * np.sin(angle_23_25) joint_coords[25] = joint_coords[23] + np.array([dx_upper_leg_left, dy_upper_leg_left]) # 8. 计算左脚(27):从左膝(25)出发 vec_25_23 = joint_coords[23] - joint_coords[25] angle_25_23 = np.arctan2(vec_25_23[1], vec_25_23[0]) angle_25_27 = angle_25_23 + np.deg2rad(180 - joint_angles[(23,25,27)]) dx_lower_leg_left = segment_lengths['lower leg'] * np.cos(angle_25_27) dy_lower_leg_left = segment_lengths['lower leg'] * np.sin(angle_25_27) joint_coords[27] = joint_coords[25] + np.array([dx_lower_leg_left, dy_lower_leg_left]) # 9. 计算右膝(26):从右髋(24)出发 vec_24_23 = joint_coords[23] - joint_coords[24] angle_24_23 = np.arctan2(vec_24_23[1], vec_24_23[0]) angle_24_26 = angle_24_23 + np.deg2rad(180 - joint_angles[(26,24,23)]) dx_upper_leg_right = segment_lengths['upper leg'] * np.cos(angle_24_26) dy_upper_leg_right = segment_lengths['upper leg'] * np.sin(angle_24_26) joint_coords[26] = joint_coords[24] + np.array([dx_upper_leg_right, dy_upper_leg_right]) # 10. 计算右脚(28):从右膝(26)出发 vec_26_24 = joint_coords[24] - joint_coords[26] angle_26_24 = np.arctan2(vec_26_24[1], vec_26_24[0]) angle_26_28 = angle_26_24 + np.deg2rad(joint_angles[(28,26,24)]) dx_lower_leg_right = segment_lengths['lower leg'] * np.cos(angle_26_28) dy_lower_leg_right = segment_lengths['lower leg'] * np.sin(angle_26_28) joint_coords[28] = joint_coords[26] + np.array([dx_lower_leg_right, dy_lower_leg_right]) # 补充右肘(16)坐标 vec_14_12 = joint_coords[12] - joint_coords[14] angle_14_12 = np.arctan2(vec_14_12[1], vec_14_12[0]) angle_14_16 = angle_14_12 + np.deg2rad(180 - joint_angles[(12,14,16)]) joint_coords[16] = joint_coords[14] + np.array([segment_lengths['upper arm']*np.cos(angle_14_16), segment_lengths['upper arm']*np.sin(angle_14_16)]) # 绘制骨骼图 plt.figure(figsize=(8, 10)) # 定义骨骼连线 bones = [ (23,24), (24,12), (12,11), (12,14), (11,13), (13,15), (14,16), (23,25), (25,27), (24,26), (26,28) ] for bone in bones: x = [joint_coords[bone[0]][0], joint_coords[bone[1]][0]] y = [joint_coords[bone[0]][1], joint_coords[bone[1]][1]] plt.plot(x, y, 'b-', linewidth=3) # 绘制关节点 for j_id, coord in joint_coords.items(): plt.scatter(coord[0], coord[1], c='red', s=50, zorder=5) plt.text(coord[0]+1, coord[1]+1, str(j_id), fontsize=10) plt.gca().set_aspect('equal', adjustable='box') plt.title('人体骨骼可视化') plt.xlabel('X坐标(cm)') plt.ylabel('Y坐标(cm)') plt.grid(True) plt.show()
3. 优化建议
- 把关节坐标计算逻辑封装成通用函数,比如
get_child_coord(parent_coord, ref_angle, joint_angle, seg_len),避免重复代码 - 如果需要3D可视化,可扩展坐标到三维,使用
matplotlib的3D模块,或PyVista、Open3D库 - 若处理时序角度数据,可循环计算每一帧坐标,实现骨骼动画
替代工具
- OpenCV:适合实时骨骼绘制,可对接视频流应用
- Blender Python API:生成高精度3D骨骼模型,支持渲染导出
- Maya Python API:专业3D动画流程,适合复杂骨骼绑定需求
内容的提问来源于stack exchange,提问作者Anónimo salvaje
相关产品推荐
相关产品推荐

