Merge pull request #1301 from carla-simulator/new_expose_vehicle_physics

New expose vehicle physics
This commit is contained in:
Néstor Subirón 2019-02-27 13:14:04 +01:00 committed by GitHub
commit d39bb39688
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1118 additions and 4 deletions

View File

@ -3,6 +3,7 @@
* Fixed `manual_control.py` and `no_rendering_mode.py` to prevent crashes when used in "no rendering mode"
* Added movable props present in the map (e.g. chairs and tables) as actors so they can be controlled from Python
* Refactored `no_rendering_mode.py` to improve performance and interface
* Exposed minimum physics control parameters for vehicles and wheels.
* Several improvements to the basic build system for Windows
* Improved time-out related error messages
* Fixed issue of retrieving an empty list when calling `world.get_actors()` right after creating the world

View File

@ -197,6 +197,34 @@
- `__eq__(other)`
- `__ne__(other)`
## `carla.WheelsPhysicsControl`
- `tire_friction`
- `damping_rate`
- `steer_angle`
- `disable_steering`
- `__eq__(other)`
- `__ne__(other)`
## `carla.VehiclePhysicsControl`
- `torque_curve`
- `max_rpm`
- `moi`
- `damping_rate_full_throttle`
- `damping_rate_zero_throttle_clutch_engaged`
- `damping_rate_zero_throttle_clutch_disengaged`
- `use_gear_autobox`
- `gear_switch_time`
- `clutch_strength`
- `mass`
- `drag_coefficient`
- `center_of_mass`
- `steering_curve`
- `wheels`
- `__eq__(other)`
- `__ne__(other)`
## `carla.Map`
- `name`

View File

@ -179,7 +179,7 @@ by providing throttle, break, and steer values
vehicle.apply_control(carla.VehicleControl(throttle=1.0, steer=-1.0))
```
These are all the parameters of the VehicleControl object and their default
These are all the parameters of the `VehicleControl` object and their default
values
```py
@ -193,6 +193,62 @@ carla.VehicleControl(
gear = 0)
```
Also, physics control properties can be tuned for vehicles and its wheels
```py
vehicle.apply_physics_control(carla.VehiclePhysicsControl(max_rpm = 5000.0, center_of_mass = carla.Vector3D(0.0, 0.0, 0.0), torque_curve=[[0,400],[5000,400]]))
```
These properties are controlled through a `VehiclePhysicsControl` object, which also contains a property to control each wheel's physics through a `WheelPhysicsControl` object.
```py
carla.VehiclePhysicsControl(
torque_curve,
max_rpm,
moi,
damping_rate_full_throttle,
damping_rate_zero_throttle_clutch_engaged,
damping_rate_zero_throttle_clutch_disengaged,
use_gear_autobox,
gear_switch_time,
clutch_strength,
mass,
drag_coefficient,
center_of_mass,
steering_curve,
wheels)
```
Where:
- *torque_curve*: Curve that indicates the torque measured in Nm for a specific revolutions per minute of the vehicle's engine
- *max_rpm*: The maximum revolutions per minute of the vehicle's engine
- *moi*: The moment of inertia of the vehicle's engine
- *damping_rate_full_throttle*: Damping rate when the throttle is maximum.
- *damping_rate_zero_throttle_clutch_engaged*: Damping rate when the thottle is zero with clutch engaged
- *damping_rate_zero_throttle_clutch_disengaged*: Damping rate when the thottle is zero with clutch disengaged
- *use_gear_autobox*: If true, the vehicle will have automatic transmission
- *gear_switch_time*: Switching time between gears
- *clutch_strength*: The clutch strength of the vehicle. Measured in Kgm^2/s
- *mass*: The mass of the vehicle measured in Kg
- *drag_coefficient*: Drag coefficient of the vehicle's chassis
- *center_of_mass*: The center of mass of the vehicle
- *steering_curve*: Curve that indicates the maximum steering for a specific forward speed
- *wheels*: List of `WheelPhysicsControl` objects.
```py
carla.WheelPhysicsControl(
tire_friction,
damping_rate,
steer_angle,
disable_steering)
```
Where:
- *tire_friction*: Scalar value that indicates the friction of the wheel.
- *damping_rate*: The damping rate of the wheel.
- *steer_angle*: The maximum angle in degrees that the wheel can steer.
- *disable_steering*: If true, the wheel will not steer.
Our vehicles also come with a handy autopilot
```py

View File

@ -25,10 +25,18 @@ namespace client {
}
}
void Vehicle::ApplyPhysicsControl(const PhysicsControl &physics_control) {
GetEpisode().Lock()->ApplyPhysicsControlToVehicle(*this, physics_control);
}
Vehicle::Control Vehicle::GetControl() const {
return GetEpisode().Lock()->GetActorDynamicState(*this).state.vehicle_data.control;
}
Vehicle::PhysicsControl Vehicle::GetPhysicsControl() const {
return GetEpisode().Lock()->GetVehiclePhysicsControl(*this);
}
float Vehicle::GetSpeedLimit() const {
return GetEpisode().Lock()->GetActorDynamicState(*this).state.vehicle_data.speed_limit;
}

View File

