void Tire::findSigmaHatAlphaHat(btScalar load, btScalar & output_sigmahat, btScalar & output_alphahat, int iterations) { btScalar x, y, ymax, junk; ymax = 0; for (x = -2; x < 2; x += 4.0/iterations) { y = PacejkaFx(x, load, 1.0, junk); if (y > ymax) { output_sigmahat = x; ymax = y; } } ymax = 0; for (x = -20; x < 20; x += 40.0/iterations) { y = PacejkaFy(x, load, 0.0, 1.0, junk); if (y > ymax) { output_alphahat = x; ymax = y; } } }
void Tire::findSigmaHatAlphaHat( btScalar load, btScalar & output_sigmahat, btScalar & output_alphahat, int iterations) { btScalar Fz = load; btScalar Fz0 = nominal_load; btScalar dFz = (Fz - Fz0) / Fz0; btScalar camber = 0.0; btScalar mu = 1.0; btScalar Dy, BCy, Shf; // unused btScalar Fxmax = 0.0; btScalar smax = 2.0; for (btScalar s = -smax; s < smax; s += 2 * smax / iterations) { btScalar Fx = PacejkaFx(s, Fz, dFz, mu); if (Fx > Fxmax) { output_sigmahat = btFabs(s); Fxmax = Fx; } } btScalar Fymax = 0.0; btScalar amax = 30.0 * (M_PI / 180.0); for (btScalar a = -amax; a < amax; a += 2 * amax / iterations) { btScalar Fy = PacejkaFy(a, camber, Fz, dFz, mu, Dy, BCy, Shf); if (Fy > Fymax) { output_alphahat = btFabs(a); Fymax = Fy; } } }
btVector3 Tire::getForce( btScalar normal_force, btScalar friction_coeff, btScalar inclination, btScalar ang_velocity, btScalar lon_velocity, btScalar lat_velocity) { if (normal_force < 1E-3 || friction_coeff < 1E-3) { slide = slip = 0; ideal_slide = ideal_slip = 1; fx = fy = fz = mz = 0; vx = vy = 0; return btVector3(0, 0, 0); } // pacejka in kN normal_force = normal_force * 1E-3; // limit input btClamp(normal_force, btScalar(0), btScalar(max_load)); btClamp(inclination, -max_camber, max_camber); // get ideal slip ratio btScalar sigma_hat(0); btScalar alpha_hat(0); getSigmaHatAlphaHat(normal_force, sigma_hat, alpha_hat); btScalar Fz = normal_force; btScalar gamma = inclination; // positive when tire top tilts to the right, viewed from rear btScalar denom = btMax(btFabs(lon_velocity), btScalar(1E-3)); btScalar lon_slip_velocity = lon_velocity - ang_velocity; btScalar sigma = -lon_slip_velocity / denom; // longitudinal slip: negative in braking, positive in traction btScalar tan_alpha = lat_velocity / denom; btScalar alpha = -atan(tan_alpha) * 180.0 / M_PI; // sideslip angle: positive in a right turn(opposite to SAE tire coords) btScalar max_Fx(0), max_Fy(0), max_Mz(0); // combining method 1: beckman method for pre-combining longitudinal and lateral forces // seems to be a variant of Bakker 1987: // refined Kamm Circle for non-isotropic tire characteristics // and slip normalization to guarantee simultaneous sliding btScalar s = sigma / sigma_hat; btScalar a = alpha / alpha_hat; btScalar rho = btMax(btSqrt(s * s + a * a), btScalar(1E-4)); // normalized slip btScalar Fx = (s / rho) * PacejkaFx(rho * sigma_hat, Fz, friction_coeff, max_Fx); btScalar Fy = (a / rho) * PacejkaFy(rho * alpha_hat, Fz, gamma, friction_coeff, max_Fy); // Bakker (with unknown factor e(rho) to get the correct direction for large slip): // btScalar Fx = -s * PacejkaFx(rho * sigma_hat, Fz, friction_coeff, max_Fx); // btScalar Fy = -a * e(rho) * PacejkaFy(rho * alpha_hat, Fz, gamma, friction_coeff, max_Fy); /* // combining method 2: orangutan btScalar Fx = PacejkaFx(sigma, Fz, friction_coeff, max_Fx); btScalar Fy = PacejkaFy(alpha, Fz, gamma, friction_coeff, max_Fy); btScalar one = (sigma < 0.0) ? -1.0 : 1.0; btScalar pure_sigma = sigma / (one + sigma); btScalar pure_alpha = -tan_alpha / (one + sigma); btScalar pure_combined = sqrt(pure_sigma * pure_sigma + pure_alpha * pure_alpha); btScalar kappa_combined = pure_combined / (1.0 - pure_combined); btScalar alpha_combined = atan((one + sigma) * pure_combined); btScalar Flimitx = PacejkaFx(kappa_combined, Fz, friction_coeff, max_Fx); btScalar Flimity = PacejkaFy(alpha_combined * 180.0 / M_PI, Fz, gamma, friction_coeff, max_Fy); btScalar x = fabs(Fx) / (fabs(Fx) + fabs(Fy)); btScalar y = 1.0 - x; btScalar Flimit = (fabs(x * Flimitx) + fabs(y * Flimity)); btScalar Fmag = sqrt(Fx * Fx + Fy * Fy); if (Fmag > Flimit) { btScalar scale = Flimit / Fmag; Fx *= scale; Fy *= scale; } */ /* // combining method 3: traction circle btScalar Fx = PacejkaFx(sigma, Fz, friction_coeff, max_Fx); btScalar Fy = PacejkaFy(alpha, Fz, gamma, friction_coeff, max_Fy); // determine to what extent the tires are long (x) gripping vs lat (y) gripping // 1.0 when Fy is zero, 0.0 when Fx is zero btScalar longfactor = 1.0; btScalar combforce = btFabs(Fx) + btFabs(Fy); if (combforce > 1) longfactor = btFabs(Fx) / combforce; // determine the maximum force for this amount of long vs lat grip by linear interpolation btScalar maxforce = btFabs(max_Fx) * longfactor + (1.0 - longfactor) * btFabs(max_Fy); // scale down forces to fit into the maximum if (combforce > maxforce) { Fx *= maxforce / combforce; Fy *= maxforce / combforce; } */ /* // combining method 4: traction ellipse (prioritize Fx) // issue: assumes that adhesion limits are fully reached for combined forces btScalar Fx = PacejkaFx(sigma, Fz, friction_coeff, max_Fx); btScalar Fy = PacejkaFy(alpha, Fz, gamma, friction_coeff, max_Fy); if (Fx < max_Fx) { Fy = Fy * sqrt(1.0 - (Fx / max_Fx) * (Fx / max_Fx)); } else { Fx = max_Fx; Fy = 0; } */ /* // combining method 5: traction ellipse (prioritize Fy) // issue: assumes that adhesion limits are fully reached for combined forces btScalar Fx = PacejkaFx(sigma, Fz, friction_coeff, max_Fx); btScalar Fy = PacejkaFy(alpha, Fz, gamma, friction_coeff, max_Fy); if (Fy < max_Fy) { Fx = Fx * sqrt(1.0 - (Fy / max_Fy) * (Fy / max_Fy)); } else { Fy = max_Fy; Fx = 0; } */ /* // no combining btScalar Fx = PacejkaFx(sigma, Fz, friction_coeff, max_Fx); btScalar Fy = PacejkaFy(alpha, Fz, gamma, friction_coeff, max_Fy); */ btScalar Mz = PacejkaMz(sigma, alpha, Fz, gamma, friction_coeff, max_Mz); camber = inclination; slide = sigma; slip = alpha; ideal_slide = sigma_hat; ideal_slip = alpha_hat; fx = Fx; fy = Fy; fz = Fz; mz = Mz; vx = lon_slip_velocity; vy = lat_velocity; // Fx positive during traction, Fy positive in a right turn, Mz positive in a left turn return btVector3(Fx, Fy, Mz); }
btVector3 Tire::getForce( btScalar normal_load, btScalar friction_coeff, btScalar camber, btScalar rot_velocity, btScalar lon_velocity, btScalar lat_velocity) { if (normal_load * friction_coeff < btScalar(1E-6)) { slip = slip_angle = 0; ideal_slip = ideal_slip_angle = 1; fx = fy = fz = mz = 0; vx = vy = 0; return btVector3(0, 0, 0); } // limit input normal_load = Clamp(normal_load, btScalar(0), btScalar(max_load)); camber = Clamp(camber, -max_camber, max_camber); // sigma and alpha btScalar denom = Max(btFabs(lon_velocity), btScalar(1E-3)); btScalar lon_slip_velocity = lon_velocity - rot_velocity; btScalar sigma = -lon_slip_velocity / denom; btScalar alpha = btAtan(lat_velocity / denom); // force parameters btScalar Fz = normal_load; btScalar Fz0 = nominal_load; btScalar dFz = (Fz - Fz0) / Fz0; // pure slip btScalar Dy, BCy, Shf; btScalar Fx0 = PacejkaFx(sigma, Fz, dFz, friction_coeff); btScalar Fy0 = PacejkaFy(alpha, camber, Fz, dFz, friction_coeff, Dy, BCy, Shf); btScalar Mz0 = PacejkaMz(alpha, camber, Fz, dFz, friction_coeff, Fy0, BCy, Shf); // combined slip btScalar Gx = PacejkaGx(sigma, alpha); btScalar Gy = PacejkaGy(sigma, alpha); btScalar Svy = PacejkaSvy(sigma, alpha, camber, dFz, Dy); btScalar Fx = Gx * Fx0; btScalar Fy = Gy * Fy0 + Svy; // ideal slip and angle btScalar sigma_hat(0), alpha_hat(0); getSigmaHatAlphaHat(normal_load, sigma_hat, alpha_hat); slip = sigma; slip_angle = alpha; ideal_slip = sigma_hat; ideal_slip_angle = alpha_hat; fx = Fx; fy = Fy; fz = Fz; mz = Mz0; vx = lon_slip_velocity; vy = lat_velocity; return btVector3(Fx, Fy, Mz0); }