| | using UnityEngine; |
| | using Unity.MLAgents; |
| | using Unity.MLAgents.Actuators; |
| | using Unity.MLAgentsExamples; |
| | using Unity.MLAgents.Sensors; |
| |
|
| | [RequireComponent(typeof(JointDriveController))] |
| | public class WormAgent : Agent |
| | { |
| | const float m_MaxWalkingSpeed = 10; |
| |
|
| | [Header("Target Prefabs")] public Transform TargetPrefab; |
| | private Transform m_Target; |
| |
|
| | [Header("Body Parts")] public Transform bodySegment0; |
| | public Transform bodySegment1; |
| | public Transform bodySegment2; |
| | public Transform bodySegment3; |
| |
|
| | |
| | |
| | OrientationCubeController m_OrientationCube; |
| |
|
| | |
| | DirectionIndicator m_DirectionIndicator; |
| | JointDriveController m_JdController; |
| |
|
| | private Vector3 m_StartingPos; |
| |
|
| | public override void Initialize() |
| | { |
| | SpawnTarget(TargetPrefab, transform.position); |
| |
|
| | m_StartingPos = bodySegment0.position; |
| | m_OrientationCube = GetComponentInChildren<OrientationCubeController>(); |
| | m_DirectionIndicator = GetComponentInChildren<DirectionIndicator>(); |
| | m_JdController = GetComponent<JointDriveController>(); |
| |
|
| | UpdateOrientationObjects(); |
| |
|
| | |
| | m_JdController.SetupBodyPart(bodySegment0); |
| | m_JdController.SetupBodyPart(bodySegment1); |
| | m_JdController.SetupBodyPart(bodySegment2); |
| | m_JdController.SetupBodyPart(bodySegment3); |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | void SpawnTarget(Transform prefab, Vector3 pos) |
| | { |
| | m_Target = Instantiate(prefab, pos, Quaternion.identity, transform.parent); |
| | } |
| |
|
| | |
| | |
| | |
| | public override void OnEpisodeBegin() |
| | { |
| | foreach (var bodyPart in m_JdController.bodyPartsList) |
| | { |
| | bodyPart.Reset(bodyPart); |
| | } |
| |
|
| | |
| | bodySegment0.rotation = Quaternion.Euler(0, Random.Range(0.0f, 360.0f), 0); |
| |
|
| | UpdateOrientationObjects(); |
| | } |
| |
|
| | |
| | |
| | |
| | public void CollectObservationBodyPart(BodyPart bp, VectorSensor sensor) |
| | { |
| | |
| | sensor.AddObservation(bp.groundContact.touchingGround ? 1 : 0); |
| |
|
| | |
| | |
| | sensor.AddObservation(m_OrientationCube.transform.InverseTransformDirection(bp.rb.velocity)); |
| | sensor.AddObservation(m_OrientationCube.transform.InverseTransformDirection(bp.rb.angularVelocity)); |
| |
|
| |
|
| | if (bp.rb.transform != bodySegment0) |
| | { |
| | |
| | sensor.AddObservation( |
| | m_OrientationCube.transform.InverseTransformDirection(bp.rb.position - bodySegment0.position)); |
| | sensor.AddObservation(bp.rb.transform.localRotation); |
| | } |
| |
|
| | if (bp.joint) |
| | sensor.AddObservation(bp.currentStrength / m_JdController.maxJointForceLimit); |
| | } |
| |
|
| | public override void CollectObservations(VectorSensor sensor) |
| | { |
| | RaycastHit hit; |
| | float maxDist = 10; |
| | if (Physics.Raycast(bodySegment0.position, Vector3.down, out hit, maxDist)) |
| | { |
| | sensor.AddObservation(hit.distance / maxDist); |
| | } |
| | else |
| | sensor.AddObservation(1); |
| |
|
| | var cubeForward = m_OrientationCube.transform.forward; |
| | var velGoal = cubeForward * m_MaxWalkingSpeed; |
| | sensor.AddObservation(m_OrientationCube.transform.InverseTransformDirection(velGoal)); |
| | sensor.AddObservation(Quaternion.Angle(m_OrientationCube.transform.rotation, |
| | m_JdController.bodyPartsDict[bodySegment0].rb.rotation) / 180); |
| | sensor.AddObservation(Quaternion.FromToRotation(bodySegment0.forward, cubeForward)); |
| |
|
| | |
| | sensor.AddObservation(m_OrientationCube.transform.InverseTransformPoint(m_Target.transform.position)); |
| |
|
| | foreach (var bodyPart in m_JdController.bodyPartsList) |
| | { |
| | CollectObservationBodyPart(bodyPart, sensor); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | public void TouchedTarget() |
| | { |
| | AddReward(1f); |
| | } |
| |
|
| | public override void OnActionReceived(ActionBuffers actionBuffers) |
| | { |
| | |
| | var bpDict = m_JdController.bodyPartsDict; |
| |
|
| | var i = -1; |
| | var continuousActions = actionBuffers.ContinuousActions; |
| | |
| | bpDict[bodySegment0].SetJointTargetRotation(continuousActions[++i], continuousActions[++i], 0); |
| | bpDict[bodySegment1].SetJointTargetRotation(continuousActions[++i], continuousActions[++i], 0); |
| | bpDict[bodySegment2].SetJointTargetRotation(continuousActions[++i], continuousActions[++i], 0); |
| |
|
| | |
| | bpDict[bodySegment0].SetJointStrength(continuousActions[++i]); |
| | bpDict[bodySegment1].SetJointStrength(continuousActions[++i]); |
| | bpDict[bodySegment2].SetJointStrength(continuousActions[++i]); |
| |
|
| | |
| | if (bodySegment0.position.y < m_StartingPos.y - 2) |
| | { |
| | EndEpisode(); |
| | } |
| | } |
| |
|
| | void FixedUpdate() |
| | { |
| | UpdateOrientationObjects(); |
| |
|
| | var velReward = |
| | GetMatchingVelocityReward(m_OrientationCube.transform.forward * m_MaxWalkingSpeed, |
| | m_JdController.bodyPartsDict[bodySegment0].rb.velocity); |
| |
|
| | |
| | |
| | var rotAngle = Quaternion.Angle(m_OrientationCube.transform.rotation, |
| | m_JdController.bodyPartsDict[bodySegment0].rb.rotation); |
| |
|
| | |
| | var facingRew = 0f; |
| | |
| | if (rotAngle < 30) |
| | { |
| | |
| | |
| | facingRew = 1 - (rotAngle / 180); |
| | } |
| |
|
| | |
| | AddReward(velReward * facingRew); |
| | } |
| |
|
| | |
| | |
| | |
| | public float GetMatchingVelocityReward(Vector3 velocityGoal, Vector3 actualVelocity) |
| | { |
| | |
| | var velDeltaMagnitude = Mathf.Clamp(Vector3.Distance(actualVelocity, velocityGoal), 0, m_MaxWalkingSpeed); |
| |
|
| | |
| | |
| | return Mathf.Pow(1 - Mathf.Pow(velDeltaMagnitude / m_MaxWalkingSpeed, 2), 2); |
| | } |
| |
|
| | |
| | |
| | |
| | void UpdateOrientationObjects() |
| | { |
| | m_OrientationCube.UpdateOrientation(bodySegment0, m_Target); |
| | if (m_DirectionIndicator) |
| | { |
| | m_DirectionIndicator.MatchOrientation(m_OrientationCube.transform); |
| | } |
| | } |
| | } |
| |
|