venerdì 31 ottobre 2025

Caricamento progressivo di cloudpoints and meshes

In caso di nuvole di punti o mesh molto numerose puo' essere molto lento il caricamento di tutto il tema via interfaccia web ed esiste la possibilita' di fare caricamenti progressivi basati sulla distanza del punto di vista...in questo caso pero' i dati devono essere preparati in modo idoneo 

POINTCLOUD

git clone https://github.com/potree/PotreeConverter.git

cd PotreeConverter

mkdir build && cd build

cmake ..

make -j4

./PotreeConverter /path/to/input.ply -o /path/to/output/
(funziona anche con i Laz)


MESH

# Clone the main gltfpack repository

git clone https://github.com/zeux/meshoptimizer.git gltfpack_source

cd gltfpack_source


# Clone the required basis_universal fork (for KHR_texture_basisu support)

git clone -b gltfpack https://github.com/zeux/basis_universal basis_universal


# Clone libwebp (for WebP support)

git clone https://github.com/webmproject/libwebp libwebp

ABSOLUTE_PATH_ROOT=$(pwd)/..


cmake .. \

    -DMESHOPT_BUILD_GLTFPACK=ON \

    -DMESHOPT_GLTFPACK_BASISU_PATH="${ABSOLUTE_PATH_ROOT}/basis_universal" \

    -DMESHOPT_GLTFPACK_LIBWEBP_PATH="${ABSOLUTE_PATH_ROOT}/libwebp"


make

make install

gltfpack -i model.glb -o model_lods.glb -cc -tc -si 0.5 -si 0.25 -si 0.1



giovedì 30 ottobre 2025

Doppio Pendolo con PyBullet

 Alla fine, usando AI,sono arrivato ad avere una versione funzionante del pendolo doppio con PyBullet. In questo caso non si devono scrivere le equazioni di moto .... ci pensa il motore fisico una volta impostato il modello e le condizioni iniziali

Alla fine del post lo stesso pendolo doppio risolto pero' in modo analitico con le equazioni di moto del sistema 

 


 

 

 

 double_pendulum_2d.urdf

<?xml version="1.0"?>
<robot name="double_pendulum_2d">
<!-- Base link (fixed to world) -->
<link name="base">
<visual>
<geometry>
<sphere radius="0.05"/>
</geometry>
<material name="gray">
<color rgba="0.5 0.5 0.5 1"/>
</material>
</visual>
<collision>
<geometry>
<sphere radius="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="0.001"/>
<inertia ixx="0.00001" ixy="0" ixz="0" iyy="0.00001" iyz="0" izz="0.00001"/>
</inertial>
</link>
<!-- First rod -->
<link name="rod1">
<visual>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<geometry>
<box size="0.04 0.01 0.5"/>
</geometry>
<material name="red">
<color rgba="0.8 0.2 0.2 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<geometry>
<box size="0.04 0.01 0.5"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<mass value="0.1"/>
<inertia ixx="0.002" ixy="0" ixz="0" iyy="0.002" iyz="0" izz="0.00001"/>
</inertial>
</link>
<!-- First bob -->
<link name="bob1">
<visual>
<geometry>
<sphere radius="0.08"/>
</geometry>
<material name="bright_red">
<color rgba="1 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<sphere radius="0.08"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.00128" ixy="0" ixz="0" iyy="0.00128" iyz="0" izz="0.00128"/>
</inertial>
</link>
<!-- Second rod -->
<link name="rod2">
<visual>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<geometry>
<box size="0.04 0.01 0.5"/>
</geometry>
<material name="blue">
<color rgba="0.2 0.2 0.8 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<geometry>
<box size="0.04 0.01 0.5"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 -0.25" rpy="0 0 0"/>
<mass value="0.1"/>
<inertia ixx="0.002" ixy="0" ixz="0" iyy="0.002" iyz="0" izz="0.00001"/>
</inertial>
</link>
<!-- Second bob -->
<link name="bob2">
<visual>
<geometry>
<sphere radius="0.08"/>
</geometry>
<material name="bright_blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<sphere radius="0.08"/>
</geometry>
</collision>
<inertial>
<mass value="0.5"/>
<inertia ixx="0.00128" ixy="0" ixz="0" iyy="0.00128" iyz="0" izz="0.00128"/>
</inertial>
</link>
<!-- Joint 1: Base to Rod1 (revolute around Y-axis for XZ plane motion) -->
<joint name="joint1" type="continuous">
<parent link="base"/>
<child link="rod1"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<dynamics damping="0.000" friction="0.0"/>
</joint>
<!-- Fixed joint: Rod1 to Bob1 -->
<joint name="rod1_to_bob1" type="fixed">
<parent link="rod1"/>
<child link="bob1"/>
<origin xyz="0 0 -0.5" rpy="0 0 0"/>
</joint>
<!-- Joint 2: Bob1 to Rod2 (revolute around Y-axis for XZ plane motion) -->
<joint name="joint2" type="continuous">
<parent link="bob1"/>
<child link="rod2"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<dynamics damping="0.000" friction="0.0"/>
</joint>
<!-- Fixed joint: Rod2 to Bob2 -->
<joint name="rod2_to_bob2" type="fixed">
<parent link="rod2"/>
<child link="bob2"/>
<origin xyz="0 0 -0.5" rpy="0 0 0"/>
</joint>
</robot>


Pendolo2d.py

import pybullet as p
import pybullet_data
import numpy as np
import time
import os
import xml.etree.ElementTree as ET

def parse_urdf(urdf_path):
"""Parse URDF file and extract relevant parameters"""
tree = ET.parse(urdf_path)
root = tree.getroot()
params = {
'links': {},
'joints': {},
'robot_name': root.get('name', 'unknown')
}
# Parse links
for link in root.findall('link'):
link_name = link.get('name')
params['links'][link_name] = {}
# Get mass
inertial = link.find('inertial')
if inertial is not None:
mass_elem = inertial.find('mass')
if mass_elem is not None:
params['links'][link_name]['mass'] = float(mass_elem.get('value'))
# Get visual geometry
visual = link.find('visual')
if visual is not None:
geom = visual.find('geometry')
if geom is not None:
# Check for sphere
sphere = geom.find('sphere')
if sphere is not None:
params['links'][link_name]['geometry'] = {
'type': 'sphere',
'radius': float(sphere.get('radius'))
}
# Check for cylinder
cylinder = geom.find('cylinder')
if cylinder is not None:
params['links'][link_name]['geometry'] = {
'type': 'cylinder',
'radius': float(cylinder.get('radius')),
'length': float(cylinder.get('length'))
}
# Get color
material = visual.find('material')
if material is not None:
color = material.find('color')
if color is not None:
rgba_str = color.get('rgba')
params['links'][link_name]['color'] = [float(x) for x in rgba_str.split()]
# Parse joints
for joint in root.findall('joint'):
joint_name = joint.get('name')
params['joints'][joint_name] = {
'type': joint.get('type'),
'parent': joint.find('parent').get('link'),
'child': joint.find('child').get('link')
}
# Get axis
axis = joint.find('axis')
if axis is not None:
xyz_str = axis.get('xyz')
params['joints'][joint_name]['axis'] = [float(x) for x in xyz_str.split()]
# Get dynamics
dynamics = joint.find('dynamics')
if dynamics is not None:
params['joints'][joint_name]['damping'] = float(dynamics.get('damping', 0))
params['joints'][joint_name]['friction'] = float(dynamics.get('friction', 0))
return params

# Initialize PyBullet
physics_client = p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
p.setGravity(0, 0, -9.81)

# Camera setup for 2D side view (looking at XZ plane)
p.resetDebugVisualizerCamera(
cameraDistance=3.0,
cameraYaw=90, # Look from the side
cameraPitch=0, # Straight horizontal view
cameraTargetPosition=[0.5, 1.3, 0.5]
)

# Disable rendering during setup for speed
p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 0)
p.configureDebugVisualizer(p.COV_ENABLE_GUI, 1)

# Add a ground plane for reference
p.loadURDF("plane.urdf", basePosition=[0, 0, -1])

# Load and parse the URDF file
urdf_path = "double_pendulum_2d.urdf"

if not os.path.exists(urdf_path):
print(f"ERROR: URDF file not found at {urdf_path}")
print("Please make sure 'double_pendulum.urdf' is in the same directory as this script")
p.disconnect()
exit(1)

# Parse URDF to get parameters
print("Parsing URDF file...")
urdf_params = parse_urdf(urdf_path)