@ -8,6 +8,7 @@
#include "carla/client/Actor.h"
#include "carla/rpc/VehicleControl.h"
#include "carla/rpc/VehiclePhysicsControl.h"
#include "carla/rpc/TrafficLightState.h"
namespace carla {
@ -18,6 +19,7 @@ namespace client {
public:
using Control = rpc::VehicleControl;
using PhysicsControl = rpc::VehiclePhysicsControl;
explicit Vehicle(ActorInitializer init) : Actor(std::move(init)) {}
@ -27,6 +29,9 @@ namespace client {
/// Apply @a control to this vehicle.
void ApplyControl(const Control &control);
/// Apply physics control to this vehicle
void ApplyPhysicsControl(const PhysicsControl &physics_control);
/// Return the control last applied to this vehicle.
///
/// @note The following functions do not call the simulator, they return the
@ -34,6 +39,7 @@ namespace client {
/// received in the last tick.
//////////////////////////////////////////////////////////////////////////////////
Control GetControl() const;
PhysicsControl GetPhysicsControl() const;
float GetSpeedLimit() const;
@ -43,6 +49,7 @@ namespace client {
SharedPtr<TrafficLight> GetTrafficLight() const;
private:
Control _control;

View File

@ -47,8 +47,8 @@ namespace client {
SharedPtr<ActorList> World::GetActors() const {
return SharedPtr<ActorList>{new ActorList{
_episode,
_episode.Lock()->GetAllTheActorsInTheEpisode()}};
_episode,
_episode.Lock()->GetAllTheActorsInTheEpisode()}};
}
SharedPtr<Actor> World::SpawnActor(

View File

@ -12,6 +12,8 @@
#include "carla/client/Timestamp.h"
#include "carla/client/detail/EpisodeProxy.h"
#include "carla/geom/Transform.h"
#include "carla/rpc/Actor.h"
#include "carla/rpc/VehiclePhysicsControl.h"
#include "carla/rpc/WeatherParameters.h"
namespace carla {

View File

@ -153,6 +153,16 @@ namespace detail {
return _pimpl->CallAndWait<return_t>("get_actors_by_id", ids);
}
rpc::VehiclePhysicsControl Client::GetVehiclePhysicsControl(const rpc::Actor &actor) const {
return _pimpl->CallAndWait<carla::rpc::VehiclePhysicsControl>("get_physics_control", actor);
}
void Client::ApplyPhysicsControlToVehicle(
const rpc::Actor &actor,
const rpc::VehiclePhysicsControl &physicsControl) {
return _pimpl->AsyncCall("apply_physics_control", actor, physicsControl);
}
rpc::Actor Client::SpawnActor(
const rpc::ActorDescription &description,
const geom::Transform &transform) {

View File

@ -16,6 +16,7 @@
#include "carla/rpc/MapInfo.h"
#include "carla/rpc/WeatherParameters.h"
#include "carla/rpc/TrafficLightState.h"
#include "carla/rpc/VehiclePhysicsControl.h"
#include <functional>
#include <memory>
@ -81,6 +82,9 @@ namespace detail {
std::vector<rpc::Actor> GetActorsById(const std::vector<actor_id_type> &ids);
rpc::VehiclePhysicsControl GetVehiclePhysicsControl(const rpc::Actor &actor) const;
rpc::Actor SpawnActor(
const rpc::ActorDescription &description,
const geom::Transform &transform);
@ -116,6 +120,10 @@ namespace detail {
const rpc::Actor &walker,
const rpc::WalkerControl &control);
void ApplyPhysicsControlToVehicle(
const rpc::Actor &vehicle,
const rpc::VehiclePhysicsControl &physicsControl);
void SetTrafficLightState(
const rpc::Actor &trafficLight,
const rpc::TrafficLightState trafficLightState);

View File

@ -132,6 +132,10 @@ namespace detail {
_client.SetWeatherParameters(weather);
}
rpc::VehiclePhysicsControl GetVehiclePhysicsControl(const Vehicle &vehicle) const {
return _client.GetVehiclePhysicsControl(vehicle.Serialize());
}
/// @}
// =========================================================================
/// @name General operations with actors
@ -222,6 +226,9 @@ namespace detail {
_client.ApplyControlToWalker(walker.Serialize(), control);
}
void ApplyPhysicsControlToVehicle(Vehicle &vehicle, const rpc::VehiclePhysicsControl &physicsControl) {
_client.ApplyPhysicsControlToVehicle(vehicle.Serialize(), physicsControl);
}
/// @}
// =========================================================================
/// @name Operations with sensors

View File

@ -0,0 +1,160 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include <cmath>
#include <limits>
namespace carla {
namespace geom {
class Vector2D {
public:
// =========================================================================
// -- Public data members --------------------------------------------------
// =========================================================================
float x = 0.0f;
float y = 0.0f;
// =========================================================================
// -- Constructors ---------------------------------------------------------
// =========================================================================
Vector2D() = default;
Vector2D(const Vector2D &) = default;
Vector2D(float ix, float iy)
: x(ix),
y(iy) {}
// =========================================================================
// -- Other methods --------------------------------------------------------
// =========================================================================
double SquaredLength() const {
return x * x + y * y;
}
double Length() const {
return std::sqrt(SquaredLength());
}
Vector2D MakeUnitVector() const {
const double len = Length();
DEBUG_ASSERT(len > std::numeric_limits<double>::epsilon());
double k = 1.0 / len;
return Vector2D(x * k, y * k);
}
// =========================================================================
// -- Arithmetic operators -------------------------------------------------
// =========================================================================
Vector2D &operator+=(const Vector2D &rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
friend Vector2D operator+(Vector2D lhs, const Vector2D &rhs) {
lhs += rhs;
return lhs;
}
Vector2D &operator-=(const Vector2D &rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend Vector2D operator-(Vector2D lhs, const Vector2D &rhs) {
lhs -= rhs;
return lhs;
}
Vector2D &operator*=(const double &rhs) {
x *= rhs;
y *= rhs;
return *this;
}
friend Vector2D operator*(Vector2D lhs, const double &rhs) {
lhs *= rhs;
return lhs;
}
friend Vector2D operator*(const double &lhs, Vector2D rhs) {
rhs *= lhs;
return rhs;
}
Vector2D &operator/=(const double &rhs) {
x /= rhs;
y /= rhs;
return *this;
}
friend Vector2D operator/(Vector2D lhs, const double &rhs) {
lhs /= rhs;
return lhs;
}
friend Vector2D operator/(const double &lhs, Vector2D rhs) {
rhs /= lhs;
return rhs;
}
// =========================================================================
// -- Comparison operators -------------------------------------------------
// =========================================================================
bool operator==(const Vector2D &rhs) const {
return (x == rhs.x) && (y == rhs.y);
}
bool operator!=(const Vector2D &rhs) const {
return !(*this == rhs);
}
// =========================================================================
// -- Conversions to UE4 types ---------------------------------------------
// =========================================================================
#ifdef LIBCARLA_INCLUDED_FROM_UE4
Vector2D(const FVector2D &vector)
: Vector2D(vector.X, vector.Y) {}
Vector2D &ToMeters(void) { // from centimeters to meters.
x *= 0.001f;
y *= 0.001f;
return *this;
}
Vector2D &ToCentimeters(void) { // from meters to centimeters.
x *= 100.0f;
y *= 100.0f;
return *this;
}
operator FVector2D() const {
return FVector2D{x, y};
}
#endif // LIBCARLA_INCLUDED_FROM_UE4
MSGPACK_DEFINE_ARRAY(x, y)
};
} // namespace geom
} // namespace carla

View File

@ -0,0 +1,17 @@
// Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/geom/Vector2D.h"
namespace carla {
namespace rpc {
using Vector2D = geom::Vector2D;
} // namespace rpc
} // namespace carla

View File

@ -0,0 +1,222 @@
// Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include "carla/rpc/Vector2D.h"
#include "carla/rpc/WheelPhysicsControl.h"
#include <string>
#include <vector>
namespace carla {
namespace rpc {
class VehiclePhysicsControl {
public:
VehiclePhysicsControl() = default;
VehiclePhysicsControl(
const std::vector<carla::geom::Vector2D> &in_torque_curve,
float in_max_rpm,
float in_moi,
float in_damping_rate_full_throttle,
float in_damping_rate_zero_throttle_clutch_engaged,
float in_damping_rate_zero_throttle_clutch_disengaged,
bool in_use_gear_autobox,
float in_gear_switch_time,
float in_clutch_strength,
float in_mass,
float in_drag_coefficient,
geom::Vector3D in_center_of_mass,
const std::vector<carla::geom::Vector2D> &in_steering_curve,
std::vector<WheelPhysicsControl> &in_wheels)
: torque_curve(in_torque_curve),
max_rpm(in_max_rpm),
moi(in_moi),
damping_rate_full_throttle(in_damping_rate_full_throttle),
damping_rate_zero_throttle_clutch_engaged(in_damping_rate_zero_throttle_clutch_engaged),
damping_rate_zero_throttle_clutch_disengaged(in_damping_rate_zero_throttle_clutch_disengaged),
use_gear_autobox(in_use_gear_autobox),
gear_switch_time(in_gear_switch_time),
clutch_strength(in_clutch_strength),
mass(in_mass),
drag_coefficient(in_drag_coefficient),
center_of_mass(in_center_of_mass),
steering_curve(in_steering_curve),
wheels(in_wheels) {}
const std::vector<WheelPhysicsControl> &GetWheels() const {
return wheels;
}
void SetWheels(std::vector<WheelPhysicsControl> &in_wheels) {
wheels = in_wheels;
}
const std::vector<geom::Vector2D> &GetTorqueCurve() const {
return torque_curve;
}
void SetTorqueCurve(std::vector<geom::Vector2D> &in_torque_curve) {
torque_curve = in_torque_curve;
}
const std::vector<geom::Vector2D> &GetSteeringCurve() const {
return steering_curve;
}
void SetSteeringCurve(std::vector<geom::Vector2D> &in_steering_curve) {
steering_curve = in_steering_curve;
}
std::vector<geom::Vector2D> torque_curve = {geom::Vector2D(0.0f, 500.0f), geom::Vector2D(5000.0f, 500.0f)};
float max_rpm = 5000.0f;
float moi = 1.0f;
float damping_rate_full_throttle = 0.15f;
float damping_rate_zero_throttle_clutch_engaged = 2.0f;
float damping_rate_zero_throttle_clutch_disengaged = 0.35f;
bool use_gear_autobox = true;
float gear_switch_time = 0.5f;
float clutch_strength = 10.0f;
float mass = 1000.0f;
float drag_coefficient = 0.3f;
geom::Vector3D center_of_mass;
std::vector<geom::Vector2D> steering_curve = {geom::Vector2D(0.0f, 1.0f), geom::Vector2D(10.0f, 0.5f)};
std::vector<WheelPhysicsControl> wheels;
bool operator!=(const VehiclePhysicsControl &rhs) const {
return
max_rpm != rhs.max_rpm ||
moi != rhs.moi ||
damping_rate_full_throttle != rhs.damping_rate_full_throttle ||
damping_rate_zero_throttle_clutch_engaged != rhs.damping_rate_zero_throttle_clutch_engaged ||
damping_rate_zero_throttle_clutch_disengaged != rhs.damping_rate_zero_throttle_clutch_disengaged ||
use_gear_autobox != rhs.use_gear_autobox ||
gear_switch_time != rhs.gear_switch_time ||
clutch_strength != rhs.clutch_strength ||
mass != rhs.mass ||
drag_coefficient != rhs.drag_coefficient ||
steering_curve != rhs.steering_curve ||
center_of_mass != rhs.center_of_mass ||
wheels != rhs.wheels;
}
bool operator==(const VehiclePhysicsControl &rhs) const {
return !(*this != rhs);
}
#ifdef LIBCARLA_INCLUDED_FROM_UE4
VehiclePhysicsControl(const FVehiclePhysicsControl &Control) {
// Engine Setup
torque_curve = std::vector<carla::geom::Vector2D>();
TArray<FRichCurveKey> TorqueCurveKeys = Control.TorqueCurve.GetCopyOfKeys();
for (int32 KeyIdx = 0; KeyIdx < TorqueCurveKeys.Num(); KeyIdx++) {
geom::Vector2D point(TorqueCurveKeys[KeyIdx].Time, TorqueCurveKeys[KeyIdx].Value);
torque_curve.push_back(point);
}
max_rpm = Control.MaxRPM;
moi = Control.MOI;
damping_rate_full_throttle = Control.DampingRateFullThrottle;
damping_rate_zero_throttle_clutch_engaged = Control.DampingRateZeroThrottleClutchEngaged;
damping_rate_zero_throttle_clutch_disengaged = Control.DampingRateZeroThrottleClutchDisengaged;
// Transmission Setup
use_gear_autobox = Control.bUseGearAutoBox;
gear_switch_time = Control.GearSwitchTime;
clutch_strength = Control.ClutchStrength;
// Vehicle Setup
mass = Control.Mass;
drag_coefficient = Control.DragCoefficient;
steering_curve = std::vector<carla::geom::Vector2D>();
TArray<FRichCurveKey> SteeringCurveKeys = Control.SteeringCurve.GetCopyOfKeys();
for (int32 KeyIdx = 0; KeyIdx < SteeringCurveKeys.Num(); KeyIdx++) {
geom::Vector2D point(SteeringCurveKeys[KeyIdx].Time, SteeringCurveKeys[KeyIdx].Value);
steering_curve.push_back(point);
}
center_of_mass = Control.CenterOfMass;
// Wheels Setup
wheels = std::vector<WheelPhysicsControl>();
for (auto Wheel : Control.Wheels) {
wheels.push_back(WheelPhysicsControl(Wheel));
}
}
operator FVehiclePhysicsControl() const {
FVehiclePhysicsControl Control;
// Engine Setup
FRichCurve TorqueCurve;
for (auto point : torque_curve) {
TorqueCurve.AddKey(point.x, point.y);
}
Control.TorqueCurve = TorqueCurve;
Control.MaxRPM = max_rpm;
Control.MOI = moi;
Control.DampingRateFullThrottle = damping_rate_full_throttle;
Control.DampingRateZeroThrottleClutchEngaged = damping_rate_zero_throttle_clutch_engaged;
Control.DampingRateZeroThrottleClutchDisengaged = damping_rate_zero_throttle_clutch_disengaged;
// Transmission Setup
Control.bUseGearAutoBox = use_gear_autobox;
Control.GearSwitchTime = gear_switch_time;
Control.ClutchStrength = clutch_strength;
// Vehicle Setup
Control.Mass = mass;
Control.DragCoefficient = drag_coefficient;
// Transmission Setup
FRichCurve SteeringCurve;
for (auto point : steering_curve) {
SteeringCurve.AddKey(point.x, point.y);
}
Control.SteeringCurve = SteeringCurve;
Control.CenterOfMass = center_of_mass;
// Wheels Setup
TArray<FWheelPhysicsControl> Wheels;
for (auto wheel : wheels) {
Wheels.Add(FWheelPhysicsControl(wheel));
}
Control.Wheels = Wheels;
return Control;
}
#endif
MSGPACK_DEFINE_ARRAY(torque_curve,
max_rpm,
moi,
damping_rate_full_throttle,
damping_rate_zero_throttle_clutch_engaged,
damping_rate_zero_throttle_clutch_disengaged,
use_gear_autobox,
gear_switch_time,
clutch_strength,
mass,
drag_coefficient,
center_of_mass,
steering_curve,
wheels);
};
} // namespace rpc
} // namespace carla

View File

@ -0,0 +1,70 @@
// Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
namespace carla {
namespace rpc {
class WheelPhysicsControl {
public:
WheelPhysicsControl() = default;
WheelPhysicsControl(
float in_tire_friction,
float in_damping_rate,
float in_steer_angle,
bool in_disable_steering)
: tire_friction(in_tire_friction),
damping_rate(in_damping_rate),
steer_angle(in_steer_angle),
disable_steering(in_disable_steering) {}
float tire_friction = 2.0f;
float damping_rate = 0.25f;
float steer_angle = 70.0f;
bool disable_steering = false;
bool operator!=(const WheelPhysicsControl &rhs) const {
return
tire_friction != rhs.tire_friction ||
damping_rate != rhs.damping_rate ||
steer_angle != rhs.steer_angle ||
disable_steering != rhs.disable_steering;
}
bool operator==(const WheelPhysicsControl &rhs) const {
return !(*this != rhs);
}
#ifdef LIBCARLA_INCLUDED_FROM_UE4
WheelPhysicsControl(const FWheelPhysicsControl &Wheel)
: tire_friction(Wheel.TireFriction),
damping_rate(Wheel.DampingRate),
steer_angle(Wheel.SteerAngle),
disable_steering(Wheel.bDisableSteering) {}
operator FWheelPhysicsControl() const {
FWheelPhysicsControl Wheel;
Wheel.TireFriction = tire_friction;
Wheel.DampingRate = damping_rate;
Wheel.SteerAngle = steer_angle;
Wheel.bDisableSteering = disable_steering;
return Wheel;
}
#endif
MSGPACK_DEFINE_ARRAY(tire_friction,
damping_rate,
steer_angle,
disable_steering)
};
}
}

View File

@ -78,6 +78,8 @@ void export_actor() {
.add_property("bounding_box", CALL_RETURNING_COPY(cc::Vehicle, GetBoundingBox))
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
.def("get_control", &cc::Vehicle::GetControl)
.def("apply_physics_control", &cc::Vehicle::ApplyPhysicsControl, (arg("physics_control")))
.def("get_physics_control", CONST_CALL_WITHOUT_GIL(cc::Vehicle, GetPhysicsControl))
.def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled") = true))
.def("get_speed_limit", &cc::Vehicle::GetSpeedLimit)
.def("get_traffic_light_state", &cc::Vehicle::GetTrafficLightState)

View File

@ -5,6 +5,8 @@
// For a copy, see <https://opensource.org/licenses/MIT>.
#include <carla/rpc/VehicleControl.h>
#include <carla/rpc/VehiclePhysicsControl.h>
#include <carla/rpc/WheelPhysicsControl.h>
#include <carla/rpc/WalkerControl.h>
#include <ostream>
@ -34,9 +36,131 @@ namespace rpc {
return out;
}
std::ostream &operator<<(std::ostream &out, const WheelPhysicsControl &control) {
out << "WheelPhysicsControl(tire_friction=" << control.tire_friction
<< ", damping_rate=" << control.damping_rate
<< ", steer_angle=" << control.steer_angle
<< ", disable_steering=" << boolalpha(control.disable_steering) << ')';
return out;
}
std::ostream &operator<<(std::ostream &out, const VehiclePhysicsControl &control) {
out << "VehiclePhysicsControl(torque_curve=" << control.torque_curve
<< ", max_rpm=" << control.max_rpm
<< ", moi=" << control.moi
<< ", damping_rate_full_throttle=" << control.damping_rate_full_throttle
<< ", damping_rate_zero_throttle_clutch_engaged=" << control.damping_rate_zero_throttle_clutch_engaged
<< ", damping_rate_zero_throttle_clutch_disengaged=" << control.damping_rate_zero_throttle_clutch_disengaged
<< ", use_gear_autobox=" << boolalpha(control.use_gear_autobox)
<< ", gear_switch_time=" << control.gear_switch_time
<< ", clutch_strength=" << control.clutch_strength
<< ", mass=" << control.mass
<< ", drag_coefficient=" << control.drag_coefficient
<< ", center_of_mass=" << control.center_of_mass
<< ", steering_curve=" << control.steering_curve
<< ", wheels=" << control.wheels << ')';
return out;
}
} // namespace rpc
} // namespace carla
static auto GetVectorOfVector2DFromList(const boost::python::list &list) {
std::vector<carla::geom::Vector2D> v;
auto length = boost::python::len(list);
v.reserve(length);
for (auto i = 0u; i < length; ++i) {
boost::python::extract<carla::geom::Vector2D> ext(list[i]);
if (ext.check()) {
v.push_back(ext);
} else {
v.push_back(carla::geom::Vector2D{
boost::python::extract<float>(list[i][0u]),
boost::python::extract<float>(list[i][1u])});
}
}
return v;
}
static auto GetWheels(const carla::rpc::VehiclePhysicsControl &self) {
const auto &wheels = self.GetWheels();
boost::python::object get_iter = boost::python::iterator<std::vector<carla::rpc::WheelPhysicsControl>>();
boost::python::object iter = get_iter(wheels);
return boost::python::list(iter);
}
static void SetWheels(carla::rpc::VehiclePhysicsControl &self, const boost::python::list &list) {
std::vector<carla::rpc::WheelPhysicsControl> wheels;
auto length = boost::python::len(list);
for (auto i = 0u; i < length; ++i) {
wheels.push_back(boost::python::extract<carla::rpc::WheelPhysicsControl &>(list[i]));
}
self.wheels = wheels;
}
static auto GetTorqueCurve(const carla::rpc::VehiclePhysicsControl &self) {
const std::vector<carla::geom::Vector2D> &torque_curve = self.GetTorqueCurve();
boost::python::object get_iter = boost::python::iterator<const std::vector<carla::geom::Vector2D>>();
boost::python::object iter = get_iter(torque_curve);
return boost::python::list(iter);
}
static void SetTorqueCurve(carla::rpc::VehiclePhysicsControl &self, const boost::python::list &list) {
self.torque_curve = GetVectorOfVector2DFromList(list);
}
static auto GetSteeringCurve(const carla::rpc::VehiclePhysicsControl &self) {
const std::vector<carla::geom::Vector2D> &steering_curve = self.GetSteeringCurve();
boost::python::object get_iter = boost::python::iterator<const std::vector<carla::geom::Vector2D>>();
boost::python::object iter = get_iter(steering_curve);
return boost::python::list(iter);
}
static void SetSteeringCurve(carla::rpc::VehiclePhysicsControl &self, const boost::python::list &list) {
self.steering_curve = GetVectorOfVector2DFromList(list);
}
boost::python::object VehiclePhysicsControl_init(boost::python::tuple args, boost::python::dict kwargs) {
// Args names
const uint32_t NUM_ARGUMENTS = 16;
const char* args_names[NUM_ARGUMENTS] = {
"torque_curve",
"max_rpm",
"moi",
"damping_rate_full_throttle",
"damping_rate_zero_throttle_clutch_engaged",
"damping_rate_zero_throttle_clutch_disengaged",
"use_gear_autobox",
"gear_switch_time",
"clutch_strength",
"mass",
"drag_coefficient",
"center_of_mass",
"steering_curve",
"wheels"
};
boost::python::object self = args[0];
args = boost::python::tuple(args.slice(1, boost::python::_));
auto res = self.attr("__init__")();
if (len(args) > 0) {
for (unsigned int i=0; i < len(args); ++i)
self.attr(args_names[i]) = args[i];
}
for (unsigned int i = 0; i < NUM_ARGUMENTS; ++i) {
if (kwargs.contains(args_names[i])) {
self.attr(args_names[i]) = kwargs[args_names[i]];
}
}
return res;
}
void export_control() {
using namespace boost::python;
namespace cr = carla::rpc;
@ -75,4 +199,52 @@ void export_control() {
.def("__ne__", &cr::WalkerControl::operator!=)
.def(self_ns::str(self_ns::self))
;
class_<std::vector<cr::WheelPhysicsControl>>("vector_of_wheels")
.def(boost::python::vector_indexing_suite<std::vector<cr::WheelPhysicsControl>>())
.def(self_ns::str(self_ns::self))
;
class_<cr::WheelPhysicsControl>("WheelPhysicsControl")
.def(init<float, float, float, bool>(
(arg("tire_friction")=2.0f,
arg("damping_rate")=0.25f,
arg("steer_angle")=70.0f,
arg("disable_steering")=false)))
.def_readwrite("tire_friction", &cr::WheelPhysicsControl::tire_friction)
.def_readwrite("damping_rate", &cr::WheelPhysicsControl::damping_rate)
.def_readwrite("steer_angle", &cr::WheelPhysicsControl::steer_angle)
.def_readwrite("disable_steering", &cr::WheelPhysicsControl::disable_steering)
.def("__eq__", &cr::WheelPhysicsControl::operator==)
.def("__ne__", &cr::WheelPhysicsControl::operator!=)
.def(self_ns::str(self_ns::self))
;
class_<cr::VehiclePhysicsControl>("VehiclePhysicsControl", no_init)
.def("__init__", raw_function(VehiclePhysicsControl_init), "raw ctor")
.def(init<>())
.add_property("torque_curve", &GetTorqueCurve, &SetTorqueCurve)
.def_readwrite("max_rpm", &cr::VehiclePhysicsControl::max_rpm)
.def_readwrite("moi", &cr::VehiclePhysicsControl::moi)
.def_readwrite("damping_rate_full_throttle", &cr::VehiclePhysicsControl::damping_rate_full_throttle)
.def_readwrite("damping_rate_zero_throttle_clutch_engaged", &cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_engaged)
.def_readwrite("damping_rate_zero_throttle_clutch_disengaged", &cr::VehiclePhysicsControl::damping_rate_zero_throttle_clutch_disengaged)
.def_readwrite("use_gear_autobox", &cr::VehiclePhysicsControl::use_gear_autobox)
.def_readwrite("gear_switch_time", &cr::VehiclePhysicsControl::gear_switch_time)
.def_readwrite("clutch_strength", &cr::VehiclePhysicsControl::clutch_strength)
.def_readwrite("mass", &cr::VehiclePhysicsControl::mass)
.def_readwrite("drag_coefficient", &cr::VehiclePhysicsControl::drag_coefficient)
.def_readwrite("center_of_mass", &cr::VehiclePhysicsControl::center_of_mass)
.add_property("steering_curve", &GetSteeringCurve, &SetSteeringCurve)
.add_property("wheels", &GetWheels, &SetWheels)
.def("__eq__", &cr::VehiclePhysicsControl::operator==)
.def("__ne__", &cr::VehiclePhysicsControl::operator!=)
.def(self_ns::str(self_ns::self))
;
}

View File

@ -8,15 +8,24 @@
#include <carla/geom/Location.h>
#include <carla/geom/Rotation.h>
#include <carla/geom/Transform.h>
#include <carla/geom/Vector2D.h>
#include <carla/geom/Vector3D.h>
#include <boost/python/implicit.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <ostream>
namespace carla {
namespace geom {
template <typename T>
static void WriteVector2D(std::ostream &out, const char *name, const T &vector2D) {
out << name
<< "(x=" << vector2D.x
<< ", y=" << vector2D.y << ')';
}
template <typename T>
static void WriteVector3D(std::ostream &out, const char *name, const T &vector3D) {
out << name
@ -25,6 +34,11 @@ namespace geom {
<< ", z=" << vector3D.z << ')';
}
std::ostream &operator<<(std::ostream &out, const Vector2D &vector2D) {
WriteVector2D(out, "Vector2D", vector2D);
return out;
}
std::ostream &operator<<(std::ostream &out, const Vector3D &vector3D) {
WriteVector3D(out, "Vector3D", vector3D);
return out;
@ -67,6 +81,29 @@ static void TransformList(const carla::geom::Transform &self, boost::python::lis
void export_geom() {
using namespace boost::python;
namespace cg = carla::geom;
class_<std::vector<cg::Vector2D>>("vector_of_vector2D")
.def(boost::python::vector_indexing_suite<std::vector<cg::Vector2D>>())
.def(self_ns::str(self_ns::self))
;
class_<cg::Vector2D>("Vector2D")
.def(init<float, float>((arg("x")=0.0f, arg("y")=0.0f)))
.def_readwrite("x", &cg::Vector2D::x)
.def_readwrite("y", &cg::Vector2D::y)
.def("__eq__", &cg::Vector2D::operator==)
.def("__ne__", &cg::Vector2D::operator!=)
.def(self += self)
.def(self + self)
.def(self -= self)
.def(self - self)
.def(self *= double())
.def(self * double())
.def(double() * self)
.def(self /= double())
.def(self / double())
.def(double() / self)
.def(self_ns::str(self_ns::self))
;
implicitly_convertible<cg::Vector3D, cg::Location>();
implicitly_convertible<cg::Location, cg::Vector3D>();

View File

@ -36,3 +36,74 @@ class testVehicleControl(unittest.TestCase):
self.assertEqual(c.brake, 3.0)
self.assertEqual(c.hand_brake, True)
self.assertEqual(c.reverse, True)
class testVehiclePhysicsControl(unittest.TestCase):
def test_named_args(self):
torque_curve = [[0, 400],
[24, 56],
[24, 56],
[1315.47, 654.445],
[5729, 400]]
steering_curve = [carla.Vector2D(x=0, y=1),
carla.Vector2D(x=20.0, y=0.9),
carla.Vector2D(x=63.0868, y=0.703473),
carla.Vector2D(x=119.12, y=0.573047)]
wheels = [carla.WheelPhysicsControl(tire_friction=2, damping_rate=0, steer_angle=30, disable_steering=1),
carla.WheelPhysicsControl(tire_friction=2, damping_rate=0, steer_angle=30, disable_steering=1),
carla.WheelPhysicsControl(tire_friction=2, damping_rate=0, steer_angle=30, disable_steering=1),
carla.WheelPhysicsControl(tire_friction=2, damping_rate=0, steer_angle=30, disable_steering=1)]
pc = carla.VehiclePhysicsControl(
torque_curve=torque_curve,
max_rpm=5729,
moi=1,
damping_rate_full_throttle=0.15,
damping_rate_zero_throttle_clutch_engaged=2,
damping_rate_zero_throttle_clutch_disengaged=0.35,
use_gear_autobox=1,
gear_switch_time=0.5,
clutch_strength=10,
mass=5500,
drag_coefficient=0.3,
center_of_mass=carla.Vector3D(x=0.5, y=1, z=1),
steering_curve=steering_curve,
wheels=wheels)
error = .001
for i in range(0,len(torque_curve)):
self.assertTrue(abs(pc.torque_curve[i].x - torque_curve[i][0]) <= error)
self.assertTrue(abs(pc.torque_curve[i].y - torque_curve[i][1]) <= error)
self.assertTrue(abs(pc.max_rpm - 5729) <= error)
self.assertTrue(abs(pc.moi - 1) <= error)
self.assertTrue(abs(pc.damping_rate_full_throttle - 0.15) <= error)
self.assertTrue(abs(pc.damping_rate_zero_throttle_clutch_engaged - 2) <= error)
self.assertTrue(abs(pc.damping_rate_zero_throttle_clutch_disengaged - 0.35) <= error)
self.assertTrue(abs(pc.use_gear_autobox - 1) <= error)
self.assertTrue(abs(pc.gear_switch_time - 0.5) <= error)
self.assertTrue(abs(pc.clutch_strength - 10) <= error)
self.assertTrue(abs(pc.mass - 5500) <= error)
self.assertTrue(abs(pc.drag_coefficient - 0.3) <= error)
self.assertTrue(abs(pc.center_of_mass.x - 0.5) <= error)
self.assertTrue(abs(pc.center_of_mass.y - 1) <= error)
self.assertTrue(abs(pc.center_of_mass.z - 1) <= error)
for i in range(0,len(steering_curve)):
self.assertTrue(abs(pc.steering_curve[i].x - steering_curve[i].x) <= error)
self.assertTrue(abs(pc.steering_curve[i].y - steering_curve[i].y) <= error)
for i in range(0,len(wheels)):
self.assertTrue(abs(pc.wheels[i].tire_friction - wheels[i].tire_friction) <= error)
self.assertTrue(abs(pc.wheels[i].damping_rate - wheels[i].damping_rate) <= error)
self.assertTrue(abs(pc.wheels[i].steer_angle - wheels[i].steer_angle) <= error)
self.assertEqual(pc.wheels[i].disable_steering, wheels[i].disable_steering)

View File

@ -23,8 +23,10 @@
#include <carla/rpc/Response.h>
#include <carla/rpc/Server.h>
#include <carla/rpc/Transform.h>
#include <carla/rpc/Vector2D.h>
#include <carla/rpc/Vector3D.h>
#include <carla/rpc/VehicleControl.h>
#include <carla/rpc/VehiclePhysicsControl.h>
#include <carla/rpc/WalkerControl.h>
#include <carla/rpc/WeatherParameters.h>
#include <carla/streaming/Server.h>
@ -367,6 +369,45 @@ void FTheNewCarlaServer::FPimpl::BindActions()
return R<void>::Success();
});
Server.BindSync("get_physics_control", [this](
cr::Actor Actor) -> R<cr::VehiclePhysicsControl>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to get actor physics control: actor not found");
}
auto Vehicle = Cast<ACarlaWheeledVehicle>(ActorView.GetActor());
if (Vehicle == nullptr)
{
RESPOND_ERROR("unable to get actor physics control: actor is not a vehicle");
}
return cr::VehiclePhysicsControl(Vehicle->GetVehiclePhysicsControl());
});
Server.BindSync("apply_physics_control", [this](
cr::Actor Actor, cr::VehiclePhysicsControl PhysicsControl) -> R<void>
{
REQUIRE_CARLA_EPISODE();
auto ActorView = Episode->FindActor(Actor.id);
if (!ActorView.IsValid())
{
RESPOND_ERROR("unable to apply actor physics control: actor not found");
}
auto Vehicle = Cast<ACarlaWheeledVehicle>(ActorView.GetActor());
if (Vehicle == nullptr)
{
RESPOND_ERROR("unable to apply actor physics control: actor is not a vehicle");
}
Vehicle->ApplyVehiclePhysicsControl(FVehiclePhysicsControl(PhysicsControl));
return R<void>::Success();
});
Server.BindSync("set_actor_simulate_physics", [this](
cr::Actor Actor,
bool bEnabled) -> R<void>

View File

@ -5,13 +5,14 @@
// For a copy, see <https://opensource.org/licenses/MIT>.
#include "Carla.h"
#include "TireConfig.h"
#include "CarlaWheeledVehicle.h"
#include "VehicleWheel.h"
#include "Agent/VehicleAgentComponent.h"
#include "Components/BoxComponent.h"
#include "Engine/CollisionProfile.h"
// =============================================================================
// -- Constructor and destructor -----------------------------------------------
// =============================================================================
@ -132,3 +133,100 @@ void ACarlaWheeledVehicle::SetHandbrakeInput(const bool Value)
GetVehicleMovementComponent()->SetHandbrakeInput(Value);
Control.bHandBrake = Value;
}
FVehiclePhysicsControl ACarlaWheeledVehicle::GetVehiclePhysicsControl()
{
UWheeledVehicleMovementComponent4W *Vehicle4W = CastChecked<UWheeledVehicleMovementComponent4W>(
GetVehicleMovement());
FVehiclePhysicsControl PhysicsControl;
// Engine Setup
PhysicsControl.TorqueCurve = Vehicle4W->EngineSetup.TorqueCurve.EditorCurveData;
PhysicsControl.MaxRPM = Vehicle4W->EngineSetup.MaxRPM;
PhysicsControl.MOI = Vehicle4W->EngineSetup.MOI;
PhysicsControl.DampingRateFullThrottle = Vehicle4W->EngineSetup.DampingRateFullThrottle;
PhysicsControl.DampingRateZeroThrottleClutchEngaged =
Vehicle4W->EngineSetup.DampingRateZeroThrottleClutchEngaged;
PhysicsControl.DampingRateZeroThrottleClutchDisengaged =
Vehicle4W->EngineSetup.DampingRateZeroThrottleClutchDisengaged;
// Transmission Setup
PhysicsControl.bUseGearAutoBox = Vehicle4W->TransmissionSetup.bUseGearAutoBox;
PhysicsControl.GearSwitchTime = Vehicle4W->TransmissionSetup.GearSwitchTime;
PhysicsControl.ClutchStrength = Vehicle4W->TransmissionSetup.ClutchStrength;
// Vehicle Setup
PhysicsControl.Mass = Vehicle4W->Mass;
PhysicsControl.DragCoefficient = Vehicle4W->DragCoefficient;
// Center of mass offset (Center of mass is always zero vector in local
// position)
UPrimitiveComponent *UpdatedPrimitive = Cast<UPrimitiveComponent>(Vehicle4W->UpdatedComponent);
PhysicsControl.CenterOfMass = UpdatedPrimitive->BodyInstance.COMNudge;
// Transmission Setup
PhysicsControl.SteeringCurve = Vehicle4W->SteeringCurve.EditorCurveData;
// Wheels Setup
TArray<FWheelPhysicsControl> Wheels;
for (auto &Wheel : Vehicle4W->Wheels)
{
FWheelPhysicsControl MyWheel;
MyWheel.TireFriction = Wheel->TireConfig->GetFrictionScale();
MyWheel.DampingRate = Wheel->DampingRate;
MyWheel.SteerAngle = Wheel->SteerAngle;
MyWheel.bDisableSteering = Wheel->GetWheelSetup().bDisableSteering;
Wheels.Add(MyWheel);
}
PhysicsControl.Wheels = Wheels;
return PhysicsControl;
}
void ACarlaWheeledVehicle::ApplyVehiclePhysicsControl(const FVehiclePhysicsControl &PhysicsControl)
{
UWheeledVehicleMovementComponent4W *Vehicle4W = CastChecked<UWheeledVehicleMovementComponent4W>(GetVehicleMovement());
// Engine Setup
Vehicle4W->EngineSetup.TorqueCurve.EditorCurveData = PhysicsControl.TorqueCurve;
Vehicle4W->EngineSetup.MaxRPM = PhysicsControl.MaxRPM;
Vehicle4W->EngineSetup.MOI = PhysicsControl.MOI;
Vehicle4W->EngineSetup.DampingRateFullThrottle = PhysicsControl.DampingRateFullThrottle;
Vehicle4W->EngineSetup.DampingRateZeroThrottleClutchEngaged = PhysicsControl.DampingRateZeroThrottleClutchEngaged;
Vehicle4W->EngineSetup.DampingRateZeroThrottleClutchDisengaged = PhysicsControl.DampingRateZeroThrottleClutchDisengaged;
// Transmission Setup
Vehicle4W->TransmissionSetup.bUseGearAutoBox = PhysicsControl.bUseGearAutoBox;
Vehicle4W->TransmissionSetup.GearSwitchTime = PhysicsControl.GearSwitchTime;
Vehicle4W->TransmissionSetup.ClutchStrength = PhysicsControl.ClutchStrength;
// Vehicle Setup
Vehicle4W->Mass = PhysicsControl.Mass;
Vehicle4W->DragCoefficient = PhysicsControl.DragCoefficient;
// Center of mass
UPrimitiveComponent *UpdatedPrimitive = Cast<UPrimitiveComponent>(Vehicle4W->UpdatedComponent);
UpdatedPrimitive->BodyInstance.COMNudge = PhysicsControl.CenterOfMass;
// Transmission Setup
Vehicle4W->SteeringCurve.EditorCurveData = PhysicsControl.SteeringCurve;
// Wheels Setup
for (unsigned int i = 0; i < PhysicsControl.Wheels.Num(); ++i)
{
Vehicle4W->Wheels[i]->DampingRate = PhysicsControl.Wheels[i].DampingRate;
Vehicle4W->Wheels[i]->SteerAngle = PhysicsControl.Wheels[i].SteerAngle;
Vehicle4W->Wheels[i]->GetWheelSetup().bDisableSteering = PhysicsControl.Wheels[i].bDisableSteering;
Vehicle4W->Wheels[i]->TireConfig->SetFrictionScale(PhysicsControl.Wheels[i].TireFriction);
}
Vehicle4W->RecreatePhysicsState();
}

View File

@ -10,11 +10,14 @@
#include "Vehicle/CarlaWheeledVehicleState.h"
#include "Vehicle/VehicleControl.h"
#include "Vehicle/VehiclePhysicsControl.h"
#include "WheeledVehicleMovementComponent4W.h"
#include "CoreMinimal.h"
#include "CarlaWheeledVehicle.generated.h"
class UBoxComponent;
class UVehicleAgentComponent;
@ -93,6 +96,10 @@ public:
return State;
}
FVehiclePhysicsControl GetVehiclePhysicsControl();
void ApplyVehiclePhysicsControl(const FVehiclePhysicsControl &PhysicsControl);
/// @}
// ===========================================================================
/// @name Set functions

View File

@ -0,0 +1,63 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "Vehicle/WheelPhysicsControl.h"
#include "VehiclePhysicsControl.generated.h"
USTRUCT(BlueprintType)
struct CARLA_API FVehiclePhysicsControl
{
GENERATED_BODY()
// MECHANICAL SETUP
// Engine Setup
FRichCurve TorqueCurve;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float MaxRPM = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float MOI = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float DampingRateFullThrottle = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float DampingRateZeroThrottleClutchEngaged = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float DampingRateZeroThrottleClutchDisengaged = 0.0f;
// // Transmission Setup
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
bool bUseGearAutoBox = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float GearSwitchTime = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float ClutchStrength = 0.0f;
// Vehicle Setup
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float Mass = 0.0f;
UPROPERTY(Category = "Vehicle Engine Physics Control", EditAnywhere, BlueprintReadWrite)
float DragCoefficient = 0.0f;
// Steering Setup
FRichCurve SteeringCurve;
// Center Of Mass
UPROPERTY(Category = "Vehicle Center Of Mass", EditAnywhere, BlueprintReadWrite)
FVector CenterOfMass;
// Wheels Setup
TArray<FWheelPhysicsControl> Wheels;
};

View File

@ -0,0 +1,27 @@
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "WheelPhysicsControl.generated.h"
USTRUCT(BlueprintType)
struct CARLA_API FWheelPhysicsControl
{
GENERATED_BODY()
UPROPERTY(Category = "Wheel Tire Friction", EditAnywhere, BlueprintReadWrite)
float TireFriction = 0.0f;
UPROPERTY(Category = "Wheel Damping Rate", EditAnywhere, BlueprintReadWrite)
float DampingRate = 0.0f;
UPROPERTY(Category = "Wheel Steer Angle", EditAnywhere, BlueprintReadWrite)
float SteerAngle = 0.0f;
UPROPERTY(Category = "Wheel Disable Steering", EditAnywhere, BlueprintReadWrite)
bool bDisableSteering = 0.0f;
};