aboutsummaryrefslogtreecommitdiff
path: root/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs')
-rw-r--r--src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs608
1 files changed, 608 insertions, 0 deletions
diff --git a/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs b/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs
new file mode 100644
index 0000000..3e31256
--- /dev/null
+++ b/src/dynamics/solver/joint_constraint/joint_velocity_constraint.rs
@@ -0,0 +1,608 @@
+use crate::dynamics::solver::joint_constraint::JointVelocityConstraintBuilder;
+use crate::dynamics::solver::DeltaVel;
+use crate::dynamics::{IntegrationParameters, JointData, JointGraphEdge, JointIndex};
+use crate::math::{AngVector, AngularInertia, Isometry, Point, Real, Vector, DIM, SPATIAL_DIM};
+use crate::utils::{WDot, WReal};
+use simba::simd::SimdRealField;
+
+#[cfg(feature = "simd-is-enabled")]
+use {
+ crate::math::{SimdReal, SIMD_WIDTH},
+ na::SimdValue,
+};
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct MotorParameters<N: SimdRealField> {
+ pub stiffness: N,
+ pub damping: N,
+ pub gamma: N,
+ // pub keep_lhs: bool,
+ pub target_pos: N,
+ pub target_vel: N,
+ pub max_impulse: N,
+}
+
+impl<N: SimdRealField> Default for MotorParameters<N> {
+ fn default() -> Self {
+ Self {
+ stiffness: N::zero(),
+ damping: N::zero(),
+ gamma: N::zero(),
+ // keep_lhs: true,
+ target_pos: N::zero(),
+ target_vel: N::zero(),
+ max_impulse: N::zero(),
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum WritebackId {
+ Dof(usize),
+ Limit(usize),
+ Motor(usize),
+}
+
+// TODO: right now we only use this for impulse_joints.
+// However, it may actually be a good idea to use this everywhere in
+// the solver, to avoid fetching data from the rigid-body set
+// every time.
+#[derive(Copy, Clone)]
+pub struct SolverBody<N: SimdRealField, const LANES: usize> {
+ pub linvel: Vector<N>,
+ pub angvel: AngVector<N>,
+ pub im: N,
+ pub sqrt_ii: AngularInertia<N>,
+ pub world_com: Point<N>,
+ pub mj_lambda: [usize; LANES],
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct JointVelocityConstraint<N: SimdRealField, const LANES: usize> {
+ pub mj_lambda1: [usize; LANES],
+ pub mj_lambda2: [usize; LANES],
+
+ pub joint_id: [JointIndex; LANES],
+
+ pub impulse: N,
+ pub impulse_bounds: [N; 2],
+ pub lin_jac: Vector<N>,
+ pub ang_jac1: AngVector<N>,
+ pub ang_jac2: AngVector<N>,
+
+ pub inv_lhs: N,
+ pub rhs: N,
+ pub rhs_wo_bias: N,
+
+ pub im1: N,
+ pub im2: N,
+
+ pub writeback_id: WritebackId,
+}
+
+impl<N: WReal, const LANES: usize> JointVelocityConstraint<N, LANES> {
+ pub fn invalid() -> Self {
+ Self {
+ mj_lambda1: [crate::INVALID_USIZE; LANES],
+ mj_lambda2: [crate::INVALID_USIZE; LANES],
+ joint_id: [crate::INVALID_USIZE; LANES],
+ impulse: N::zero(),
+ impulse_bounds: [N::zero(), N::zero()],
+ lin_jac: Vector::zeros(),
+ ang_jac1: na::zero(),
+ ang_jac2: na::zero(),
+ inv_lhs: N::zero(),
+ rhs: N::zero(),
+ rhs_wo_bias: N::zero(),
+ im1: N::zero(),
+ im2: N::zero(),
+ writeback_id: WritebackId::Dof(0),
+ }
+ }
+
+ pub fn solve_generic(&mut self, mj_lambda1: &mut DeltaVel<N>, mj_lambda2: &mut DeltaVel<N>) {
+ let dlinvel = self.lin_jac.dot(&(mj_lambda2.linear - mj_lambda1.linear));
+ let dangvel =
+ self.ang_jac2.gdot(mj_lambda2.angular) - self.ang_jac1.gdot(mj_lambda1.angular);
+
+ let rhs = dlinvel + dangvel + self.rhs;
+ let total_impulse = (self.impulse + self.inv_lhs * rhs)
+ .simd_clamp(self.impulse_bounds[0], self.impulse_bounds[1]);
+ let delta_impulse = total_impulse - self.impulse;
+ self.impulse = total_impulse;
+
+ let lin_impulse = self.lin_jac * delta_impulse;
+ let ang_impulse1 = self.ang_jac1 * delta_impulse;
+ let ang_impulse2 = self.ang_jac2 * delta_impulse;
+
+ mj_lambda1.linear += lin_impulse * self.im1;
+ mj_lambda1.angular += ang_impulse1;
+ mj_lambda2.linear -= lin_impulse * self.im2;
+ mj_lambda2.angular -= ang_impulse2;
+ }
+
+ pub fn remove_bias_from_rhs(&mut self) {
+ self.rhs = self.rhs_wo_bias;
+ }
+}
+
+impl JointVelocityConstraint<Real, 1> {
+ pub fn lock_axes(
+ params: &IntegrationParameters,
+ joint_id: JointIndex,
+ body1: &SolverBody<Real, 1>,
+ body2: &SolverBody<Real, 1>,
+ frame1: &Isometry<Real>,
+ frame2: &Isometry<Real>,
+ joint: &JointData,
+ out: &mut [Self],
+ ) -> usize {
+ let mut len = 0;
+ let locked_axes = joint.locked_axes.bits();
+ let motor_axes = joint.motor_axes.bits();
+ let limit_axes = joint.limit_axes.bits();
+
+ let builder = JointVelocityConstraintBuilder::new(
+ frame1,
+ frame2,
+ &body1.world_com,
+ &body2.world_com,
+ locked_axes,
+ );
+
+ for i in 0..DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] =
+ builder.lock_linear(params, [joint_id], body1, body2, i, WritebackId::Dof(i));
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_angular(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+
+ for i in 0..DIM {
+ if motor_axes & (1 << i) != 0 {
+ out[len] = builder.motor_linear(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ locked_axes >> DIM,
+ i,
+ &joint.motors[i].motor_params(params.dt),
+ WritebackId::Motor(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if motor_axes & (1 << i) != 0 {
+ out[len] = builder.motor_angular(
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ &joint.motors[i].motor_params(params.dt),
+ WritebackId::Motor(i),
+ );
+ len += 1;
+ }
+ }
+
+ for i in 0..DIM {
+ if limit_axes & (1 << i) != 0 {
+ out[len] = builder.limit_linear(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i,
+ [joint.limits[i].min, joint.limits[i].max],
+ WritebackId::Limit(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if limit_axes & (1 << i) != 0 {
+ out[len] = builder.limit_angular(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ [joint.limits[i].min, joint.limits[i].max],
+ WritebackId::Limit(i),
+ );
+ len += 1;
+ }
+ }
+
+ JointVelocityConstraintBuilder::finalize_constraints(&mut out[..len]);
+ len
+ }
+
+ pub fn solve(&mut self, mj_lambdas: &mut [DeltaVel<Real>]) {
+ let mut mj_lambda1 = mj_lambdas[self.mj_lambda1[0] as usize];
+ let mut mj_lambda2 = mj_lambdas[self.mj_lambda2[0] as usize];
+
+ self.solve_generic(&mut mj_lambda1, &mut mj_lambda2);
+
+ mj_lambdas[self.mj_lambda1[0] as usize] = mj_lambda1;
+ mj_lambdas[self.mj_lambda2[0] as usize] = mj_lambda2;
+ }
+
+ pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
+ let joint = &mut joints_all[self.joint_id[0]].weight;
+ match self.writeback_id {
+ WritebackId::Dof(i) => joint.impulses[i] = self.impulse,
+ WritebackId::Limit(i) => joint.data.limits[i].impulse = self.impulse,
+ WritebackId::Motor(i) => joint.data.motors[i].impulse = self.impulse,
+ }
+ }
+}
+#[cfg(feature = "simd-is-enabled")]
+impl JointVelocityConstraint<SimdReal, SIMD_WIDTH> {
+ pub fn lock_axes(
+ params: &IntegrationParameters,
+ joint_id: [JointIndex; SIMD_WIDTH],
+ body1: &SolverBody<SimdReal, SIMD_WIDTH>,
+ body2: &SolverBody<SimdReal, SIMD_WIDTH>,
+ frame1: &Isometry<SimdReal>,
+ frame2: &Isometry<SimdReal>,
+ locked_axes: u8,
+ out: &mut [Self],
+ ) -> usize {
+ let builder = JointVelocityConstraintBuilder::new(
+ frame1,
+ frame2,
+ &body1.world_com,
+ &body2.world_com,
+ locked_axes,
+ );
+
+ let mut len = 0;
+ for i in 0..DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] =
+ builder.lock_linear(params, joint_id, body1, body2, i, WritebackId::Dof(i));
+ len += 1;
+ }
+ }
+
+ for i in DIM..SPATIAL_DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_angular(
+ params,
+ joint_id,
+ body1,
+ body2,
+ i - DIM,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+
+ JointVelocityConstraintBuilder::finalize_constraints(&mut out[..len]);
+ len
+ }
+
+ pub fn solve(&mut self, mj_lambdas: &mut [DeltaVel<Real>]) {
+ let mut mj_lambda1 = DeltaVel {
+ linear: Vector::from(gather![|ii| mj_lambdas[self.mj_lambda1[ii] as usize].linear]),
+ angular: AngVector::from(gather![
+ |ii| mj_lambdas[self.mj_lambda1[ii] as usize].angular
+ ]),
+ };
+ let mut mj_lambda2 = DeltaVel {
+ linear: Vector::from(gather![|ii| mj_lambdas[self.mj_lambda2[ii] as usize].linear]),
+ angular: AngVector::from(gather![
+ |ii| mj_lambdas[self.mj_lambda2[ii] as usize].angular
+ ]),
+ };
+
+ self.solve_generic(&mut mj_lambda1, &mut mj_lambda2);
+
+ for ii in 0..SIMD_WIDTH {
+ mj_lambdas[self.mj_lambda1[ii] as usize].linear = mj_lambda1.linear.extract(ii);
+ mj_lambdas[self.mj_lambda1[ii] as usize].angular = mj_lambda1.angular.extract(ii);
+ mj_lambdas[self.mj_lambda2[ii] as usize].linear = mj_lambda2.linear.extract(ii);
+ mj_lambdas[self.mj_lambda2[ii] as usize].angular = mj_lambda2.angular.extract(ii);
+ }
+ }
+
+ pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
+ let impulses: [_; SIMD_WIDTH] = self.impulse.into();
+
+ // TODO: should we move the iteration on ii deeper in the mested match?
+ for ii in 0..SIMD_WIDTH {
+ let joint = &mut joints_all[self.joint_id[ii]].weight;
+ match self.writeback_id {
+ WritebackId::Dof(i) => joint.impulses[i] = impulses[ii],
+ WritebackId::Limit(i) => joint.data.limits[i].impulse = impulses[ii],
+ WritebackId::Motor(i) => joint.data.motors[i].impulse = impulses[ii],
+ }
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct JointVelocityGroundConstraint<N: SimdRealField, const LANES: usize> {
+ pub mj_lambda2: [usize; LANES],
+
+ pub joint_id: [JointIndex; LANES],
+
+ pub impulse: N,
+ pub impulse_bounds: [N; 2],
+ pub lin_jac: Vector<N>,
+ pub ang_jac2: AngVector<N>,
+
+ pub inv_lhs: N,
+ pub rhs: N,
+ pub rhs_wo_bias: N,
+
+ pub im2: N,
+
+ pub writeback_id: WritebackId,
+}
+
+impl<N: WReal, const LANES: usize> JointVelocityGroundConstraint<N, LANES> {
+ pub fn invalid() -> Self {
+ Self {
+ mj_lambda2: [crate::INVALID_USIZE; LANES],
+ joint_id: [crate::INVALID_USIZE; LANES],
+ impulse: N::zero(),
+ impulse_bounds: [N::zero(), N::zero()],
+ lin_jac: Vector::zeros(),
+ ang_jac2: na::zero(),
+ inv_lhs: N::zero(),
+ rhs: N::zero(),
+ rhs_wo_bias: N::zero(),
+ im2: N::zero(),
+ writeback_id: WritebackId::Dof(0),
+ }
+ }
+
+ pub fn solve_generic(&mut self, mj_lambda2: &mut DeltaVel<N>) {
+ let dlinvel = mj_lambda2.linear;
+ let dangvel = mj_lambda2.angular;
+
+ let dvel = self.lin_jac.dot(&dlinvel) + self.ang_jac2.gdot(dangvel) + self.rhs;
+ let total_impulse = (self.impulse + self.inv_lhs * dvel)
+ .simd_clamp(self.impulse_bounds[0], self.impulse_bounds[1]);
+ let delta_impulse = total_impulse - self.impulse;
+ self.impulse = total_impulse;
+
+ let lin_impulse = self.lin_jac * delta_impulse;
+ let ang_impulse = self.ang_jac2 * delta_impulse;
+
+ mj_lambda2.linear -= lin_impulse * self.im2;
+ mj_lambda2.angular -= ang_impulse;
+ }
+
+ pub fn remove_bias_from_rhs(&mut self) {
+ self.rhs = self.rhs_wo_bias;
+ }
+}
+
+impl JointVelocityGroundConstraint<Real, 1> {
+ pub fn lock_axes(
+ params: &IntegrationParameters,
+ joint_id: JointIndex,
+ body1: &SolverBody<Real, 1>,
+ body2: &SolverBody<Real, 1>,
+ frame1: &Isometry<Real>,
+ frame2: &Isometry<Real>,
+ joint: &JointData,
+ out: &mut [Self],
+ ) -> usize {
+ let mut len = 0;
+ let locked_axes = joint.locked_axes.bits() as u8;
+ let motor_axes = joint.motor_axes.bits() as u8;
+ let limit_axes = joint.limit_axes.bits() as u8;
+
+ let builder = JointVelocityConstraintBuilder::new(
+ frame1,
+ frame2,
+ &body1.world_com,
+ &body2.world_com,
+ locked_axes,
+ );
+
+ for i in 0..DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_linear_ground(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_angular_ground(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+
+ for i in 0..DIM {
+ if motor_axes & (1 << i) != 0 {
+ out[len] = builder.motor_linear_ground(
+ [joint_id],
+ body1,
+ body2,
+ i,
+ &joint.motors[i].motor_params(params.dt),
+ WritebackId::Motor(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if motor_axes & (1 << i) != 0 {
+ out[len] = builder.motor_angular_ground(
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ &joint.motors[i].motor_params(params.dt),
+ WritebackId::Motor(i),
+ );
+ len += 1;
+ }
+ }
+
+ for i in 0..DIM {
+ if limit_axes & (1 << i) != 0 {
+ out[len] = builder.limit_linear_ground(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i,
+ [joint.limits[i].min, joint.limits[i].max],
+ WritebackId::Limit(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if limit_axes & (1 << i) != 0 {
+ out[len] = builder.limit_angular_ground(
+ params,
+ [joint_id],
+ body1,
+ body2,
+ i - DIM,
+ [joint.limits[i].min, joint.limits[i].max],
+ WritebackId::Limit(i),
+ );
+ len += 1;
+ }
+ }
+
+ JointVelocityConstraintBuilder::finalize_ground_constraints(&mut out[..len]);
+ len
+ }
+
+ pub fn solve(&mut self, mj_lambdas: &mut [DeltaVel<Real>]) {
+ let mut mj_lambda2 = mj_lambdas[self.mj_lambda2[0] as usize];
+ self.solve_generic(&mut mj_lambda2);
+ mj_lambdas[self.mj_lambda2[0] as usize] = mj_lambda2;
+ }
+
+ pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
+ let joint = &mut joints_all[self.joint_id[0]].weight;
+ match self.writeback_id {
+ WritebackId::Dof(i) => joint.impulses[i] = self.impulse,
+ WritebackId::Limit(i) => joint.data.limits[i].impulse = self.impulse,
+ WritebackId::Motor(i) => joint.data.motors[i].impulse = self.impulse,
+ }
+ }
+}
+
+#[cfg(feature = "simd-is-enabled")]
+impl JointVelocityGroundConstraint<SimdReal, SIMD_WIDTH> {
+ pub fn lock_axes(
+ params: &IntegrationParameters,
+ joint_id: [JointIndex; SIMD_WIDTH],
+ body1: &SolverBody<SimdReal, SIMD_WIDTH>,
+ body2: &SolverBody<SimdReal, SIMD_WIDTH>,
+ frame1: &Isometry<SimdReal>,
+ frame2: &Isometry<SimdReal>,
+ locked_axes: u8,
+ out: &mut [Self],
+ ) -> usize {
+ let mut len = 0;
+ let builder = JointVelocityConstraintBuilder::new(
+ frame1,
+ frame2,
+ &body1.world_com,
+ &body2.world_com,
+ locked_axes,
+ );
+
+ for i in 0..DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_linear_ground(
+ params,
+ joint_id,
+ body1,
+ body2,
+ i,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+ for i in DIM..SPATIAL_DIM {
+ if locked_axes & (1 << i) != 0 {
+ out[len] = builder.lock_angular_ground(
+ params,
+ joint_id,
+ body1,
+ body2,
+ i - DIM,
+ WritebackId::Dof(i),
+ );
+ len += 1;
+ }
+ }
+
+ JointVelocityConstraintBuilder::finalize_ground_constraints(&mut out[..len]);
+ len
+ }
+
+ pub fn solve(&mut self, mj_lambdas: &mut [DeltaVel<Real>]) {
+ let mut mj_lambda2 = DeltaVel {
+ linear: Vector::from(gather![|ii| mj_lambdas[self.mj_lambda2[ii] as usize].linear]),
+ angular: AngVector::from(gather![
+ |ii| mj_lambdas[self.mj_lambda2[ii] as usize].angular
+ ]),
+ };
+
+ self.solve_generic(&mut mj_lambda2);
+
+ for ii in 0..SIMD_WIDTH {
+ mj_lambdas[self.mj_lambda2[ii] as usize].linear = mj_lambda2.linear.extract(ii);
+ mj_lambdas[self.mj_lambda2[ii] as usize].angular = mj_lambda2.angular.extract(ii);
+ }
+ }
+
+ pub fn writeback_impulses(&self, joints_all: &mut [JointGraphEdge]) {
+ let impulses: [_; SIMD_WIDTH] = self.impulse.into();
+
+ // TODO: should we move the iteration on ii deeper in the mested match?
+ for ii in 0..SIMD_WIDTH {
+ let joint = &mut joints_all[self.joint_id[ii]].weight;
+ match self.writeback_id {
+ WritebackId::Dof(i) => joint.impulses[i] = impulses[ii],
+ WritebackId::Limit(i) => joint.data.limits[i].impulse = impulses[ii],
+ WritebackId::Motor(i) => joint.data.motors[i].impulse = impulses[ii],
+ }
+ }
+ }
+}