organisation & splat changes

This commit is contained in:
2024-03-28 14:55:33 +00:00
parent d487af62b8
commit 0535f6df4c
347 changed files with 3579 additions and 1956 deletions

View File

@@ -0,0 +1,302 @@
// Implmentation of an Agent. Agent reads observations relevant to the reinforcement
// learning task at hand, acts based on the observations, and receives a reward
// based on its performance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using System.Linq;
using ManyWorlds;
public class StyleTransfer002Agent : Agent, IOnSensorCollision, IOnTerrainCollision {
public float FrameReward;
public float AverageReward;
public List<float> Rewards;
public List<float> SensorIsInTouch;
StyleTransfer002Master _master;
StyleTransfer002Animator _localStyleAnimator;
StyleTransfer002Animator _styleAnimator;
DecisionRequester _decisionRequester;
List<GameObject> _sensors;
public bool ShowMonitor = false;
static int _startCount;
static ScoreHistogramData _scoreHistogramData;
int _totalAnimFrames;
bool _ignorScoreForThisFrame;
bool _isDone;
bool _hasLazyInitialized;
// Use this for initialization
void Start () {
_master = GetComponent<StyleTransfer002Master>();
_decisionRequester = GetComponent<DecisionRequester>();
var spawnableEnv = GetComponentInParent<SpawnableEnv>();
_localStyleAnimator = spawnableEnv.gameObject.GetComponentInChildren<StyleTransfer002Animator>();
_styleAnimator = _localStyleAnimator.GetFirstOfThisAnim();
_startCount++;
}
// Update is called once per frame
void Update () {
}
// Collect observations that are used by the Neural Network for training and inference.
override public void CollectObservations(VectorSensor sensor)
{
if (!_hasLazyInitialized)
{
OnEpisodeBegin();
}
sensor.AddObservation(_master.ObsPhase);
foreach (var bodyPart in _master.BodyParts)
{
sensor.AddObservation(bodyPart.ObsLocalPosition);
sensor.AddObservation(bodyPart.ObsRotation);
sensor.AddObservation(bodyPart.ObsRotationVelocity);
sensor.AddObservation(bodyPart.ObsVelocity);
}
foreach (var muscle in _master.Muscles)
{
if (muscle.ConfigurableJoint.angularXMotion != ConfigurableJointMotion.Locked)
sensor.AddObservation(muscle.TargetNormalizedRotationX);
if (muscle.ConfigurableJoint.angularYMotion != ConfigurableJointMotion.Locked)
sensor.AddObservation(muscle.TargetNormalizedRotationY);
if (muscle.ConfigurableJoint.angularZMotion != ConfigurableJointMotion.Locked)
sensor.AddObservation(muscle.TargetNormalizedRotationZ);
}
sensor.AddObservation(_master.ObsCenterOfMass);
sensor.AddObservation(_master.ObsVelocity);
sensor.AddObservation(_master.ObsAngularMoment);
sensor.AddObservation(SensorIsInTouch);
}
// A method that applies the vectorAction to the muscles, and calculates the rewards.
public override void OnActionReceived(ActionBuffers actions)
{
float[] vectorAction = actions.ContinuousActions.Select(x=>x).ToArray();
if (!_hasLazyInitialized)
{
return;
}
_isDone = false;
if (_styleAnimator == _localStyleAnimator)
_styleAnimator.OnAgentAction();
_master.OnAgentAction();
int i = 0;
foreach (var muscle in _master.Muscles)
{
if (muscle.ConfigurableJoint.angularXMotion != ConfigurableJointMotion.Locked)
muscle.TargetNormalizedRotationX = vectorAction[i++];
if (muscle.ConfigurableJoint.angularYMotion != ConfigurableJointMotion.Locked)
muscle.TargetNormalizedRotationY = vectorAction[i++];
if (muscle.ConfigurableJoint.angularZMotion != ConfigurableJointMotion.Locked)
muscle.TargetNormalizedRotationZ = vectorAction[i++];
}
// the scaler factors are picked empirically by calculating the MaxRotationDistance, MaxVelocityDistance achieved for an untrained agent.
var rotationDistance = _master.RotationDistance / 16f ;
var centerOfMassvelocityDistance = _master.CenterOfMassVelocityDistance / 6f ;
var endEffectorDistance = _master.EndEffectorDistance / 1f ;
var endEffectorVelocityDistance = _master.EndEffectorVelocityDistance / 170f;
var jointAngularVelocityDistance = _master.JointAngularVelocityDistance / 7000f;
var jointAngularVelocityDistanceWorld = _master.JointAngularVelocityDistanceWorld / 7000f;
var centerOfMassDistance = _master.CenterOfMassDistance / 0.3f;
var angularMomentDistance = _master.AngularMomentDistance / 150.0f;
var sensorDistance = _master.SensorDistance / 1f;
var rotationReward = 0.35f * Mathf.Exp(-rotationDistance);
var centerOfMassVelocityReward = 0.1f * Mathf.Exp(-centerOfMassvelocityDistance);
var endEffectorReward = 0.15f * Mathf.Exp(-endEffectorDistance);
var endEffectorVelocityReward = 0.1f * Mathf.Exp(-endEffectorVelocityDistance);
var jointAngularVelocityReward = 0.1f * Mathf.Exp(-jointAngularVelocityDistance);
var jointAngularVelocityRewardWorld = 0.0f * Mathf.Exp(-jointAngularVelocityDistanceWorld);
var centerMassReward = 0.05f * Mathf.Exp(-centerOfMassDistance);
var angularMomentReward = 0.15f * Mathf.Exp(-angularMomentDistance);
var sensorReward = 0.0f * Mathf.Exp(-sensorDistance);
var jointsNotAtLimitReward = 0.0f * Mathf.Exp(-JointsAtLimit());
//Debug.Log("---------------");
//Debug.Log("rotation reward: " + rotationReward);
//Debug.Log("endEffectorReward: " + endEffectorReward);
//Debug.Log("endEffectorVelocityReward: " + endEffectorVelocityReward);
//Debug.Log("jointAngularVelocityReward: " + jointAngularVelocityReward);
//Debug.Log("jointAngularVelocityRewardWorld: " + jointAngularVelocityRewardWorld);
//Debug.Log("centerMassReward: " + centerMassReward);
//Debug.Log("centerMassVelocityReward: " + centerOfMassVelocityReward);
//Debug.Log("angularMomentReward: " + angularMomentReward);
//Debug.Log("sensorReward: " + sensorReward);
//Debug.Log("joints not at limit rewards:" + jointsNotAtLimitReward);
float reward = rotationReward +
centerOfMassVelocityReward +
endEffectorReward +
endEffectorVelocityReward +
jointAngularVelocityReward +
jointAngularVelocityRewardWorld +
centerMassReward +
angularMomentReward +
sensorReward +
jointsNotAtLimitReward;
if (!_master.IgnorRewardUntilObservation)
AddReward(reward);
if (reward < 0.5)
EndEpisode();
if (!_isDone){
if (_master.IsDone()){
EndEpisode();
if (_master.StartAnimationIndex > 0)
_master.StartAnimationIndex--;
}
}
FrameReward = reward;
var stepCount = StepCount > 0 ? StepCount : 1;
AverageReward = GetCumulativeReward() / (float) stepCount;
}
// A helper function that calculates a fraction of joints at their limit positions
float JointsAtLimit(string[] ignorJoints = null)
{
int atLimitCount = 0;
int totalJoints = 0;
foreach (var muscle in _master.Muscles)
{
if(muscle.Parent == null)
continue;
var name = muscle.Name;
if (ignorJoints != null && ignorJoints.Contains(name))
continue;
if (Mathf.Abs(muscle.TargetNormalizedRotationX) >= 1f)
atLimitCount++;
if (Mathf.Abs(muscle.TargetNormalizedRotationY) >= 1f)
atLimitCount++;
if (Mathf.Abs(muscle.TargetNormalizedRotationZ) >= 1f)
atLimitCount++;
totalJoints++;
}
float fractionOfJointsAtLimit = (float)atLimitCount / (float)totalJoints;
return fractionOfJointsAtLimit;
}
// Sets reward
public void SetTotalAnimFrames(int totalAnimFrames)
{
_totalAnimFrames = totalAnimFrames;
if (_scoreHistogramData == null) {
var columns = _totalAnimFrames;
if (_decisionRequester?.DecisionPeriod > 1)
columns /= _decisionRequester.DecisionPeriod;
_scoreHistogramData = new ScoreHistogramData(columns, 30);
}
Rewards = _scoreHistogramData.GetAverages().Select(x=>(float)x).ToList();
}
// Resets the agent. Initialize the style animator and master if not initialized.
public override void OnEpisodeBegin()
{
if (!_hasLazyInitialized)
{
_master = GetComponent<StyleTransfer002Master>();
_master.BodyConfig = MarathonManAgent.BodyConfig;
_decisionRequester = GetComponent<DecisionRequester>();
var spawnableEnv = GetComponentInParent<SpawnableEnv>();
_localStyleAnimator = spawnableEnv.gameObject.GetComponentInChildren<StyleTransfer002Animator>();
_styleAnimator = _localStyleAnimator.GetFirstOfThisAnim();
_styleAnimator.BodyConfig = MarathonManAgent.BodyConfig;
_styleAnimator.OnInitializeAgent();
_master.OnInitializeAgent();
_hasLazyInitialized = true;
_localStyleAnimator.DestoryIfNotFirstAnim();
}
_isDone = true;
_ignorScoreForThisFrame = true;
_master.ResetPhase();
_sensors = GetComponentsInChildren<SensorBehavior>()
.Select(x=>x.gameObject)
.ToList();
SensorIsInTouch = Enumerable.Range(0,_sensors.Count).Select(x=>0f).ToList();
if (_scoreHistogramData != null) {
var column = _master.StartAnimationIndex;
if (_decisionRequester?.DecisionPeriod > 1)
column /= _decisionRequester.DecisionPeriod;
if (_ignorScoreForThisFrame)
_ignorScoreForThisFrame = false;
else
_scoreHistogramData.SetItem(column, AverageReward);
}
}
// A method called on terrain collision. Used for early stopping an episode
// on specific objects' collision with terrain.
public virtual void OnTerrainCollision(GameObject other, GameObject terrain)
{
if (string.Compare(terrain.name, "Terrain", true) != 0)
return;
if (!_styleAnimator.AnimationStepsReady)
return;
var bodyPart = _master.BodyParts.FirstOrDefault(x=>x.Transform.gameObject == other);
if (bodyPart == null)
return;
switch (bodyPart.Group)
{
case BodyHelper002.BodyPartGroup.None:
case BodyHelper002.BodyPartGroup.Foot:
case BodyHelper002.BodyPartGroup.LegUpper:
case BodyHelper002.BodyPartGroup.LegLower:
case BodyHelper002.BodyPartGroup.Hand:
case BodyHelper002.BodyPartGroup.ArmLower:
case BodyHelper002.BodyPartGroup.ArmUpper:
break;
default:
EndEpisode();
break;
}
}
// Sets the a flag in Sensors In Touch array when an object enters collision with terrain
public void OnSensorCollisionEnter(Collider sensorCollider, GameObject other)
{
if (string.Compare(other.name, "Terrain", true) !=0)
return;
if (_sensors == null || _sensors.Count == 0)
return;
var sensor = _sensors
.FirstOrDefault(x=>x == sensorCollider.gameObject);
if (sensor != null) {
var idx = _sensors.IndexOf(sensor);
SensorIsInTouch[idx] = 1f;
}
}
// Sets the a flag in Sensors In Touch array when an object stops colliding with terrain
public void OnSensorCollisionExit(Collider sensorCollider, GameObject other)
{
if (string.Compare(other.gameObject.name, "Terrain", true) !=0)
return;
if (_sensors == null || _sensors.Count == 0)
return;
var sensor = _sensors
.FirstOrDefault(x=>x == sensorCollider.gameObject);
if (sensor != null) {
var idx = _sensors.IndexOf(sensor);
SensorIsInTouch[idx] = 0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec9cfe0db841e420283facda78fa8b61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,443 @@
// A class defining the Animator. The Animator is used as a reference for an
// Agent that mimicks the animator behavior. Before training, the animator's
// animation is run once and all the charateristics of it are stored as animSteps.
// During training, the agent can simply acess the precomputed values and mimick
// Animator's body Part' velocities, positions, rotations, angular velocities, etc.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.MLAgents;
using UnityEngine;
public class StyleTransfer002Animator : MonoBehaviour, IOnSensorCollision {
internal Animator anim;
public List<float> SensorIsInTouch;
List<GameObject> _sensors;
public List<AnimationStep> AnimationSteps;
public bool AnimationStepsReady;
public bool IsLoopingAnimation;
[Range(0f,1f)]
public float NormalizedTime;
public float Lenght;
private List<Vector3> _lastPosition;
private List<Quaternion> _lastRotation;
private List<Vector3> _lastPositionLocal;
private List<Quaternion> _lastRotationLocal;
List<Quaternion> _initialRotations;
public List<BodyPart002> BodyParts;
private Vector3 _lastCenterOfMass;
private List<Rigidbody> _rigidbodies;
private List<Transform> _transforms;
private bool isFirstOfThisAnim;
[System.Serializable]
public class AnimationStep
{
public float TimeStep;
public float NormalizedTime;
public List<Vector3> Velocities;
public List<Vector3> VelocitiesLocal;
public Vector3 CenterOfMassVelocity;
public List<Vector3> AngularVelocities;
public List<Vector3> AngularVelocitiesLocal;
public List<Vector3> Positions;
public List<Quaternion> Rotations;
public List<string> Names;
public Vector3 CenterOfMass;
public Vector3 AngularMoment;
public Vector3 TransformPosition;
public Quaternion TransformRotation;
public List<float> SensorIsInTouch;
}
public BodyConfig BodyConfig;
DecisionRequester _decisionRequester;
// Use this for initialization
public void OnInitializeAgent()
{
_decisionRequester = GameObject.Find("MarathonMan").GetComponent<DecisionRequester>();
anim = GetComponent<Animator>();
anim.Play("Record",0, NormalizedTime);
anim.Update(0f);
AnimationSteps = new List<AnimationStep>();
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
SetupSensors();
OnAgentAction();
}
void Awake()
{
SetupSensors();
}
void SetupSensors()
{
_sensors = GetComponentsInChildren<SensorBehavior>()
.Select(x=>x.gameObject)
.ToList();
SensorIsInTouch = Enumerable.Range(0,_sensors.Count).Select(x=>0f).ToList();
}
// Reset the animator.
void Reset()
{
BodyParts = new List<BodyPart002> ();
BodyPart002 root = null;
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
foreach (var t in _transforms)
{
if (BodyConfig.GetBodyPartGroup(t.name) == BodyHelper002.BodyPartGroup.None)
continue;
var bodyPart = new BodyPart002 {
Rigidbody = t.GetComponent<Rigidbody>(),
Transform = t,
Name = t.name,
Group = BodyConfig.GetBodyPartGroup(t.name),
};
if (bodyPart.Group == BodyConfig.GetRootBodyPart())
root = bodyPart;
bodyPart.Root = root;
bodyPart.Init();
BodyParts.Add(bodyPart);
}
var partCount = BodyParts.Count;
SetupSensors();
_lastPosition = Enumerable.Repeat(Vector3.zero, partCount).ToList();
_lastRotation = Enumerable.Repeat(Quaternion.identity, partCount).ToList();
_lastPositionLocal = Enumerable.Repeat(Vector3.zero, partCount).ToList();
_lastRotationLocal = Enumerable.Repeat(Quaternion.identity, partCount).ToList();
_lastCenterOfMass = transform.position;
_initialRotations = BodyParts
.Select(x=> x.Transform.rotation)
.ToList();
BecomeAnimated();
}
public StyleTransfer002Animator GetFirstOfThisAnim()
{
if (isFirstOfThisAnim)
return this;
var anim = GetComponent<Animator>();
var styleAnimators = FindObjectsOfType<StyleTransfer002Animator>().ToList();
var firstOfThisAnim = styleAnimators
.Where(x=> x.GetComponent<Animator>().avatar == anim.avatar)
.FirstOrDefault(x=> x.isFirstOfThisAnim);
if (firstOfThisAnim != null)
return firstOfThisAnim;
isFirstOfThisAnim = true;
return this;
}
// Mimics the positions of body parts of an animation. Computes AnimStep structure
// for the step if it is not computed already.
public void OnAgentAction() {
if (AnimationStepsReady){
MimicAnimation();
return;
}
if (_lastPosition == null)
Reset();
AnimatorStateInfo stateInfo = anim.GetCurrentAnimatorStateInfo(0);
AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
Lenght = stateInfo.length;
NormalizedTime = stateInfo.normalizedTime;
IsLoopingAnimation = stateInfo.loop;
var timeStep = stateInfo.length * stateInfo.normalizedTime;
var endTime = 1f;
if (IsLoopingAnimation)
endTime = 3f;
if (NormalizedTime <= endTime) {
MimicAnimation();
if (!AnimationStepsReady)
UpdateAnimationStep(timeStep);
}
else {
StopAnimation();
// BecomeRagDoll();
}
}
// Prepares an animation step. Records the positions, rotations, velocities
// of the rigid bodies forming an animation into the animation step structure.
void UpdateAnimationStep(float timeStep)
{
// HACK deal with two of first frame
if (NormalizedTime == 0f && AnimationSteps.FirstOrDefault(x=>x.NormalizedTime == 0f) != null)
return;
// var c = _master.Muscles.Count;
var c = BodyParts.Count;
var animStep = new AnimationStep();
animStep.TimeStep = timeStep;
animStep.NormalizedTime = NormalizedTime;
animStep.Velocities = Enumerable.Repeat(Vector3.zero, c).ToList();
animStep.VelocitiesLocal = Enumerable.Repeat(Vector3.zero, c).ToList();
animStep.AngularVelocities = Enumerable.Repeat(Vector3.zero, c).ToList();
animStep.AngularVelocitiesLocal = Enumerable.Repeat(Vector3.zero, c).ToList();
animStep.Positions = Enumerable.Repeat(Vector3.zero, c).ToList();
animStep.Rotations = Enumerable.Repeat(Quaternion.identity, c).ToList();
animStep.CenterOfMass = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
//animStep.CenterOfMass = GetCenterOfMass();
animStep.CenterOfMassVelocity = animStep.CenterOfMass - _lastCenterOfMass;
animStep.Names = BodyParts.Select(x=>x.Name).ToList();
animStep.SensorIsInTouch = new List<float>(SensorIsInTouch);
_lastCenterOfMass = animStep.CenterOfMass;
var rootBone = BodyParts[0];
foreach (var bodyPart in BodyParts)
{
var i = BodyParts.IndexOf(bodyPart);
if (i ==0) {
animStep.Rotations[i] = Quaternion.Inverse(bodyPart.InitialRootRotation) * bodyPart.Transform.rotation;
animStep.Positions[i] = bodyPart.Transform.position - bodyPart.InitialRootPosition;
}
else {
animStep.Rotations[i] = Quaternion.Inverse(rootBone.Transform.rotation) * bodyPart.Transform.rotation;
animStep.Positions[i] = bodyPart.Transform.position - rootBone.Transform.position;
}
if (NormalizedTime != 0f) {
animStep.Velocities[i] = (bodyPart.Transform.position - _lastPosition[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime); ;
animStep.AngularVelocities[i] = JointHelper002.CalcDeltaRotationNormalizedEuler(_lastRotation[i], bodyPart.Transform.rotation) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime); ;
animStep.VelocitiesLocal[i] = (animStep.Positions[i] - _lastPositionLocal[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime); ;
animStep.AngularVelocitiesLocal[i] = JointHelper002.CalcDeltaRotationNormalizedEuler(_lastRotationLocal[i], animStep.Rotations[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
}
if (bodyPart.Rigidbody != null) {
bodyPart.Rigidbody.angularVelocity = JointHelper002.CalcDeltaRotationNormalizedEuler(bodyPart.Transform.rotation, _lastRotation[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
bodyPart.Rigidbody.velocity = (bodyPart.Transform.position - _lastPosition[i]) / (_decisionRequester.DecisionPeriod * Time.fixedDeltaTime);
bodyPart.Rigidbody.transform.position = bodyPart.Transform.position;
bodyPart.Rigidbody.transform.rotation = bodyPart.Transform.rotation;
}
_lastPosition[i] = bodyPart.Transform.position;
_lastRotation[i] = bodyPart.Transform.rotation;
_lastPositionLocal[i] = animStep.Positions[i];
_lastRotationLocal[i] = animStep.Rotations[i];
}
animStep.TransformPosition = transform.position;
animStep.TransformRotation = transform.rotation;
animStep.AngularMoment = JointHelper002.GetAngularMoment(BodyParts);
AnimationSteps.Add(animStep);
}
// Sets kinematic flag for Animator's rigid bodies to true
public void BecomeAnimated()
{
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
foreach (var rb in _rigidbodies)
{
rb.isKinematic = true;
}
}
// Sets the kinematic flags for Animator's rigid bodies to false
public void BecomeRagDoll()
{
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
foreach (var rb in _rigidbodies)
{
rb.isKinematic = false;
}
}
// Stop the animation
public void StopAnimation()
{
AnimationStepsReady = true;
anim.enabled=false;
}
public void DestoryIfNotFirstAnim()
{
if (!isFirstOfThisAnim){
Destroy(this.gameObject);
}
}
// Sets positions and rotations of the Animator's body Rigid Bodies to match
// the positions and rotations of the animation avatar. The rotatin and position
// values passed as arguments to the MimicBone() calls are adjusted so that
// the avatar's body parts' positions and rotations look identical to the
// Animator's body parts
public void MimicAnimation()
{
if (!anim.enabled)
return;
MimicBone("butt", "mixamorig:Hips", new Vector3(.0f, -.055f, .0f), Quaternion.Euler(90, 0f, 0f));
MimicBone("lower_waist", "mixamorig:Spine", new Vector3(.0f, .0153f, .0f), Quaternion.Euler(90, 0f, 0f));
MimicBone("torso", "mixamorig:Spine2", new Vector3(.0f, .04f, .0f), Quaternion.Euler(90, 0f, 0f));
MimicBone("left_upper_arm", "mixamorig:LeftArm", "mixamorig:LeftForeArm", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, 45, 180));
MimicBone("left_larm", "mixamorig:LeftForeArm", "mixamorig:LeftHand", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, -180-45, 180));
MimicBone("right_upper_arm", "mixamorig:RightArm", "mixamorig:RightForeArm", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, 180-45, 180));
MimicBone("right_larm", "mixamorig:RightForeArm", "mixamorig:RightHand", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, 90-45, 180));
MimicBone("left_thigh", "mixamorig:LeftUpLeg", "mixamorig:LeftLeg", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, 0, 180));
MimicBone("left_shin", "mixamorig:LeftLeg", "mixamorig:LeftFoot", new Vector3(.0f, .02f, .0f), Quaternion.Euler(0, 0, 180));
MimicBone("right_thigh", "mixamorig:RightUpLeg", "mixamorig:RightLeg", new Vector3(.0f, .0f, .0f), Quaternion.Euler(0, 0, 180));
MimicBone("right_shin", "mixamorig:RightLeg", "mixamorig:RightFoot", new Vector3(.0f, .02f, .0f), Quaternion.Euler(0, 0, 180));
MimicRightFoot("right_right_foot", new Vector3(.0f, -.0f, -.0f), Quaternion.Euler(3, -90, 180));//3));
MimicLeftFoot("left_left_foot", new Vector3(-.0f, -.0f, -.0f), Quaternion.Euler(-8, -90, 180));//3));
}
// Set position and rotation of a rigid body to match avatar's rigid body
void MimicBone(string name, string bodyPartName, Vector3 offset, Quaternion rotationOffset)
{
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
var bodyPart = _transforms.First(x=>x.name == bodyPartName);
var target = _rigidbodies.First(x=>x.name == name);
target.transform.position = bodyPart.transform.position + offset;
target.transform.rotation = bodyPart.transform.rotation * rotationOffset;
}
// Set position and rotation of a rigid body to match avatar's rigid body
void MimicBone(string name, string animStartName, string animEndtName, Vector3 offset, Quaternion rotationOffset)
{
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
var animStartBone = _transforms.First(x=>x.name == animStartName);
var animEndBone = _transforms.First(x=>x.name == animEndtName);
var target = _rigidbodies.First(x=>x.name == name);
var pos = (animEndBone.transform.position - animStartBone.transform.position);
target.transform.position = animStartBone.transform.position + pos/2 + offset;
target.transform.rotation = animStartBone.transform.rotation * rotationOffset;
}
[Range(0f,1f)]
public float toePositionOffset = .3f;
[Range(0f,1f)]
public float toeRotationOffset = .7f;
// Set position and rotation of the left foot to match avatar's left foor
void MimicLeftFoot(string name, Vector3 offset, Quaternion rotationOffset)
{
string animStartName = "mixamorig:LeftFoot";
string animEndtName = "mixamorig:LeftToe_End";
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
var animStartBone = _transforms.First(x=>x.name == animStartName);
var animEndBone = _transforms.First(x=>x.name == animEndtName);
var target = _rigidbodies.First(x=>x.name == name);
var rotation = Quaternion.Lerp(animStartBone.rotation, animEndBone.rotation, toeRotationOffset);
var skinOffset = (animEndBone.transform.position - animStartBone.transform.position);
target.transform.position = animStartBone.transform.position + (skinOffset * toePositionOffset) + offset;
target.transform.rotation = rotation * rotationOffset;
}
// Set position and rotation of the right foot to match avatar's right foot
void MimicRightFoot(string name, Vector3 offset, Quaternion rotationOffset)
{
string animStartName = "mixamorig:RightFoot";
string animEndtName = "mixamorig:RightToe_End";
if (_rigidbodies == null || _transforms == null)
{
_rigidbodies = GetComponentsInChildren<Rigidbody>().ToList();
_transforms = GetComponentsInChildren<Transform>().ToList();
}
var animStartBone = _transforms.First(x=>x.name == animStartName);
var animEndBone = _transforms.First(x=>x.name == animEndtName);
var target = _rigidbodies.First(x=>x.name == name);
var rotation = Quaternion.Lerp(animStartBone.rotation, animEndBone.rotation, toeRotationOffset);
var skinOffset = (animEndBone.transform.position - animStartBone.transform.position);
target.transform.position = animStartBone.transform.position + (skinOffset * toePositionOffset) + offset;
target.transform.rotation = rotation * rotationOffset;
}
// Update the array of Sensors In Touch if an Animator's collider collides
// with an object named "Terrain"
public void OnSensorCollisionEnter(Collider sensorCollider, GameObject other)
{
if (string.Compare(other.name, "Terrain", true) !=0)
return;
var sensor = _sensors
.FirstOrDefault(x=>x == sensorCollider.gameObject);
if (sensor != null) {
var idx = _sensors.IndexOf(sensor);
SensorIsInTouch[idx] = 1f;
}
}
// Update the array of Sensors In Touch if a sensor no more collides with terrain
public void OnSensorCollisionExit(Collider sensorCollider, GameObject other)
{
if (string.Compare(other.gameObject.name, "Terrain", true) !=0)
return;
var sensor = _sensors
.FirstOrDefault(x=>x == sensorCollider.gameObject);
if (sensor != null) {
var idx = _sensors.IndexOf(sensor);
SensorIsInTouch[idx] = 0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3543848243544424b5503dcdc34970c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,437 @@
// The Master script is attached to the Agent object. Calculates the distance metrics
// used by the Agent to calculate rewards: distances between the rotations of agent's
// body parts and Animator's body parts, the distance between the angular momentum, and
// the center of masses.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Unity.MLAgents;
using System;
using ManyWorlds;
public class StyleTransfer002Master : MonoBehaviour {
public float FixedDeltaTime = 0.005f;
public bool visualizeAnimator = true;
// general observations
public List<Muscle002> Muscles;
public List<BodyPart002> BodyParts;
public float ObsPhase;
public Vector3 ObsCenterOfMass;
public Vector3 ObsAngularMoment;
public Vector3 ObsVelocity;
// model observations
// i.e. model = difference between mocap and actual)
// ideally we dont want to generate model at inference
public float EndEffectorDistance; // feet, hands, head
public float EndEffectorVelocityDistance; // feet, hands, head
public float JointAngularVelocityDistance;
public float JointAngularVelocityDistanceWorld;
public float RotationDistance;
public float CenterOfMassVelocityDistance;
public float CenterOfMassDistance;
public float AngularMomentDistance;
public float SensorDistance;
public float MaxEndEffectorDistance; // feet, hands, head
public float MaxEndEffectorVelocityDistance; // feet, hands, head
public float MaxJointAngularVelocityDistance;
public float MaxJointAngularVelocityDistanceWorld;
public float MaxRotationDistance;
public float MaxCenterOfMassVelocityDistance;
public float MaxCenterOfMassDistance;
public float MaxAngularMomentDistance;
public float MaxSensorDistance;
// debug variables
public bool IgnorRewardUntilObservation;
public float ErrorCutoff;
public bool DebugShowWithOffset;
public bool DebugMode;
public bool DebugDisableMotor;
[Range(-100,100)]
public int DebugAnimOffset;
public float TimeStep;
public int AnimationIndex;
public int EpisodeAnimationIndex;
public int StartAnimationIndex;
public bool UseRandomIndexForTraining;
public bool UseRandomIndexForInference;
public bool CameraFollowMe;
public Transform CameraTarget;
private bool _isDone;
bool _resetCenterOfMassOnLastUpdate;
bool _fakeVelocity;
bool _waitingForAnimation;
private StyleTransfer002Animator _muscleAnimator;
private StyleTransfer002Agent _agent;
StyleTransfer002Animator _styleAnimator;
StyleTransfer002Animator _localStyleAnimator;
DecisionRequester _decisionRequester;
public bool IsInferenceMode;
bool _phaseIsRunning;
Vector3 _lastCenterOfMass;
public BodyConfig BodyConfig;
// Initialize the Agent. Sets up Body Parts, Muscles.
public void OnInitializeAgent()
{
foreach (var rb in GetComponentsInChildren<Rigidbody>())
{
if (rb.useGravity == false)
rb.solverVelocityIterations = 255;
}
var masters = FindObjectsOfType<StyleTransfer002Master>().ToList();
if (masters.Count(x=>x.CameraFollowMe) < 1)
CameraFollowMe = true;
Time.fixedDeltaTime = FixedDeltaTime;
_waitingForAnimation = true;
_decisionRequester = GetComponent<DecisionRequester>();
BodyParts = new List<BodyPart002> ();
BodyPart002 root = null;
foreach (var t in GetComponentsInChildren<Transform>())
{
if (BodyConfig.GetBodyPartGroup(t.name) == BodyHelper002.BodyPartGroup.None)
continue;
var bodyPart = new BodyPart002{
Rigidbody = t.GetComponent<Rigidbody>(),
Transform = t,
Name = t.name,
Group = BodyConfig.GetBodyPartGroup(t.name),
};
if (bodyPart.Group == BodyConfig.GetRootBodyPart())
root = bodyPart;
bodyPart.Root = root;
bodyPart.Init();
BodyParts.Add(bodyPart);
}
var partCount = BodyParts.Count;
Muscles = new List<Muscle002> ();
var muscles = GetComponentsInChildren<ConfigurableJoint>();
ConfigurableJoint rootConfigurableJoint = null;
var ragDoll = GetComponent<RagDoll002>();
foreach (var m in muscles)
{
var maximumForce = ragDoll.MusclePowers.First(x=>x.Muscle == m.name).PowerVector;
var muscle = new Muscle002{
Rigidbody = m.GetComponent<Rigidbody>(),
Transform = m.GetComponent<Transform>(),
ConfigurableJoint = m,
Name = m.name,
Group = BodyConfig.GetMuscleGroup(m.name),
MaximumForce = maximumForce
};
if (muscle.Group == BodyConfig.GetRootMuscle())
rootConfigurableJoint = muscle.ConfigurableJoint;
muscle.RootConfigurableJoint = rootConfigurableJoint;
muscle.Init();
Muscles.Add(muscle);
}
var spawnableEnv = GetComponentInParent<SpawnableEnv>();
_localStyleAnimator = spawnableEnv.gameObject.GetComponentInChildren<StyleTransfer002Animator>();
_styleAnimator = _localStyleAnimator.GetFirstOfThisAnim();
_muscleAnimator = _styleAnimator;
_agent = GetComponent<StyleTransfer002Agent>();
IsInferenceMode = !Academy.Instance.IsCommunicatorOn;
}
// Update is called once per frame
void Update () {
}
public void OnAgentAction()
{
if (_waitingForAnimation && _styleAnimator.AnimationStepsReady){
_waitingForAnimation = false;
ResetPhase();
}
var animStep = UpdateObservations();
Step(animStep);
}
// Calculate values like ObsCenterOfMass, ObsAngularMoment that will be used
// as observations fed into the neural network for training and inference. Also
// calculates the distance metrics between animation's and agents' body parts.
// The distances are used by the Agent to calculate rewards. The max distances
// can be used to manually tune the denominators in exp(-distance/denominator)
// when forming reward function.
StyleTransfer002Animator.AnimationStep UpdateObservations()
{
if (DebugMode)
AnimationIndex = 0;
var debugStepIdx = AnimationIndex;
StyleTransfer002Animator.AnimationStep animStep = null;
StyleTransfer002Animator.AnimationStep debugAnimStep = null;
if (_phaseIsRunning) {
debugStepIdx += DebugAnimOffset;
if (DebugShowWithOffset){
debugStepIdx = Mathf.Clamp(debugStepIdx, 0, _muscleAnimator.AnimationSteps.Count);
debugAnimStep = _muscleAnimator.AnimationSteps[debugStepIdx];
}
animStep = _muscleAnimator.AnimationSteps[AnimationIndex];
}
EndEffectorDistance = 0f;
EndEffectorVelocityDistance = 0;
JointAngularVelocityDistance = 0;
JointAngularVelocityDistanceWorld = 0;
RotationDistance = 0f;
CenterOfMassVelocityDistance = 0f;
CenterOfMassDistance = 0f;
AngularMomentDistance = 0f;
SensorDistance = 0f;
if (_phaseIsRunning && DebugShowWithOffset)
MimicAnimationFrame(debugAnimStep);
else if (_phaseIsRunning)
CompareAnimationFrame(animStep);
foreach (var muscle in Muscles)
{
var i = Muscles.IndexOf(muscle);
muscle.UpdateObservations();
if (!DebugShowWithOffset && !DebugDisableMotor)
muscle.UpdateMotor();
if (!muscle.Rigidbody.useGravity)
continue; // skip sub joints
}
foreach (var bodyPart in BodyParts)
{
if (_phaseIsRunning){
bodyPart.UpdateObservations();
var rotDistance = bodyPart.ObsAngleDeltaFromAnimationRotation;
var squareRotDistance = Mathf.Pow(rotDistance,2);
RotationDistance += squareRotDistance;
JointAngularVelocityDistance += bodyPart.ObsDeltaFromAnimationAngularVelocity.sqrMagnitude;
JointAngularVelocityDistanceWorld += bodyPart.ObsDeltaFromAnimationAngularVelocityWorld.sqrMagnitude;
if (bodyPart.Group == BodyHelper002.BodyPartGroup.Hand
|| bodyPart.Group == BodyHelper002.BodyPartGroup.Torso
|| bodyPart.Group == BodyHelper002.BodyPartGroup.Foot)
{
EndEffectorDistance += bodyPart.ObsDeltaFromAnimationPosition.sqrMagnitude;
EndEffectorVelocityDistance += bodyPart.ObsDeltaFromAnimationVelocity.sqrMagnitude;
}
}
}
ObsCenterOfMass = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
ObsAngularMoment = JointHelper002.GetAngularMoment(BodyParts);
if (_phaseIsRunning) {
CenterOfMassDistance = (animStep.CenterOfMass - ObsCenterOfMass).sqrMagnitude;
AngularMomentDistance = (animStep.AngularMoment - ObsAngularMoment).sqrMagnitude;
}
ObsVelocity = ObsCenterOfMass - _lastCenterOfMass;
if (_fakeVelocity)
ObsVelocity = animStep.CenterOfMassVelocity;
_lastCenterOfMass = ObsCenterOfMass;
if (!_resetCenterOfMassOnLastUpdate)
_fakeVelocity = false;
if (_phaseIsRunning){
var animVelocity = animStep.CenterOfMassVelocity / (Time.fixedDeltaTime * _decisionRequester.DecisionPeriod);
ObsVelocity /= (Time.fixedDeltaTime * _decisionRequester.DecisionPeriod);
CenterOfMassVelocityDistance = (ObsVelocity - animVelocity).sqrMagnitude;
SensorDistance = 0.0f;
var sensorDistanceStep = 1.0f / _agent.SensorIsInTouch.Count;
for (int i = 0; i < _agent.SensorIsInTouch.Count; i++)
{
if (animStep.SensorIsInTouch[i] != _agent.SensorIsInTouch[i]) {
SensorDistance += sensorDistanceStep;
}
}
}
if (!IgnorRewardUntilObservation){
MaxEndEffectorDistance = Mathf.Max(MaxEndEffectorDistance, EndEffectorDistance);
MaxEndEffectorVelocityDistance = Mathf.Max(MaxEndEffectorVelocityDistance, EndEffectorVelocityDistance);
MaxRotationDistance = Mathf.Max(MaxRotationDistance, RotationDistance);
MaxCenterOfMassVelocityDistance = Mathf.Max(MaxCenterOfMassVelocityDistance, CenterOfMassVelocityDistance);
MaxEndEffectorVelocityDistance = Mathf.Max(MaxEndEffectorVelocityDistance, EndEffectorVelocityDistance);
MaxJointAngularVelocityDistance = Mathf.Max(MaxJointAngularVelocityDistance, JointAngularVelocityDistance);
MaxJointAngularVelocityDistanceWorld = Mathf.Max(MaxJointAngularVelocityDistanceWorld, JointAngularVelocityDistanceWorld);
MaxCenterOfMassDistance = Mathf.Max(MaxCenterOfMassDistance, CenterOfMassDistance);
MaxAngularMomentDistance = Mathf.Max(MaxAngularMomentDistance, AngularMomentDistance);
MaxSensorDistance = Mathf.Max(MaxSensorDistance, SensorDistance);
}
if (IgnorRewardUntilObservation)
IgnorRewardUntilObservation = false;
ObsPhase = _muscleAnimator.AnimationSteps[AnimationIndex].NormalizedTime % 1f;
return animStep;
}
// Increment animation index. The index is used to get the current AnimStep structure.
void Step(StyleTransfer002Animator.AnimationStep animStep)
{
if (_phaseIsRunning){
if (!DebugShowWithOffset)
AnimationIndex++;
if (AnimationIndex>=_muscleAnimator.AnimationSteps.Count) {
EndEpisode();
AnimationIndex--;
}
}
if (_phaseIsRunning && IsInferenceMode && CameraFollowMe)
{
_muscleAnimator.anim.enabled = true;
_muscleAnimator.anim.Play("Record",0, animStep.NormalizedTime);
_muscleAnimator.anim.transform.position = animStep.TransformPosition;
_muscleAnimator.anim.transform.rotation = animStep.TransformRotation;
}
}
void CompareAnimationFrame(StyleTransfer002Animator.AnimationStep animStep)
{
MimicAnimationFrame(animStep, true);
}
// Moves Agent's body part to animation's position and rotation if onlySetAnimation = false.
// Then sets the target animation for each Agent's body part. The animation's
// positions and rotations are later on used by the Body Part object to calculate
// distance metrics to the target animation.
void MimicAnimationFrame(StyleTransfer002Animator.AnimationStep animStep, bool onlySetAnimation = false)
{
if (!onlySetAnimation)
{
foreach (var rb in GetComponentsInChildren<Rigidbody>())
{
rb.angularVelocity = Vector3.zero;
rb.velocity = Vector3.zero;
}
}
foreach (var bodyPart in BodyParts)
{
var i = animStep.Names.IndexOf(bodyPart.Name);
Vector3 animPosition = bodyPart.InitialRootPosition + animStep.Positions[0];
Quaternion animRotation = bodyPart.InitialRootRotation * animStep.Rotations[0];
if (i != 0) {
animPosition += animStep.Positions[i];
animRotation *= animStep.Rotations[i];
}
Vector3 angularVelocity = animStep.AngularVelocities[i];
Vector3 velocity = animStep.Velocities[i];
Vector3 angularVelocityLocal = animStep.AngularVelocitiesLocal[i];
Vector3 velocityLocal = animStep.VelocitiesLocal[i];
bool setAnim = !onlySetAnimation;
if (bodyPart.Name.Contains("head") || bodyPart.Name.Contains("upper_waist"))
setAnim = false;
if (setAnim) {
bodyPart.MoveToAnim(animPosition, animRotation, angularVelocity, velocity);
}
bodyPart.SetAnimationPosition(animPosition, animStep.Rotations[i], velocity, angularVelocityLocal, angularVelocity);
}
}
protected virtual void LateUpdate() {
if (_resetCenterOfMassOnLastUpdate){
ObsCenterOfMass = JointHelper002.GetCenterOfMassRelativeToRoot(BodyParts);
//ObsCenterOfMass = GetCenterOfMass();
_lastCenterOfMass = ObsCenterOfMass;
_resetCenterOfMassOnLastUpdate = false;
}
#if UNITY_EDITOR
VisualizeTargetPose();
#endif
}
public bool IsDone()
{
return _isDone;
}
void EndEpisode()
{
_isDone = true;
}
public void ResetPhase()
{
if (_waitingForAnimation)
return;
_decisionRequester.enabled = true;
_agent.SetTotalAnimFrames(_muscleAnimator.AnimationSteps.Count);
SetStartIndex(0); // HACK for gym
UpdateObservations();
}
public void SetStartIndex(int startIdx)
{
_decisionRequester.enabled = false;
if (!_phaseIsRunning){
StartAnimationIndex = _muscleAnimator.AnimationSteps.Count-1;
EpisodeAnimationIndex = _muscleAnimator.AnimationSteps.Count-1;
AnimationIndex = EpisodeAnimationIndex;
if (CameraFollowMe){
var camera = FindObjectOfType<Camera>();
var follow = camera.GetComponent<SmoothFollow>();
follow.target = CameraTarget;
}
}
AnimationIndex = startIdx;
if (_decisionRequester?.DecisionPeriod > 1)
AnimationIndex *= this._decisionRequester.DecisionPeriod;
StartAnimationIndex = AnimationIndex;
EpisodeAnimationIndex = AnimationIndex;
_phaseIsRunning = true;
_isDone = false;
var animStep = _muscleAnimator.AnimationSteps[AnimationIndex];
TimeStep = animStep.TimeStep;
EndEffectorDistance = 0f;
EndEffectorVelocityDistance = 0f;
JointAngularVelocityDistance = 0;
JointAngularVelocityDistanceWorld = 0;
RotationDistance = 0f;
CenterOfMassVelocityDistance = 0f;
IgnorRewardUntilObservation = true;
_resetCenterOfMassOnLastUpdate = true;
_fakeVelocity = true;
foreach (var muscle in Muscles)
muscle.Init();
foreach (var bodyPart in BodyParts)
bodyPart.Init();
MimicAnimationFrame(animStep);
EpisodeAnimationIndex = AnimationIndex;
}
private void VisualizeTargetPose() {
if (!visualizeAnimator) return;
if (!Application.isEditor) return;
}
// Recursively visualizes a bone hierarchy
private void VisualizeHierarchy(Transform t, Color color) {
for (int i = 0; i < t.childCount; i++) {
Debug.DrawLine(t.position, t.GetChild(i).position, color);
VisualizeHierarchy(t.GetChild(i), color);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbee4689469f148ca897ef6aabbab08d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: