Comments (7)
If I could find out how the ground element is created/referenced I would be able to partition into patches of higher friction based on the coordinates, but I can't find anything in your API which directly lets me extract coordinates of objects.
from pyelastica.
Hello Akash, I can help with this. Yes, your understanding is correct - the "AnisotropicFrictionalPlane" doesn't have a boundary, so by adding many of them together will just duplicate friction force.
The way to make local friction patches is to create a custom frictional plane. You can use the "AnisotropicFrictionalPlane" as a template, which should give you most of the basics you need. Then, the only change you want to make there, is to create a function to check the distance between your patch center and snake center. This sudo code may explain this:
scale_friction = 1
for every element/node on snake:
compute distance between this element to patch center
if distance < radius of the patch:
scale_friction = 30
This way, you can have different friction for each segment on the snake, and only add the friction when the element is in the patch region.
One more thing to add - in case you don't find the ground setup, it is in elastica/interaction.py
Please let me know if you have further question.
from pyelastica.
Hi thanks for your reply @xzhan139 . I had a follow up question, when I try to create the customfrictional plane I would still need a way to bound the dimensions of the plane, where are the commands to set the boundaries inside the customfrictional plane?
from pyelastica.
Hi, let me explain this in another way. Your customfricitonal plane should start looking like a normal plane - no boundary and uniform friction everywhere. Then you just need to create a few high frictional spots on that plane. To do this, just scale the friction whenever snake elements are inside the region. So in the end, you just need one plane - your custom friction plane, which is an infinite plane, no bounds. Does this make sense?
from pyelastica.
I get the idea, I haven't gotten the code to work yet.
Basically what I did was , create a function like you suggested which checks if a given point is inside a patch defined by known coordinates. And it works for test cases,
import numpy as np
def is_point_inside_patch(point, patch_vertices):
"""
Check if a 3D point is inside a patch defined by four vertices.
Args:
point (numpy.ndarray): 3D coordinates of the point.
patch_vertices (list of numpy.ndarray): List of four 3D coordinates defining the patch.
Returns:
bool: True if the point is inside the patch, False otherwise.
"""
# Calculate the normal vectors of the four triangles formed by the point and patch vertices
normals = []
for i in range(4):
vertex1 = patch_vertices[i]
vertex2 = patch_vertices[(i + 1) % 4]
normal = np.cross(vertex2 - vertex1, point - vertex1)
normals.append(normal)
# Check if the point is on the same side of all the patch's triangles (using dot product)
for i in range(4):
if np.dot(normals[i], normals[(i + 1) % 4]) < 0:
return False
return True
Test cases
if name == "main":
# Define patch vertices (four known points in 3D space)
patch_vertices = [
np.array([0.0, 0.0, 0.0]),
np.array([0.5, 0.0, 0.0]),
np.array([0.5, 0.5, 0.0]),
np.array([0.0, 0.5, 0.0]),
]
# Generate 10 random points
random_points = np.random.uniform(size=(10, 3))
# Check if each random point is inside the patch and print the result
for i, point in enumerate(random_points):
is_inside = is_point_inside_patch(point, patch_vertices)
print(f"Point {i + 1}: {point} is inside the patch: {is_inside}")
Now when I do the same thing in the main sim, snake_diffraction.py. Which I am copy pasting... I am running into an error.
Here's the code.
import os
import numpy as np
import elastica as ea
def is_point_inside_patch(point, patch_vertices):
"""
Check if a 3D point is inside a patch defined by four vertices.
Args:
point (numpy.ndarray): 3D coordinates of the point.
patch_vertices (list of numpy.ndarray): List of four 3D coordinates defining the patch.
Returns:
bool: True if the point is inside the patch, False otherwise.
"""
# Calculate the normal vectors of the four triangles formed by the point and patch vertices
normals = []
for i in range(4):
vertex1 = patch_vertices[i]
vertex2 = patch_vertices[(i + 1) % 4]
normal = np.cross(vertex2 - vertex1, point - vertex1)
normals.append(normal)
# Check if the point is on the same side of all the patch's triangles (using dot product)
for i in range(4):
if np.dot(normals[i], normals[(i + 1) % 4]) < 0:
return False
return True
from snake_diffraction_postprocessing import (
plot_snake_velocity,
plot_video,
compute_projected_velocity,
plot_curvature,
)
class SnakeDiffractionSimulator(
ea.BaseSystemCollection, ea.Constraints, ea.Forcing, ea.Damping, ea.CallBacks
):
pass
def run_snake(
b_coeff, PLOT_FIGURE=False, SAVE_FIGURE=False, SAVE_VIDEO=False, SAVE_RESULTS=False
):
# Initialize the simulation class
snake_diffraction_sim = SnakeDiffractionSimulator()
# Simulation parameters
period = 2
final_time = (20.0 + 0.01) * period
# setting up test params
n_elem = 50
start = np.zeros((3,))
direction = np.array([0.0, 0.0, 1.0])
normal = np.array([0.0, 1.0, 0.0])
base_length = 0.35
base_radius = base_length * 0.011
density = 1000
E = 1e6
poisson_ratio = 0.5
shear_modulus = E / (poisson_ratio + 1.0)
snake_body = ea.CosseratRod.straight_rod(
n_elem,
start,
direction,
normal,
base_length,
base_radius,
density,
youngs_modulus=E,
shear_modulus=shear_modulus,
)
snake_diffraction_sim.append(snake_body)
snake_positions = snake_body.position_collection
print("snake position is...")
print(snake_positions)
# Add gravitational forces
gravitational_acc = -9.80665
snake_diffraction_sim.add_forcing_to(snake_body).using(
ea.GravityForces, acc_gravity=np.array([0.0, gravitational_acc, 0.0])
)
# Add muscle torques only Lateral Wave right now
wave_length = b_coeff[-1]
snake_diffraction_sim.add_forcing_to(snake_body).using(
ea.MuscleTorques,
base_length=base_length,
b_coeff=b_coeff[:-1],
period=period,
wave_number=2.0 * np.pi / (wave_length),
phase_shift=0.0,
rest_lengths=snake_body.rest_lengths,
ramp_up_time=period,
direction=normal,
with_spline=True,
)
# Add friction forces
# Uniform friction with ground
origin_plane = np.array([0.0, -base_radius, 0.0])
normal_plane = normal
slip_velocity_tol_Regular = 1e-6
slip_velocity_tol_HighFriction = 1e-4
p_factor = 30
froude = 0.1
mu = base_length / (period * period * np.abs(gravitational_acc) * froude)
kinetic_mu_array_Regular = np.array(
[mu, 2 * mu, 10.0 * mu]
) # [forward, backward, sideways]
static_mu_array_Regular = np.zeros(kinetic_mu_array_Regular.shape)
kinetic_mu_array_HighFriction = np.array(
[mu * p_factor, 2 * mu * p_factor, 10.0 * mu * p_factor]
)
static_mu_array_HighFriction = np.zeros(kinetic_mu_array_HighFriction.shape)
patch1_A_coord = np.array([0.15, 0.0, 0.6])
patch1_B_coord = np.array([0.15, 0.0, 0.9])
patch1_C_coord = np.array([-0.15, 0.0, 0.9])
patch1_D_coord = np.array([-0.15, 0.0, 0.6])
patch_vertices = [
patch1_A_coord,
patch1_B_coord,
patch1_C_coord,
patch1_D_coord,
]
for i, node_position in enumerate(snake_body.position_collection):
# Check if the node position is inside the patch
if is_point_inside_patch(node_position, patch_vertices):
# Apply high friction coefficients
snake_diffraction_sim.add_forcing_to(snake_body).using(
ea.AnisotropicFrictionalPlane,
k=1.0, # Modify this to your desired value
nu=1e-6, # Modify this to your desired value
plane_origin=origin_plane,
plane_normal=normal_plane,
slip_velocity_tol=slip_velocity_tol_HighFriction, # Define this value
static_mu_array=static_mu_array_HighFriction, # Define this array
kinetic_mu_array=kinetic_mu_array_HighFriction, # Define this array
)
else:
# Apply regular friction coefficients
snake_diffraction_sim.add_forcing_to(snake_body).using(
ea.AnisotropicFrictionalPlane,
k=1.0, # Modify this to your desired value
nu=1e-6, # Modify this to your desired value
plane_origin=origin_plane,
plane_normal=normal_plane,
slip_velocity_tol=slip_velocity_tol_Regular,
static_mu_array=static_mu_array_Regular,
kinetic_mu_array=kinetic_mu_array_Regular,
)
# snake_diffraction_sim.add_forcing_to(snake_body).using(
# ea.AnisotropicFrictionalPlane,
# k=1.0,
# nu=1e-6,
# plane_origin=origin_plane,
# plane_normal=normal_plane,
# slip_velocity_tol=slip_velocity_tol_LessFriction,
# static_mu_array=static_mu_array_LessFriction,
# kinetic_mu_array=kinetic_mu_array_LessFriction,
# )
# add damping
damping_constant = 2e-3
time_step = 1e-4
snake_diffraction_sim.dampen(snake_body).using(
ea.AnalyticalLinearDamper,
damping_constant=damping_constant,
time_step=time_step,
)
# Implement Pegs Via Collisions
# start = np.array([[0.0, -base_radius, 0.75]])
# Peg_n_elem = 50
# start_peg1 = start
# direction_peg1 = np.array([0.0, 1.0, 0.0])
# normal_peg1 = np.array([0.0, 0.0, 1.0])
# Rod parameters
# Peg_base_length = 0.05
# Peg_base_radius = 0.023
# Peg_base_area = np.pi * base_radius ** 2
# Peg_density = 1750
# Peg_nu = 0.0
# Peg_E = 5e6
# Peg_poisson_ratio = 0.5
# eg_shear_modulus = Peg_E / (Peg_poisson_ratio + 1.0)
# peg1 = ea.CosseratRod.straight_rod(
# Peg_n_elem,
# start_peg1,
# direction_peg1,
# normal_peg1,
# Peg_base_length,
# Peg_base_radius,
# Peg_density,
# youngs_modulus=Peg_E,
# shear_modulus=Peg_shear_modulus,
# )
# snake_diffraction_sim.append(peg1)
# Contact between two rods
# snake_diffraction_sim.connect(shearable_rod, peg1).using(
# ea.ExternalContact, k=1e3, nu=0.5
# )
total_steps = int(final_time / time_step)
rendering_fps = 60
step_skip = int(1.0 / (rendering_fps * time_step))
# Add call backs
class ContinuumSnakeCallBack(ea.CallBackBaseClass):
"""
Call back function for continuum snake
"""
def __init__(self, step_skip: int, callback_params: dict):
ea.CallBackBaseClass.__init__(self)
self.every = step_skip
self.callback_params = callback_params
def make_callback(self, system, time, current_step: int):
if current_step % self.every == 0:
self.callback_params["time"].append(time)
self.callback_params["step"].append(current_step)
self.callback_params["position"].append(
system.position_collection.copy()
)
self.callback_params["velocity"].append(
system.velocity_collection.copy()
)
self.callback_params["avg_velocity"].append(
system.compute_velocity_center_of_mass()
)
self.callback_params["center_of_mass"].append(
system.compute_position_center_of_mass()
)
self.callback_params["curvature"].append(system.kappa.copy())
return
pp_list = ea.defaultdict(list)
snake_diffraction_sim.collect_diagnostics(snake_body).using(
ContinuumSnakeCallBack, step_skip=step_skip, callback_params=pp_list
)
snake_diffraction_sim.finalize()
timestepper = ea.PositionVerlet()
ea.integrate(timestepper, snake_diffraction_sim, final_time, total_steps)
if PLOT_FIGURE:
filename_plot = "snake_diffraction_velocity2.png"
plot_snake_velocity(pp_list, period, filename_plot, SAVE_FIGURE)
plot_curvature(pp_list, snake_body.rest_lengths, period, SAVE_FIGURE)
if SAVE_VIDEO:
filename_video = 'snake_diffraction2.gif'
# filename_video = "continuum_snake.mp4"
plot_video(
pp_list,
video_name=filename_video,
fps=rendering_fps,
xlim=(0, 4),
ylim=(-1, 1),
)
if SAVE_RESULTS:
import pickle
filename = "snake_diffraction.dat"
file = open(filename, "wb")
pickle.dump(pp_list, file)
file.close()
# Compute the average forward velocity. These will be used for optimization.
[_, _, avg_forward, avg_lateral] = compute_projected_velocity(pp_list, period)
return avg_forward, avg_lateral, pp_list
if name == "main":
# Options
PLOT_FIGURE = True
SAVE_FIGURE = True
SAVE_VIDEO = True
SAVE_RESULTS = False
CMA_OPTION = False
if CMA_OPTION:
import cma
SAVE_OPTIMIZED_COEFFICIENTS = False
def optimize_snake(spline_coefficient):
[avg_forward, _, _] = run_snake(
spline_coefficient,
PLOT_FIGURE=False,
SAVE_FIGURE=False,
SAVE_VIDEO=False,
SAVE_RESULTS=False,
)
return -avg_forward
# Optimize snake for forward velocity. In cma.fmin first input is function
# to be optimized, second input is initial guess for coefficients you are optimizing
# for and third input is standard deviation you initially set.
optimized_spline_coefficients = cma.fmin(optimize_snake, 7 * [0], 0.5)
# Save the optimized coefficients to a file
filename_data = "optimized_coefficients.txt"
if SAVE_OPTIMIZED_COEFFICIENTS:
assert filename_data != "", "provide a file name for coefficients"
np.savetxt(filename_data, optimized_spline_coefficients, delimiter=",")
else:
# Add muscle forces on the rod
if os.path.exists("optimized_coefficients.txt"):
t_coeff_optimized = np.genfromtxt(
"optimized_coefficients.txt", delimiter=","
)
else:
wave_length = 1.0
t_coeff_optimized = np.array(
[3.4e-3, 3.3e-3, 4.2e-3, 2.6e-3, 3.6e-3, 3.5e-3]
)
t_coeff_optimized = np.hstack((t_coeff_optimized, wave_length))
# run the simulation
[avg_forward, avg_lateral, pp_list] = run_snake(
t_coeff_optimized, PLOT_FIGURE, SAVE_FIGURE, SAVE_VIDEO, SAVE_RESULTS
)
print("average forward velocity:", avg_forward)
print("average forward lateral:", avg_lateral)
And here's the error...
Traceback (most recent call last):
File "C:\Users\avardhan7\AppData\Local\JetBrains\PyCharm 2023.1.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode
coro = func()
File "", line 1, in
File "C:\Users\avardhan7\AppData\Local\JetBrains\PyCharm 2023.1.2\plugins\python\helpers\pydev_pydev_bundle\pydev_umd.py", line 198, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "C:\Users\avardhan7\AppData\Local\JetBrains\PyCharm 2023.1.2\plugins\python\helpers\pydev_pydev_imps_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:\Users\avardhan7\Documents\OPEN_AI\pyElastica\snake_diffraction\snake_diffraction.py", line 351, in
[avg_forward, avg_lateral, pp_list] = run_snake(
File "C:\Users\avardhan7\Documents\OPEN_AI\pyElastica\snake_diffraction\snake_diffraction.py", line 143, in run_snake
if is_point_inside_patch(node_position, patch_vertices):
File "C:\Users\avardhan7\Documents\OPEN_AI\pyElastica\snake_diffraction\snake_diffraction.py", line 22, in is_point_inside_patch
normal = np.cross(vertex2 - vertex1, point - vertex1)
ValueError: operands could not be broadcast together with shapes (51,) (3,)
I haven't had time to debug this...you can choose to keep this thread open or close it, and I will post with the status once I am able to resolve the issue. Thanks.
from pyelastica.
Finally got around to getting it to work, there was a minor error with the loop syntax.
Here's a video. Thanks for your help.
from pyelastica.
Seems like this issue has been resolved, I am closing it.
from pyelastica.
Related Issues (20)
- Failure for Helix under Gravity Case HOT 2
- Can PyElastica support GPU acceleration? HOT 3
- Do PyElastica support to simulate a rod containing two segments? HOT 3
- Question about base length HOT 8
- Replicating side-winding results from your Nat-comm snake paper HOT 29
- Instant controler and simulation HOT 6
- Small scale rod simulation HOT 11
- Updating muscle forces mid-simulation - how does it work? HOT 1
- Small-scale simulation problem 2 HOT 1
- OneEndFixedBC didn't work for applying a external torque HOT 4
- Adding force and torque at same time HOT 5
- How to using PyElastica data to visualize in 3D models? HOT 1
- `FileNotFoundError` `python continuum_snake.py` HOT 1
- Bug and fix in 2_Slithering_Snake.ipynb HOT 1
- Task 1: Utilizing Python to clean and convert data from Elastica into a format recognizable by Blender. HOT 1
- Avoid having randomized unittest HOT 17
- [GSOC project 3] Binding PyElastica with Elastica++ HOT 1
- GSoC interest in Optimizing backend kernels in PyElastica HOT 1
- Examples not running due to modules not being found HOT 18
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pyelastica.