2.使用ClampReward
if(koboldBodies[i].damageCoef > 0f){clampReward += -0.1f * koboldBodies[i].damageCoef;}
3.
//Set: judge.endEpisode = false//Set: nearModeRange = 1f//Set: weapon, tail is not weakness. If is, Stand would back to GetUp//Set: calf is not weaknessif(weaknessOnGround){// LogWeaknessOnGround();if(inferenceMode){brainMode = BrainMode.GetUp;SetModel("KoboldGetUp", getUpBrain);behaviorParameters.BehaviorType = BehaviorType.InferenceOnly;}else{AddReward(-1f);judge.outLife++;judge.Reset();return;}}else if(koboldRoot.localPosition.y < -10f){if(inferenceMode){brainMode = BrainMode.GetUp;SetModel("KoboldGetUp", getUpBrain);behaviorParameters.BehaviorType = BehaviorType.InferenceOnly;}else{AddReward(-1f);judge.outY++;judge.Reset();return;}}else{targetSmoothPosition = targetPositionBuffer.GetSmoothVal();headDir = targetSmoothPosition - stageBase.InverseTransformPoint(koboldHeadRb.position);rootDir = targetSmoothPosition - stageBase.InverseTransformPoint(koboldRootRb.position);flatTargetVelocity = rootDir;flatTargetVelocity.y = 0f;targetDistance = flatTargetVelocity.magnitude;//Naruto ArmVector3 flatLeftDir = Vector3.Cross(flatTargetVelocity, Vector3.up);lookAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldHead.up, headDir));//Side LookupAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldHead.forward, flatLeftDir));aimVelocity = flatTargetVelocity.normalized;aimVelocity.y = 0.2f;//LeanVector3 leanDir = rootAimRot * flatTargetVelocity;spineUpAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldSpine.right * -1f, leanDir));rootUpAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldRoot.up, leanDir));leftUpperArmAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldLeftUpperArm.right, leftUpperArmAimRot * flatTargetVelocity));leftForeArmAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldLeftForeArm.right, leftForeArmAimRot * flatTargetVelocity));rightUpperArmAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldRightUpperArm.right, rightUpperArmAimRot * flatTargetVelocity));rightForeArmAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldRightForeArm.right, rightForeArmAimRot * flatTargetVelocity));weaponAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldWeapon.up, weaponAimRot * flatTargetVelocity));tailRootAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldTailRoot.right, flatTargetVelocity));tailMidAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldTailMid.right, flatTargetVelocity));tailTopAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldTailTop.right, flatTargetVelocity));leftThighAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldLeftThigh.forward * -1f, flatLeftDir));rightThighAngle = Mathf.InverseLerp(180f, 0f, Vector3.Angle(koboldRightThigh.forward * -1f, flatLeftDir));avgVelocity = velocityBuffer.GetSmoothVal();velocityAngle = Vector3.Angle(avgVelocity, aimVelocity);velocityAngleCoef = Mathf.InverseLerp(180f, 0f, velocityAngle);flatVelocity = avgVelocity;flatVelocity.y = 0f;flatVelocityManitude = flatVelocity.magnitude;velocityCoef = Mathf.InverseLerp(0f, 10f, Vector3.Project(avgVelocity, aimVelocity).magnitude );flatVelocityAngle = Vector3.Angle(flatVelocity, flatTargetVelocity);if(!inferenceMode){if(targetDistance > nearModeRange){if(Time.fixedTime - landingMoment > landingBufferTime){bool outSpeed = flatVelocityManitude < Mathf.Lerp(0f, 7f, (Time.fixedTime - landingMoment - landingBufferTime)/4f);bool outDirection = flatVelocityAngle > Mathf.Lerp(180f, 10f, (Time.fixedTime - landingMoment - landingBufferTime)/4f);float motionLimit = Mathf.Lerp(0f, 0.5f, (Time.fixedTime - landingMoment - landingBufferTime)/4f);float motionLimit2 = Mathf.Lerp(0f, 0.7f, (Time.fixedTime - landingMoment - landingBufferTime)/4f);bool outMotion = lookAngle < motionLimit2 || upAngle < motionLimit2 || leftThighAngle < motionLimit2 || rightThighAngle < motionLimit2 || spineUpAngle < motionLimit || rootUpAngle < motionLimit || leftUpperArmAngle < motionLimit || leftForeArmAngle < motionLimit || rightUpperArmAngle < motionLimit || rightForeArmAngle < motionLimit|| weaponAngle < motionLimit;if( outSpeed || outDirection || outMotion){AddReward(-1f);if(outSpeed){judge.outSpeed++;}if(outDirection){judge.outDirection++;}if(outMotion){judge.outMotion++;}judge.Reset();return;}}lastReward = (velocityAngleCoef + velocityCoef) * 0.02f + (lookAngle+upAngle) * 0.0125f + (leftThighAngle+rightThighAngle) * 0.0075f+ (spineUpAngle+rootUpAngle) * 0.005f+ (leftUpperArmAngle+leftForeArmAngle+rightUpperArmAngle+rightForeArmAngle+weaponAngle+tailRootAngle+tailMidAngle+tailTopAngle ) * 0.001f+ (1f - exertionRatio) * 0.002f;if(useClampReward){lastReward = lastReward+clampReward;if(lastReward < 0f) lastReward = 0f;}totalReward += lastReward;AddReward( lastReward );}// else if(targetDistance > 1.5f)else{// AddReward(1f);judge.survived++;judge.Reset();return;}}}
//大致來說,
--1.獎勵視線,並使用Force Sharping
--2.獎勵投影至"跑動推薦向量"的速度和角度,並使用Force Sharping
--3.獎勵Root、Spine、雙臂特定向量(forward/up/right)符合指定角度,並使用Force Sharping
--4.獎勵尾巴全體符合指定角度,但"並不使用Force Sharping"
--5.獎勵減少動作變化
實驗時間:
Step: 5e7
Time Elapsed: 73029s (20.29hr)
實驗結果:
實驗結果為大致成功,小部分失敗
以觀測結論來說,比例1.3以上的狗頭人哨兵都變得可以進行全程奔跑
但是比例1.3以下的狗頭人哨兵,比例越小就越不擅長全程奔跑
分析原因的話
假說1.訓練數不足
可能比例區間與沒有進行觀察項Localize,導致部分比例訓練數不足,但這不能解釋為什麼比例越小越糟的問題
2.常態分佈
Model會建立成能泛用於所有比例的,因此才會造成兩端的適應性不好,但這不能解釋為什麼大比例那端其實穩定性超級好
3.強者恆強
奔跑的設計存在一個瑕疵,為了訓練出無限奔跑,會讓他盡可能跑到極限長度
但如果在某個瞬間大比例比較能多跑幾步,那麼訓練步數就可能會開始失衡
也就是大比例可能會因此每次都能多累積一些步數當作實驗數據並修正動作
因此最後會導致雖然比例是隨機的,但是一旦脫穎而出的比例就會遠比其他比例更優化,並壓迫到其他比例的訓練步數
但這不能解釋為什麼為什麼包含前幾次訓練都是比例越大越穩定,除非納入假說4、5
4.ForceSharping瑕疵
對於比例縮放,ForceSharping存在著瑕疵,在對於速度的要求,並沒有要求比例越大的角色必須跑越快,至是希望不管身型,狗頭人哨兵接近的速度都差不多,否則想像俠客歐尼爾可以用每秒15公尺以上的速度衝過來,這種比閃電伯爾特還快的速度,大概會無從反應和對抗
但是這種設計也導致比例越大的角色,越容易通過對於速度的ForceSharping,因為就算是同樣步頻的跨步,身高越高的角色一步就是比較遠比較快
這個假說可以解釋為什麼比例越大的角色越穩定的問題
5.物理系統瑕疵
Unity物理系統絕非完美無瑕的物理模擬,很多Unity新手常見的指責就是,如果用超高的速度發射一枚子彈,則子彈有很高機率會穿透一面很薄的牆,並因此認為Unity Bug很多
但實際上這應該是物理模擬和運算效能間的平衡取捨,如果真的希望對於極端的物理進行精密計算,Unity內要去調整碰裝偵測類型,並且可以提高Physic內的Iteration等相關設定
總之,如果不付出效能為代價,物理系統的模擬絕非完美無瑕,因此假設其實Unity模擬可能每個Rigidbody在某些時候都會有1cm的誤差
那麼像狗頭人哨兵腳掌很薄,脛骨護具又超厚
有可能正是這1cm的誤差就導致腳掌會偶爾被意外穿透,或是在往前奔跑時脛骨護具會觸地但是比例越大的狗頭人,腳掌就越厚,脛骨護具離地高度越高
與此同時,Unity物理的誤差還是1cm的話,就變成發生機率越低
這個假說也能解釋為什麼比例越高的狗頭人就越穩定
有鑑於比例超過1.3之後其實都相當穩定,因此假說1的Localize和假說2都可能不成立
但假說345都可能成立,並且產生疊加效果
另外一個顯著問題是ForceSharping的演算法設計
ForceSharping設計成一旦不符理想就立即淘汰,但這個設計讓人偶對於抵禦意外的能力很低
例如其實輕微失衡,只要能矯正回來也很好,但可以看到狗頭人幾乎一旦失衡就是放棄狀態
那麼稍微思考一下吧
這個研究的長期目的是 "建立紅蓮人偶的技術體系"
因此就算當下成果不是厲害的行為表現也沒關係,但數據很有意義就能大幅推進技術體系的進程
因此只要有意義,不管哪個實驗都值得進行
但短期目的是 "希望能用來出產Demo影片並參加某競賽"
如果是這個目的的話,在時間內盡可能產出好的行為表現才是理想
所以下個研究最好以有容易有好的行為表現又有數據為理想
再考慮今天和這周的行程
適合研究的是3,可以輕易把比例從平均隨機,調整成線性或球型隨機,讓表現容易不好的小比例更容易被訓練到
然後並同時實驗是否能把ForceSharping改為有一定容錯範圍的演算法
因此下個實驗是狗頭人追逐
1.比例從從平均隨機,調整成線性或球型隨機,比例越小機率越高
2.ForceSharping改為有一定容錯範圍的演算法