print(f"\nRobot: {urdf_params['robot_name']}")
print(f"Found {len(urdf_params['links'])} links:")
for link_name, link_data in urdf_params['links'].items():
print(f" - {link_name}:", end=" ")
if 'mass' in link_data:
print(f"mass={link_data['mass']}kg", end=" ")
if 'geometry' in link_data:
geom = link_data['geometry']
if geom['type'] == 'sphere':
print(f"[sphere r={geom['radius']}m]", end=" ")
elif geom['type'] == 'cylinder':
print(f"[cylinder r={geom['radius']}m l={geom['length']}m]", end=" ")
print()

print(f"\nFound {len(urdf_params['joints'])} joints:")
for joint_name, joint_data in urdf_params['joints'].items():
print(f" - {joint_name}: {joint_data['type']}")
print(f" {joint_data['parent']} -> {joint_data['child']}")
if 'damping' in joint_data:
print(f" damping={joint_data['damping']}, friction={joint_data['friction']}")

# Load the double pendulum
base_position = [0, 0, 1]
pendulum_id = p.loadURDF(
urdf_path,
basePosition=base_position,
useFixedBase=True,
flags=p.URDF_USE_INERTIA_FROM_FILE
)

print(f"\nLoaded URDF with {p.getNumJoints(pendulum_id)} joints in PyBullet")


#sblocca i motori
for i in range(p.getNumJoints(pendulum_id)):
info = p.getJointInfo(pendulum_id, i)
joint_type = info[2]
if joint_type in [p.JOINT_REVOLUTE, getattr(p, "JOINT_CONTINUOUS", p.JOINT_REVOLUTE)]: p.setJointMotorControl2(
bodyUniqueId=pendulum_id,
jointIndex=i,
controlMode=p.VELOCITY_CONTROL,
targetVelocity=0,
force=0
)

# Get joint information from PyBullet
num_joints = p.getNumJoints(pendulum_id)
joint_map = {} # Map joint names to indices
link_map = {} # Map link names to indices

for i in range(num_joints):
info = p.getJointInfo(pendulum_id, i)
joint_name = info[1].decode('utf-8')
link_name = info[12].decode('utf-8')
joint_map[joint_name] = i
link_map[link_name] = i

# Set initial angles for the revolute joints
initial_angle1 = np.pi / 3 # 60 degrees
initial_angle2 = np.pi / 2 # 45 degrees

print("\nSetting initial joint angles:")
if 'joint1' in joint_map:
p.resetJointState(pendulum_id, joint_map['joint1'], initial_angle1)
print(f" joint1: {np.degrees(initial_angle1):.1f}°")

if 'joint2' in joint_map:
p.resetJointState(pendulum_id, joint_map['joint2'], initial_angle2)
print(f" joint2: {np.degrees(initial_angle2):.1f}°")

# Re-enable rendering
p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, 1)

# Trail tracking
trail_bob1 = []
trail_bob2 = []
trail_line_ids_1 = []
trail_line_ids_2 = []
max_trail_length = 200

print("\n" + "="*50)
print("Double Pendulum Simulation Started")
print("="*50)
print("Close the window to exit")

# Simulation parameters
dt = 1./240.
p.setTimeStep(dt)
frame_skip = 4
frame_count = 0

# Get bob link indices
bob1_index = link_map.get('bob1')
bob2_index = link_map.get('bob2')

p.resetDebugVisualizerCamera(
cameraDistance=1.0, # how far the camera is
cameraYaw=180, # rotate around Z axis so we look along +Y
cameraPitch=-10, # tilt downward a bit
cameraTargetPosition=[0, 3, 0] # center the camera on the pendulum
)

try:
while True:
p.stepSimulation()
frame_count += 1
# Only update visuals every few frames
if frame_count % frame_skip == 0:
# Get bob positions
pos1, pos2 = None, None
if bob1_index is not None:
link_state1 = p.getLinkState(pendulum_id, bob1_index)
pos1 = link_state1[0]
if bob2_index is not None:
link_state2 = p.getLinkState(pendulum_id, bob2_index)
pos2 = link_state2[0]
# Add to trails
if pos1:
trail_bob1.append(pos1)
if pos2:
trail_bob2.append(pos2)
# Limit trail length
if len(trail_bob1) > max_trail_length:
trail_bob1.pop(0)
if trail_line_ids_1:
p.removeUserDebugItem(trail_line_ids_1.pop(0))
if len(trail_bob2) > max_trail_length:
trail_bob2.pop(0)
if trail_line_ids_2:
p.removeUserDebugItem(trail_line_ids_2.pop(0))
# Draw new trail segments
if len(trail_bob1) > 1:
line_id = p.addUserDebugLine(
trail_bob1[-2], trail_bob1[-1],
lineColorRGB=[1, 0.5, 0.5],
lineWidth=2
)
trail_line_ids_1.append(line_id)
if len(trail_bob2) > 1:
line_id = p.addUserDebugLine(
trail_bob2[-2], trail_bob2[-1],
lineColorRGB=[0.5, 0.5, 1],
lineWidth=2
)
trail_line_ids_2.append(line_id)
time.sleep(dt)

