176 lines
8.8 KiB
C#
176 lines
8.8 KiB
C#
using System.Numerics;
|
|
using System.Runtime.Intrinsics;
|
|
using OpenTK;
|
|
using OpenTK.Windowing.Desktop;
|
|
using Q01_Cálculos;
|
|
|
|
// Convenio de signos:
|
|
// X: Eje horizontal de la pantalla (izquierda a derecha positivo)
|
|
// Z: Eje vertical de la pantalla (abajo a arriba positivo)
|
|
// Y: Eje perpendicular a la pantalla (saliente positivo)
|
|
|
|
|
|
// Definimos una palanca en punto neutro, utilizando las medidas que
|
|
// hemos tomado para definir el volumen, la masa y el centro de gravedad
|
|
// de los componentes del ensamblaje que componen la palanca.
|
|
|
|
// La masa la estimaremos como el volumen del componente por la densidad
|
|
// de su material.
|
|
|
|
// Creamos una instancia de la clase Assembly para unir las piezas
|
|
// Para simplificar el programa, usamos técnicas de programación orientada a objetos (OOP)
|
|
// que nos permiten crear clases que describen los componentes del sistema, y las operaciones
|
|
// que se pueden realizar con ellos. Estas son "Assembly" y "Component".
|
|
Assembly Palanca = new() { Name = "Palanca de cambio" };
|
|
|
|
// Describimos físicamente los componentes, estimando su masa a partir del
|
|
// volumen y la densidad del material, y su centro de masas en base a sólidos
|
|
// tridimensionales sencillos:
|
|
|
|
// Perimero definimos las densidades de los materiales
|
|
const float
|
|
DensAcero = 7800f, // kg/m3
|
|
DensAbs = 1110f; // kg/m3
|
|
|
|
// Aproximamos la parte superior del pomo como una esfera
|
|
float diametro_esfera = 4.5e-2f; //m
|
|
float VolPomo1 = 4 / 3 * MathF.PI * MathF.Pow(diametro_esfera / 2, 3);
|
|
Palanca.components.Add(new Component()
|
|
{
|
|
Name = "Parte Superior Pomo",
|
|
Mass = VolPomo1 * DensAbs,
|
|
CenterOfMass = new(0, -diametro_esfera / 2, 30e-2f)
|
|
});
|
|
float LongitudPalanca = 30e-2f; //m
|
|
|
|
// Aproximamos la parte inferior del pomo como un cilindro hueco por donde
|
|
// pasarán la varilla y su cubierta
|
|
float diametro_cilindro = 2.5e-2f; // m
|
|
float diametro_interno_cilindro = 1.5e-2f; // m
|
|
float altura_cilindro = 1.5e-2f; // m
|
|
float VolPomo2 = MathF.PI * (MathF.Pow(diametro_cilindro / 2, 2) - MathF.Pow(diametro_interno_cilindro / 2, 2)) * altura_cilindro;
|
|
Palanca.components.Add(new Component()
|
|
{
|
|
Name = "Base Pomo",
|
|
Mass = VolPomo2 * DensAbs,
|
|
CenterOfMass = new(0, 0, LongitudPalanca - ((diametro_esfera / 2) - (altura_cilindro / 2)))
|
|
});
|
|
|
|
// Aproximamos la cubierta de la varilla como un cilindro hueco
|
|
float DiametroExterno2 = 1.5e-2f; //m
|
|
float DiametroInterno2 = 0.8e-2f; //m
|
|
float VolPalancaExterna = MathF.PI * (MathF.Pow(DiametroExterno2 / 2, 2) - MathF.Pow(DiametroInterno2 / 2, 2)) * LongitudPalanca;
|
|
Palanca.components.Add(new Component()
|
|
{
|
|
Name = "Cubierta plástica de la varilla",
|
|
Mass = VolPalancaExterna * DensAbs,
|
|
CenterOfMass = new(0, 0, LongitudPalanca / 2)
|
|
});
|
|
|
|
// Aproximamos la varilla como un cilindro macizo
|
|
float DiametroExterno1 = 1.5e-2f; //m
|
|
float VolPalancaInterna = MathF.PI * MathF.Pow(DiametroExterno1 / 2, 2) * LongitudPalanca;
|
|
Palanca.components.Add(new Component()
|
|
{
|
|
Name = "Varilla interna de la palanca",
|
|
Mass = VolPalancaInterna * DensAcero,
|
|
CenterOfMass = new(0, 0, LongitudPalanca / 2)
|
|
});
|
|
|
|
// Definimos las posicíones que hemos medido en el plano como tuplas (plano X Y)
|
|
(float, float) primera = (-6.5e-2f, -4.5e-2f);
|
|
(float, float) tercera = (0, -4.5e-2f);
|
|
(float, float) quinta = (4.5e-2f, -4.5e-2f);
|
|
(float, float) segunda = (-6.5e-2f, 10e-2f);
|
|
(float, float) cuarta = (0, 10e-2f);
|
|
(float, float) reversa = (4.5e-2f, 10e-2f);
|
|
|
|
Console.WriteLine("================================================");
|
|
Console.WriteLine("== Datos del sistema ==");
|
|
Console.WriteLine("================================================");
|
|
|
|
// Mostramos las propiedades físicas de la palanca por pantalla
|
|
Console.Write(Palanca.Summary());
|
|
|
|
// La fuerza que hace la palanca en reposo sobre la base es el peso de la palanca:
|
|
var fuerza_base = Palanca.Weight;
|
|
// Como está en reposo, no se trasalda, de modo que el sumatorio de fuerzas debe ser cero.
|
|
// Por ello, la reacción será el vector nulo menos la fuerza sobre la base (el peso de la palanca).
|
|
var reaccion_base = Vector3.Zero - fuerza_base;
|
|
|
|
// El momento sobre la base será el producto vectorial de la posición del centro de masas
|
|
// por el peso de la palanca.
|
|
var momento_base = Vector3.Cross(Palanca.CenterOfMass, Palanca.Weight);
|
|
// Y como tampoco rota, significa que el sumatorio de momentos se cancela.
|
|
// Por ello, el momento de reacción que hace la base sobre la palanca será el vector nulo menos
|
|
// el momento que hace la palanca sobre la base.
|
|
var momento_reaccion = Vector3.Zero - momento_base;
|
|
|
|
Console.WriteLine();
|
|
Console.WriteLine("===============================================================================");
|
|
Console.WriteLine("== Cálculos de fuerzas y momentos : Escalares / <x,y,z> ==");
|
|
Console.WriteLine("===============================================================================");
|
|
Console.WriteLine($"Fuerza que ejerce la barra sobre la base : {fuerza_base} N");
|
|
Console.WriteLine($"Fuerza resistente que hace la base sobre la palanca : {reaccion_base} N");
|
|
Console.WriteLine($"Momento que hace la barra sobre la base : {momento_base * 100} N·cm");
|
|
Console.WriteLine($"Momento que hace la base sobre la palanca : {momento_reaccion * 100} N·cm");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Momentos que hace la palanca cuando engrana una marcha:");
|
|
Console.WriteLine($"Centro de masa 1a : {CentroDeMasasRotado(Palanca, LongitudPalanca, primera)}");
|
|
Console.WriteLine($"Centro de masa 2a : {CentroDeMasasRotado(Palanca, LongitudPalanca, segunda)}");
|
|
Console.WriteLine($"Centro de masa 3a : {CentroDeMasasRotado(Palanca, LongitudPalanca, tercera)}");
|
|
Console.WriteLine($"Centro de masa 4a : {CentroDeMasasRotado(Palanca, LongitudPalanca, cuarta)}");
|
|
Console.WriteLine($"Centro de masa 5a : {CentroDeMasasRotado(Palanca, LongitudPalanca, quinta)}");
|
|
Console.WriteLine($"Centro de masa Reversa : {CentroDeMasasRotado(Palanca, LongitudPalanca, reversa)}");
|
|
Console.WriteLine();
|
|
|
|
//Simulation simulación = new Simulation(new GameWindowSettings() { UpdateFrequency = 20, RenderFrequency = 20 }, new NativeWindowSettings() { Size = new OpenTK.Mathematics.Vector2i(500, 500) });
|
|
//simulación.Run();
|
|
|
|
|
|
// Obtiene el vector de longitud determinada que corta los planos X e Y por
|
|
// los puntos especificados
|
|
Vector3 Position(float X, float Y, float vectorLength)
|
|
{
|
|
// Calculamos la longitud del segmento de la base de nuestro vector resultado como
|
|
// la hipotenusa del triangulo de catetos X,Y (usando Pitágoras):
|
|
float _base = MathF.Sqrt(MathF.Pow(X, 2) + MathF.Pow(Y, 2));
|
|
// Calculamos la componente Z como el cateto restante del triangulo cuya hipotenusa
|
|
// es el módulo de nuestro vector, con la base calculada en el paso anterior.
|
|
float _z = MathF.Sqrt(MathF.Pow(vectorLength, 2) - MathF.Pow(_base, 2));
|
|
// Construimos y devolvemos un vector con el valor Z hallado.
|
|
return new Vector3(X, Y, _z);
|
|
}
|
|
|
|
// Obtiene el vector que representa el centro de masas de la palanca cuando se mueve en el plano XY
|
|
// hacia la posición de la marcha.
|
|
Vector3 CentroDeMasasRotado(IRigidBody componente, float longitud, (float, float) posicionesMarcha)
|
|
{
|
|
Vector3 rcm_rotado;
|
|
|
|
// Calculamos el vector de la posición de la palanca con la marcha metida.
|
|
var posiciónDestino = Position(posicionesMarcha.Item1, posicionesMarcha.Item2, longitud);
|
|
// Calculamos el eje de la rotación que se debe producir para que el vector de posición
|
|
// realice el movimiento para engranar la marcha.
|
|
// El eje de rotación lo podemos definir como el vector unitario perpendicular al vector rotado
|
|
// respecto al vector original. Para ello podemos usar el producto vectorial (producto en cruz)
|
|
// normalizado, para convertirlo en vector unitario.
|
|
var ejeDeRotación = Vector3.Normalize(Vector3.Cross(componente.CenterOfMass, posiciónDestino));
|
|
|
|
// El siguiente caso es determinar el ángulo que rotó el vector para orientarse a la posición
|
|
// final. Para ello usamos una formula derivada de la definición de producto escalar.
|
|
// El producti escalar se puede calcular como = a * b * el coseno del angulo que forman, de modo que:
|
|
var ángulo = MathF.Acos(
|
|
Vector3.Dot(componente.CenterOfMass, posiciónDestino) /
|
|
(componente.CenterOfMass.Length() * posiciónDestino.Length()));
|
|
|
|
// A continuación, crearemos un quaternión a partir del eje y el ángulo de rotación hallados:
|
|
Quaternion rotación = Quaternion.CreateFromAxisAngle(ejeDeRotación, ángulo);
|
|
|
|
// Y finalmente aplicamos la transformación vectorial del centro de masas para aplicar la misma
|
|
// rotación que ha sufrido el vector que representa la orientación de la palanca de cambio.
|
|
rcm_rotado = Vector3.Transform(componente.CenterOfMass, rotación);
|
|
|
|
// Devolvemos el centro de masas rotado como resultado
|
|
return rcm_rotado;
|
|
} |