どんな工作物でも,各パーツの重量と重心位置のバランスは大切です!
何を設計するに際しても,(例えばアームロボットなど)
工作物の重量を把握し,全体の重量バランスを検討する必要があります.
下図のように,Pythonでサクッと計算・可視化して確認できるといいですよね.
このプログラムでのポイントは,
1.最初に何個の質量の総和を求めたいのか,個数を指定できる
2.質量と3次元空間の座標値をダイアログボックスにて入力できる
3.3次元プロットで可視化
①個別の質量は,青丸印で表示
②トータルの質量は,赤丸印で表示
③質量に応じて丸印の大きさが異なる
④質量と重心位置の座標値をプロット内に表示
実行方法は,コマンドプロンプト(Macではターミナル)で以下を実行
python3 calc_total_cg.py
calc_total_cg.py
# coding: utf-8
import tkinter as tk
from tkinter import simpledialog, messagebox
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def get_mass_points_from_dialog():
"""
Accepts mass point information as input from dialog boxes.
Returns:
list: A list of [mass, x, y, z] for each mass point.
"""
mass_points = []
while True:
try:
# Ask the user for the number of mass points
num_points_str = simpledialog.askstring("Mass Point Input", "Enter the number of mass points (e.g., 3):")
if num_points_str is None: # If canceled
return None
num_points = int(num_points_str)
if num_points <= 0:
raise ValueError("The number of mass points must be a positive integer.")
break
except ValueError as e:
messagebox.showerror("Input Error", str(e))
except TypeError: # If NoneType (e.g., Cancel button pressed)
return None
for i in range(num_points):
while True:
try:
# Enter mass and coordinates for each mass point
input_str = simpledialog.askstring(f"Mass Point {i+1} Input",
f"Enter mass (kg) and coordinates (x, y, z) for mass point {i+1}, separated by commas.\nExample: 10, 1.0, 2.0, 3.0")
if input_str is None: # If canceled
return None
parts = [float(p.strip()) for p in input_str.split(',')]
if len(parts) != 4:
raise ValueError("Please enter 4 values: mass and 3 coordinates (x, y, z).")
mass, x, y, z = parts
if mass < 0:
raise ValueError("Mass cannot be negative.")
mass_points.append([mass, x, y, z])
break
except ValueError as e:
messagebox.showerror("Input Error", str(e))
except Exception as e:
messagebox.showerror("Error", f"An unexpected error occurred: {e}")
return mass_points
def calculate_center_of_gravity(mass_points):
"""
Calculates the center of gravity for multiple mass points.
Args:
mass_points (list): A list of [mass, x, y, z] for each mass point.
Returns:
tuple: (Total Mass, CoG_x, CoG_y, CoG_z)
or None (if no input).
"""
if not mass_points:
return None
total_mass = sum(mp[0] for mp in mass_points)
if total_mass == 0:
messagebox.showwarning("Calculation Warning", "Total mass is 0, so CoG cannot be calculated.")
return 0, 0, 0, 0 # If total mass is 0, return origin or handle as an error
sum_mx = sum(mp[0] * mp[1] for mp in mass_points)
sum_my = sum(mp[0] * mp[2] for mp in mass_points)
sum_mz = sum(mp[0] * mp[3] for mp in mass_points)
cog_x = sum_mx / total_mass
cog_y = sum_my / total_mass
cog_z = sum_mz / total_mass
return total_mass, cog_x, cog_y, cog_z
def plot_3d_points(mass_points, cog_info):
"""
Plots mass points and the center of gravity in 3D space.
Args:
mass_points (list): A list of [mass, x, y, z] for each mass point.
cog_info (tuple): (Total Mass, CoG_x, CoG_y, CoG_z).
"""
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# Calculate max individual mass for scaling reference
max_individual_mass = max(mp[0] for mp in mass_points) if mass_points else 1
# Plot each mass point
for i, mp in enumerate(mass_points):
mass, x, y, z = mp
# Scale blue marker size relative to its mass.
# Max marker size for individual points is about 200, min is 20.
# This makes sure smaller points are still visible.
marker_size = max(20, (mass / max_individual_mass) * 200)
ax.scatter(x, y, z, color='blue', s=marker_size, label=f'Mass Point {i+1}' if i == 0 else "", alpha=0.7)
ax.text(x, y, z, f'M: {mass}kg\n({x:.1f},{y:.1f},{z:.1f})', color='blue')
# Plot the total center of gravity
if cog_info:
total_mass, cog_x, cog_y, cog_z = cog_info
# Scale red marker size based on total mass, significantly larger than individual points.
# This uses the total mass relative to the max individual mass for a more intuitive scale.
# Minimum size 100, larger points will be scaled up from there.
cog_marker_size = max(100, (total_mass / max_individual_mass) * 300)
ax.scatter(cog_x, cog_y, cog_z, color='red', marker='o', s=cog_marker_size, label='Center of Gravity', edgecolor='black', linewidth=0.8)
ax.text(cog_x, cog_y, cog_z, f'Total Mass: {total_mass:.2f}kg\nCoG: ({cog_x:.2f},{cog_y:.2f},{cog_z:.2f})', color='red', fontsize=10, ha='right')
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_zlabel('Z-axis')
ax.set_title('Mass Points and Center of Gravity in 3D Space')
ax.legend()
ax.grid(True)
plt.show()
# Hide the Tkinter root window
root = tk.Tk()
root.withdraw()
# Get mass point data
mass_points_data = get_mass_points_from_dialog()
if mass_points_data:
# Calculate CoG
center_of_gravity_info = calculate_center_of_gravity(mass_points_data)
if center_of_gravity_info is not None:
# Plot the results
plot_3d_points(mass_points_data, center_of_gravity_info)
else:
messagebox.showinfo("Information", "No mass point data entered. Exiting process.")