except KeyboardInterrupt:
print("\nSimulation stopped by user")

p.disconnect()
print("PyBullet disconnected. Goodbye!")


 


 

 

 

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# --- Parametri fisici ---
g = 9.81
L1 = L2 = 1.0
m1 = m2 = 1.0

# --- Equazioni del moto ---
def derivs(state, t):
th1, w1, th2, w2 = state
delta = th2 - th1

den1 = (m1 + m2) * L1 - m2 * L1 * np.cos(delta)**2
den2 = (L2 / L1) * den1

dw1 = (m2 * L1 * w1**2 * np.sin(delta) * np.cos(delta)
+ m2 * g * np.sin(th2) * np.cos(delta)
+ m2 * L2 * w2**2 * np.sin(delta)
- (m1 + m2) * g * np.sin(th1)) / den1

dw2 = (-L2 / L1 * m2 * w2**2 * np.sin(delta) * np.cos(delta)
+ (m1 + m2) * (g * np.sin(th1) * np.cos(delta)
- L1 * w1**2 * np.sin(delta) - g * np.sin(th2))) / den2

return np.array([w1, dw1, w2, dw2])

# --- Integrazione numerica (RK4) ---
def rk4_step(f, y, t, dt):
k1 = f(y, t)
k2 = f(y + 0.5 * dt * k1, t + 0.5 * dt)
k3 = f(y + 0.5 * dt * k2, t + 0.5 * dt)
k4 = f(y + dt * k3, t + dt)
return y + (dt / 6) * (k1 + 2*k2 + 2*k3 + k4)

# --- Simulazione ---
dt = 0.01
t = np.arange(0, 20, dt)

# Due condizioni iniziali quasi identiche
y1 = np.array([np.pi/2, 0, np.pi/2 + 0.01, 0])
y2 = np.array([np.pi/2, 0, np.pi/2 + 0.010001, 0])

y1s, y2s = [], []
for _ in t:
y1 = rk4_step(derivs, y1, 0, dt)
y2 = rk4_step(derivs, y2, 0, dt)
y1s.append(y1.copy())
y2s.append(y2.copy())

y1s, y2s = np.array(y1s), np.array(y2s)

# --- Conversione in coordinate cartesiane ---
x1_1 = L1 * np.sin(y1s[:,0])
y1_1 = -L1 * np.cos(y1s[:,0])
x2_1 = x1_1 + L2 * np.sin(y1s[:,2])
y2_1 = y1_1 - L2 * np.cos(y1s[:,2])

x1_2 = L1 * np.sin(y2s[:,0])
y1_2 = -L1 * np.cos(y2s[:,0])
x2_2 = x1_2 + L2 * np.sin(y2s[:,2])
y2_2 = y1_2 - L2 * np.cos(y2s[:,2])

# --- Animazione ---
fig, ax = plt.subplots()
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-2.2, 2.2)
ax.set_aspect('equal')
ax.set_title("Double Pendulum Chaos")

(line1,) = ax.plot([], [], 'o-', lw=2, color='blue', label='Pendulum A')
(line2,) = ax.plot([], [], 'o-', lw=2, color='red', alpha=0.6, label='Pendulum B')
ax.legend()

def update(i):
line1.set_data([0, x1_1[i], x2_1[i]], [0, y1_1[i], y2_1[i]])
line2.set_data([0, x1_2[i], x2_2[i]], [0, y1_2[i], y2_2[i]])
return line1, line2

ani = FuncAnimation(fig, update, frames=len(t), interval=10, blit=True)
plt.show()


 

 

 

Montagne frattali

 Negli anni 90 quando i miei amici matematici leggevano (Ri)creazioni al calcolatore di Dewdney (vedi anche pag. 107 del libro The Magic Mac...