質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

3回答

5631閲覧

ローカル座標軸を使ったオブジェクトの動きの抑制

DY2peace

総合スコア20

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2020/10/21 12:34

編集2020/11/12 05:04

前提・実現したいこと

 ConfigurableJoint連結したカプセルをAddRelativeTorqueを使って曲げています。
曲げる際、オレンジカプセルが曲がった状態のままピタッと止めるようにし、重力を働かせたら曲げ状態を維持したまま落ちるようにしたいと考えてます。
イメージ説明

イメージ説明

イメージ説明

発生している問題・エラーメッセージ

 Torqueを使いながら曲げると慣性が働いてしまい、ゲームコントローラーの入力がないときにピタッと止めることができませんでした。
またRigidbodyの『Constraints』のFreezeを使うと今度は、ワールド座標で位置が固定されてしまい(空中に浮いた状態)、重力を働かせることができませんでした。

ほかに試したこと

1.慣性をなくす方法で、以下のURL記載の方法も試しました。
https://teratail.com/questions/105824
↓の速度をゼロにしてしまうと、本来効かせたい重力分までゼロになってしまいます。

C#

1rigid.velocity = Vector3.zero;

2.RigidBodyのローカルにConstraintsを働かせる
こちらも試してみました。
https://answers.unity.com/questions/404420/rigidbody-constraints-in-local-space.html
真似して以下のように記載しましたが、ピタッと止めることはできませんでした。

C#

1Vector3 LocalSpeed = transform.InverseTransformDirection(rigid.velocity); 2LocalSpeed.x = 0; 3LocalSpeed.y = 0; 4LocalSpeed.z = 0;

ほかにも『GetRelativePoitVelocity』や『magnitude』を使って、曲げスピードに制限をかけるなども試してみましたが、結果は変わらず、ピタッと止めることができませんでした。

やりたいこと

 オレンジカプセルを青色カプセルの子に配置し、
『ゲームコントローラの入力がないとき』
⇒『オレンジカプセルのローカル座標だけに、RigidBodyのConstraintsのFreezePositonを働かせる』
ができれば上記の問題が解決できるのではないかと考えてます。

初歩的な質問で申し訳ございませんが、どなたかご教示いただけると幸いです。
以上です。

###追記(2020年11月12日)ローカル座標を使ったストップ
回転軸をわかりやすくするため、Sphereで説明します。黄色ボールがスクリプトを当ててトルクで回転させたい部品です。
イメージ説明

ヒエラルキーの階層は、↓のように段階的に親子関係を作ってます。根本となる青ボールが『Sphere』です。
イメージ説明

上記の階層構成を取ることで、個々の黄色ボールのTransformのZ角(25度)の値をきれいに取得することができます。
イメージ説明
↓曲げたときの画像
イメージ説明
親子関係を取らないと、根本(青ボール側)の角度の合算値が出てしまいます。角度指定が難しくなるかと思います。

この階層構成を使い、黄色ボールのローカルZ角度座標を使って、うまく慣性ストップ・位置固定できないかと考えております。例えば、狙いの角度30度曲がれ!と設定して、30度に近づいたら徐々にスピードが弱まり、30度位置に来たら、ローカル座標Z30度だけを位置キープ(フリーズ)する ということができないか考えております。

以上です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

オレンジカプセルの角度を動かしたくない時だけFixedJointを増設して、両者の位置関係をがっちり拘束してしまう...というのはどうでしょうかね?

C#

1using UnityEngine; 2 3[RequireComponent(typeof(Rigidbody), typeof(ConfigurableJoint))] 4public class CapsuleController : MonoBehaviour 5{ 6 [SerializeField] private float torqueMagnitude = 5.0f; 7 private Rigidbody thisRigidbody; 8 private Rigidbody parentRigidbody; 9 private FixedJoint fixedJoint; 10 11 private float input; 12 13 private void Start() 14 { 15 this.thisRigidbody = this.GetComponent<Rigidbody>(); // オレンジ色のカプセル 16 this.parentRigidbody = this.transform.parent.GetComponent<Rigidbody>(); // 青色のカプセル 17 this.SetKinematic(true); 18 } 19 20 private void SetKinematic(bool value) 21 { 22 if (value) 23 { 24 this.parentRigidbody.isKinematic = true; 25 this.thisRigidbody.useGravity = false; 26 this.thisRigidbody.velocity = Vector3.zero; 27 this.thisRigidbody.angularVelocity = Vector3.zero; 28 this.Lock(); 29 } 30 else 31 { 32 this.parentRigidbody.isKinematic = false; 33 this.thisRigidbody.useGravity = true; 34 this.Lock(); 35 } 36 } 37 38 private void Lock() 39 { 40 if (this.fixedJoint != null) 41 { 42 return; 43 } 44 45 this.fixedJoint = this.gameObject.AddComponent<FixedJoint>(); 46 this.fixedJoint.connectedBody = this.parentRigidbody; 47 } 48 49 private void Free() 50 { 51 if (this.fixedJoint == null) 52 { 53 return; 54 } 55 56 Destroy(this.fixedJoint); 57 this.fixedJoint = null; 58 } 59 60 private void Update() 61 { 62 this.input = this.parentRigidbody.isKinematic ? Input.GetAxis("Vertical") : 0.0f; 63 64 // 入力がなければFixedJointを取り付けてロック、入力があれば解除 65 if (Mathf.Approximately(this.input, 0.0f)) 66 { 67 this.Lock(); 68 } 69 else 70 { 71 this.Free(); 72 } 73 74 // スペースバーで青カプセルのisKinematicをオフ、 75 // オレンジカプセルのuseGravityをオンにして落下開始 76 if (Input.GetKeyDown(KeyCode.Space)) 77 { 78 this.SetKinematic(false); 79 } 80 } 81 82 private void FixedUpdate() 83 { 84 // 今回の実験では、オレンジカプセルの回転軸がローカルX軸になるようにしてしまったため 85 // X軸周りのトルクをかけていますが、ご質問者さんの場合はY軸ということでしょうかね? 86 this.thisRigidbody.AddRelativeTorque(this.input * this.torqueMagnitude, 0.0f, 0.0f); 87 } 88}

図

##追記

根拠のない直感なんですが、ヘビ型ロボットのようなものを作る布石ということを念頭に置くと、個々の体節が駆動する仕組みはなるべくシンプルにしておいた方がいいような予感がします。
駆動方法についても、個人的には当初のAddRelativeTorqueによる方法は「オレンジカプセルを指でつまんでひねる」といったイメージ...つまりヘビ本体の動力ではなく、外部からの力で回転しているような感じがしました。それよりもジョイント部分に仕込まれたモーターで回転させるイメージの方がしっくりくる気がするのですが、いかがでしょうかね?

オレンジカプセルのスクリプトを下記のようにしてみました。本質的なのはUpdateFixedUpdateで、Start内ではジョイントのパラメーターを設定しているだけです。
ここで設定しなくてもジョイントのインスペクター上に直接入力すればいいのですが、やたら設定項目が多くてどれをいじったか忘れてしまいそうになりこのようにしました。

C#

1using UnityEngine; 2 3[RequireComponent(typeof(ConfigurableJoint))] 4public class CapsuleController2 : MonoBehaviour 5{ 6 [SerializeField] private float angularSpeedInDegrees = 120.0f; 7 [SerializeField] private float damper = 100.0f; 8 [SerializeField] private float angularLimitInDegrees = 60.0f; 9 10 private ConfigurableJoint configurableJoint; 11 private float input; 12 13 private void Start() 14 { 15 this.configurableJoint = this.GetComponent<ConfigurableJoint>(); 16 this.configurableJoint.xMotion = ConfigurableJointMotion.Locked; 17 this.configurableJoint.yMotion = ConfigurableJointMotion.Locked; 18 this.configurableJoint.zMotion = ConfigurableJointMotion.Locked; 19 this.configurableJoint.angularXMotion = ConfigurableJointMotion.Limited; 20 this.configurableJoint.angularYMotion = ConfigurableJointMotion.Locked; 21 this.configurableJoint.angularZMotion = ConfigurableJointMotion.Locked; 22 this.configurableJoint.highAngularXLimit = new SoftJointLimit 23 { 24 limit = this.angularLimitInDegrees 25 }; 26 this.configurableJoint.lowAngularXLimit = new SoftJointLimit 27 { 28 limit = -this.angularLimitInDegrees 29 }; 30 this.configurableJoint.angularXDrive = new JointDrive 31 { 32 maximumForce = float.MaxValue, 33 positionSpring = 0.0f, 34 positionDamper = this.damper 35 }; 36 } 37 38 private void Update() 39 { 40 this.input = Input.GetAxis("Vertical"); 41 } 42 43 private void FixedUpdate() 44 { 45 this.configurableJoint.targetAngularVelocity = new Vector3( 46 this.input * this.angularSpeedInDegrees * Mathf.Deg2Rad, 47 0.0f, 48 0.0f); 49 } 50}

また、両カプセルのコライダーはBoxColliderに差し替えました。丸いカプセルではなく角柱にすることで、地面の上に落ちた後ふとしたはずみにコケてしまうのを防ごうという魂胆です。Freeze Rotation Yをオンにすることも考えたのですが、力の解決の結果すごく大きな力がかかることがあるようで、たまに空高く吹っ飛んでしまうことがありました。

図1

他にも、青カプセルとオレンジカプセルを親子関係にするのはやめました。親子関係になっていると、青カプセルが移動・回転した時に子オブジェクトであるオレンジカプセルも移動・回転してしまうことになります。経験上、物理シミュレーションの制御下にあるオブジェクトの位置や姿勢をダイレクトに操作するとろくなことにならないと思っておりまして、このようなオレンジカプセルの移動・回転もそれと同様の性質を持つだろうと考えてのことです。

動かしてみると下図のようになりました。コメント欄で申し上げたたとえ話の、スカイダイビングをする人のような挙動になっています。

図2

また、このようなコンセプトで駆動する体節を鎖状に繋げるとどんな挙動をするだろうかと気になりまして、下図のようにオブジェクトをセットアップしてみました。Headが青カプセル、Segment1~9がオレンジカプセルで、Segment1~9はConfigurableJointを持っており、Segment1はHeadに、Segment2はSegment1に...といった具合に鎖状につながっています。
コライダーはHeadだけCapsuleCollider、他はBoxColliderとしました。

図3

HeadとSegment1~8はスクリプトを持っておらず、Segment9にだけ下記のようなスクリプトをアタッチしました。
CapsuleController2targetAngularVelocityを使ってモーターの速度を設定する方式なのに対し、こちらはtargetRotationを使って目標角度を与える方式になっているという差異はありますが、セグメント間のジョイントが動力になっているという点は同様です。

C#

1using System.Collections.Generic; 2using UnityEngine; 3 4[RequireComponent(typeof(ConfigurableJoint))] 5public class WormController : MonoBehaviour 6{ 7 [SerializeField] private float omegaInDegrees = 90.0f; 8 [SerializeField] private float timeOffset = 0.5f; 9 [SerializeField] private float spring = 500.0f; 10 [SerializeField] private float damper = 100.0f; 11 [SerializeField] private float angularLimitInDegrees = 60.0f; 12 13 private readonly List<ConfigurableJoint> joints = new List<ConfigurableJoint>(); 14 private float time; 15 16 private void Start() 17 { 18 var j = this.GetComponent<ConfigurableJoint>(); 19 do 20 { 21 j.xMotion = ConfigurableJointMotion.Locked; 22 j.yMotion = ConfigurableJointMotion.Locked; 23 j.zMotion = ConfigurableJointMotion.Locked; 24 j.angularXMotion = ConfigurableJointMotion.Free; 25 j.angularYMotion = ConfigurableJointMotion.Locked; 26 j.angularZMotion = ConfigurableJointMotion.Locked; 27 j.angularXDrive = new JointDrive 28 { 29 maximumForce = float.MaxValue, 30 positionSpring = this.spring, 31 positionDamper = this.damper 32 }; 33 this.joints.Add(j); 34 j = j.connectedBody.GetComponent<ConfigurableJoint>(); 35 } while (j != null); 36 } 37 38 private void FixedUpdate() 39 { 40 for (var i = 0; i < this.joints.Count; i++) 41 { 42 var j = this.joints[i]; 43 var t = Mathf.Max(this.time - (i * this.timeOffset), 0.0f); 44 var angle = this.angularLimitInDegrees * Mathf.Sin(t * this.omegaInDegrees * Mathf.Deg2Rad); 45 j.targetRotation = Quaternion.Euler(angle, 0.0f, 0.0f); 46 } 47 48 this.time += Time.deltaTime; 49 } 50}

実行してみると、おぞましい化け物のような動きをしました...

図4

##追記: targetRotationによるカプセル操作

C#

1using UnityEngine; 2 3[RequireComponent(typeof(ConfigurableJoint))] 4public class CapsuleController3 : MonoBehaviour 5{ 6 [SerializeField] private float angularSpeedInDegrees = 120.0f; 7 [SerializeField] private float spring = 500.0f; 8 [SerializeField] private float damper = 100.0f; 9 [SerializeField] private float angularLimitInDegrees = 60.0f; 10 11 private ConfigurableJoint configurableJoint; 12 private float angle; 13 14 private void Start() 15 { 16 this.configurableJoint = this.GetComponent<ConfigurableJoint>(); 17 this.configurableJoint.xMotion = ConfigurableJointMotion.Locked; 18 this.configurableJoint.yMotion = ConfigurableJointMotion.Locked; 19 this.configurableJoint.zMotion = ConfigurableJointMotion.Locked; 20 this.configurableJoint.angularXMotion = ConfigurableJointMotion.Free; 21 this.configurableJoint.angularYMotion = ConfigurableJointMotion.Locked; 22 this.configurableJoint.angularZMotion = ConfigurableJointMotion.Locked; 23 this.configurableJoint.angularXDrive = new JointDrive 24 { 25 maximumForce = float.MaxValue, 26 positionSpring = this.spring, 27 positionDamper = this.damper 28 }; 29 } 30 31 private void Update() 32 { 33 var input = Input.GetAxis("Vertical"); 34 this.angle = Mathf.Clamp( 35 this.angle + (this.angularSpeedInDegrees * Time.deltaTime * input), 36 -this.angularLimitInDegrees, 37 this.angularLimitInDegrees); 38 this.configurableJoint.targetRotation = Quaternion.Euler(this.angle, 0.0f, 0.0f); 39 } 40}

投稿2020/10/21 20:53

編集2020/11/10 20:00
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

DY2peace

2020/10/22 01:57

ご回答ありがとうございます。申し訳ございません。説明不足だったのですが、常に重力はかかっている状態、つまり根本側の青いカプセルはオレンジカプセル曲げ操作時にも落ち続けるが、青カプセルとオレンジカプセルの相対位置関係は変わらない状態にしたかったです。  iskinematicを使ってしまうと一瞬、空中で停止させないと曲げられない状態になるかと思います。 もし何かいい案がご教示いただけると幸いです。
Bongo

2020/10/22 03:22

なるほど...おっしゃる通り現状では空中で一旦停止してしまうと思います。 夕方からまたUnityをいじる時間ができそうですので、なにかいい手が思いつきましたら追記いたします。 ちょっとうかがいたいのですが、人体の下半身を青カプセル、上半身をオレンジカプセルに見立てて、地面に対してうつ伏せの姿勢で高所からスカイダイビングしたとします。 落下しながら腰を前に90°倒してお辞儀しようとすると、上半身と下半身が互いに作用・反作用を及ぼしあって、落下する人を地上から見ると上半身と下半身をどちらも45°地上の方に向けたハの字の姿勢になりそうな気がしますが、同じく落下中にオレンジカプセルにトルクをかけたときに青カプセルの方も反作用で回転した方が望ましいでしょうか。 あるいはそうではなく、オレンジカプセル回転中の青カプセルの姿勢は一定のままの方がいいでしょうか?
DY2peace

2020/10/22 06:17 編集

ご検討ありがとうございます。説明を省略させていただいたのですが、別の質問でさせていただいた蛇型の多関節ロボットの検討をしておりまして、このような質問をさせていただきました。蛇の持ち上がるような動きを物理計算で再現したいと考えていました。  実機の場合だと、青とオレンジのカプセルの点接触部分に強い摩擦が働いていて、重力で落ちることはありません。Unityで点接触で強い摩擦を働かせると、物理計算が大変になるため、今回のような手段をとっています。  ほかにもUnity最新版のPhysics4.1にしてArticulationBodyを使ったロボットアームで蛇の動きを再現しようとしたのですが、最後の絵にある持ち上がり状態を作ることはできませんでした。(単純にロボットアームの動きだけのSIMのため)  今回の方法が確立できましたら、ロボットアームで重たいものが持ち上げれるか、モーターの最適なトルクは?といったシミュレーションをUnityでできてくると思います。
Bongo

2020/10/22 13:25

なかなか面白そうな研究に挑戦していらっしゃるようですね。 駆動機構をジョイントの機能を使った形に変更したバージョンを追記しましたが、こういった方式はすでにお試しでしょうか? あいにくロボットのたぐいは門外漢でして、本物のロボットをシミュレートする上ではまだまだ考慮不足な点があるかもしれませんが、多少なりともご参考になれば幸いです。
DY2peace

2020/10/22 14:20

ご回答・ご協力いただきありがとうございます。また検討にお時間取らせてしまい申し訳ございません。 話が前後してしまいますが、これから上記の内容を確認させていただきます。こういったご相談できる相手が周囲にいなかったので、大変勉強になります。  実は、蛇型ロボットではなく、別の物の検討を行っております。動きが蛇に似ているので、そのように書かせていただきました。各関節には、モーターは配置されておらず、後端(青カプセル)側から先端に向けて、内部に通した紐でひっかけて引っ張るようなイメージです。(マリオネットのような感じです。)  Unity上で紐の表現は避けたかったので、Joint接続とトルク、質量調整でなんとか入力動作時だけは実機に近づけることができました。  ですが各関節接触部分の強い摩擦の表現がUnityではできなかったので、慣性をなくす方法を探しておりました。  いただいた方法を少し時間をかけて読まさせていただきます。しばらくお待ちいただけると幸いです。
DY2peace

2020/11/10 10:26

ご回答遅れて申し訳ございません。2つとも試してみたのですが、最初の絵に描いた重力に影響されずに姿勢を維持することができず、徐々に垂れ下がってしまいました。ばねとダンパーだけでは、いくら値を大きくしても姿勢維持が難しいみたいです。 もし対策案が難しいようでしたらいったん、ここでベスト回答とさせていただきます。いかがでしょうか。
Bongo

2020/11/10 12:16

うまくいきませんでしたか...お力になれず申し訳ないです。 重力に負けて垂れ下がってしまうとなると、体節が重すぎるんでしょうかね?ですがそれならジョイントのトルクを十分大きくすれば対抗できそうな気もしますが、ダメだったということなら何かしらシミュレーションの限界にひっかかる要素があるのかもしれません。やはりコンピューター上でのシミュレーションでは一定の制限が発生してしまうのは致し方ないところがあり、物理学の教科書に載っているような理想的な理屈が通用しないケースもあってやっかいですね。 ちなみに、私が実験した際のRigidbodyの設定(massとかdragとか)は思い当たるかぎりは全部デフォルト状態だったはずでした。ご質問者さんの場合もし何かデフォルトから設定を大きく変更したような心当たりがありましたらコメントいただければ、私の分かる範囲であれば原因を調べてみたいと思います。
DY2peace

2020/11/10 13:30

説明不足で申し訳ございません。アナログスティック入力があるときには、うまく持ち上がりました。  アナログスティックの入力を入れてないときに、じわりじわりと垂れ下がってくる状態です。実はこの状態には過去に自分の検討でも別の方法で、できていました。確認した条件はすべて初期設定、RigidBodyは1kgで行っています。  話がもどってしまいますが、一番根本のカプセルに子・孫とカプセルを入れてあげて、子・孫カプセルのローカル座標のみ拾ってあげての位置・角度をFreezeという方法はやはり難しいのでしょうか。
Bongo

2020/11/10 20:03 編集

なるほど、入力量に応じてtargetAngularVelocityを変える方式だとすると無入力なら目標角速度が0となり、確かにダンパーによって回転を抑制する効果は発生するものの、重力を受けてゆっくりへたり込んでしまう...というのはもっともらしい現象のように感じますね。 では、回答中のヘビ怪物の部分で使ったtargetRotationによる方式ならどうでしょうか。こちらは目標角を与えるとそれに到達するようトルクをかける性質がありますので(よく知らないのですがサーボモーターみたいなものでしょうかね)、重力によって垂れ下がると目標角へ向かうようモーターが駆動し、重力とトルクが釣り合った地点で姿勢をキープできるかもしれません。 回答にtargetRotation方式のコードも追記してみました。 親子関係を作って子をフリーズ...というのもうまくやれば可能かもしれません。個人的にはどちらかというと https://sites.google.com/view/ronsu900/createfs/phys-intro の方のおっしゃるような「Rigidbody同士のヒエラルキーは独立を保ち、位置関係はジョイントで拘束する」というスタイルに賛同する考えではありますが、マニュアル(https://docs.unity3d.com/ja/current/Manual/class-Rigidbody.html )のRigidbodyを持つオブジェクトを別のオブジェクトの子にした場合の挙動に関する説明(「親子関係」の節)では、そういう風に親子関係を作ること自体について禁止してはいないようでした。targetRotation方式でも挙動がいまいちでしたら、次は親子関係方式について検討してみようかと思います。
DY2peace

2020/11/12 05:05

ご回答ありがとうございます。試してみましたが、残念ながら、ジョイントを増やしていくと垂れ下がりが生じてしまいました。  ローカル座標でのストップの件ですが、本日、追記内容を記載しました。ご確認いただけると幸いです。
Bongo

2020/11/12 22:31

追加情報ありがとうございます。ご質問者さんの考える動きが次第に見えてきたように感じますが、まだ意図を取り違えているかもしれませんので気になる点がありましたらお気軽にコメントください。 親のローカル空間における位置は固定して回転は許す...といった拘束を行うならConfigurableJoint、特に回転軸が1本ならば、もっとシンプルに操作できるHingeJointでいいんじゃないかと思いまして試してみました。 投稿字数が尽きてしまい、別回答に移りますがご容赦ください...
DY2peace

2020/11/13 01:34

ご検討いただきありがとうございます。これからソース確認させていただきます。 実はご検討いただいているのは、内視鏡の先端部になります。↓このようなイメージです。 https://www.thanko.jp/shopdetail/000000003306/ ぷるぷる震えてしまうのはリアルタイムで物理計算させているので、どうしても起きてしまうものかなと思いました。 Unityで用意されているロボットアーム『Articulation Body』も検討したことがあります。 https://blogs.unity3d.com/jp/2020/05/20/use-articulation-bodies-to-easily-prototype-industrial-designs-with-realistic-motion-and-behavior/ これもピタッと止まることはなく、少し行き過ぎてしまう傾向にあります。(だいぶ動きはいいのですが・・・) また持ち上がる動きも試してみたのですが、トルクをかけてないためか、動きは再現できませんでした。 もしご興味いただければArticulation Bodyご確認いただけると幸いです。サンプルプログラムもGitHubにあげられてます。
Bongo

2020/11/15 23:02 編集

ご紹介ありがとうございます。ArticulationBodyを試してみましたので、またもや別回答になりすみませんが追記しました。ですがご質問者さんの希望されるピタッと止まる動きは実現しきれず、大してお役に立てる情報ではないかもしれません。 ArticulationBodyは初挑戦なのですが、これもなかなか面白そうなものですね。回答の末尾にも少し申し上げましたが、ArticulationBodyとRigidbodyを連結するにはどうするのがいいんでしょうかね? 手軽に繋ぐことができれば、部分的にArticulationBodyを使う...つまり従来のRigidbodyのキャラクターにArticulationBodyの腕を生やしたりできそうで興味深いです。今後のアップデートに期待するところでしょうかね。 (追記: https://cginterest.com/2020/09/17/unity-2020-2-%E3%83%99%E3%83%BC%E3%82%BF%E7%89%88%E3%81%8C%E5%88%A9%E7%94%A8%E5%8F%AF%E8%83%BD%E3%81%AB%EF%BC%81/ によると、2020.2なら可能?)
guest

0

ベストアンサー

ArticulationBodyを試してみました。
コメント欄でご紹介いただいた内視鏡をイメージして、下記のように役割分担しています。

  • Root...アーティキュレーションのルートとして空のオブジェクトにArticulationBodyをアタッチしただけのもの。
  • Tail...青い直方体。タイプはPrismaticJointで、体軸方向への平行移動だけを許可する。内視鏡を配管内へ挿入する腕のつもり。
  • PassiveSegment...グレーのカプセル。タイプはSphericalJointで、±90°までスイングできる。スイングのstiffnessは0、dampingはある程度大きな値とし、これを鎖状に繋いである程度の固さを持つ紐として振る舞わせる。自由にツイスト可能とするが、ツイストのstiffnessは非常に大きな値、dampingはかなり大きな値とする(ツイストをLockedMotionにして完全に回転を禁止しようとすると、無理にねじれを解消しようとした結果なのか暴れ回ることがあったため、多少のねじれは許容するようにした)。内視鏡のコードのつもり。
  • ActiveSegment...黄色のカプセル。タイプはSphericalJointで、±後述の回転角上限÷セグメント数°までスイングできる。stiffnessは非常に大きな値として目標角度まで速やかに回転しつつ、dampingもかなり大きな値としてオーバーシュートがなるべく起こらないようにした。stiffnessが大きいので重力などの外力ではへたれにくく、曲がった形を維持できる。目標角度への追従性が高いため、目標角度は急激に変化させずゆったりと動かすことでそれらしい挙動になると思われる。とはいえ、PassiveSegmentのたわみなどのせいで内視鏡全体としてはいくらかのバネ運動をしてしまうようだった。また、根元のセグメントだけはツイスト可能とし、目標角度操作によって体軸周りに回転できるようにした。内視鏡先端の可動部分のつもり。
  • Head...赤いシリンダー。タイプはFixedJointで、ActiveSegmentの末端に固定された状態にある。後述のEndoscopeControllerがアタッチされている。動作上必須なパーツではないが、アーティキュレーションの動作制御役として具体的な形状を与えたくなったためこのようにした。おまけとしてカメラを搭載しており、内視鏡の映像を見ることができる。

各部位のArticulationBodyの設定は、シーン編集時点ではアンカーの位置・向きを調整する程度にとどめ(今回の場合はアンカーのX軸を体軸方向であるローカルZ軸方向に向けています)、各種物理特性はHeadにアタッチしたEndoscopeControllerで設定させました。

C#

1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4 5public class EndoscopeController : MonoBehaviour 6{ 7 [SerializeField] private float linearSpeed = 5.0f; 8 [SerializeField] private float angleLimit = 150.0f; 9 [SerializeField] private float angularSpeed = 60.0f; 10 [SerializeField] private float activeSegmentStiffness = 1e+07f; 11 [SerializeField] private float activeSegmentDamping = 1e+05f; 12 [SerializeField] private float passiveSegmentSwingDamping = 1e+04f; 13 [SerializeField] private float passiveSegmentTwistStiffness = 1e+07f; 14 [SerializeField] private float passiveSegmentTwistDamping = 1e+05f; 15 [SerializeField] private float tailDamping = 1e+04f; 16 [SerializeField] private bool lockPassiveSegmentSwingH; 17 [SerializeField] private bool lockPassiveSegmentSwingV; 18 [SerializeField] private bool lockPassiveSegmentTwist; 19 [SerializeField] private float headMass = 0.1f; 20 [SerializeField] private float activeSegmentMass = 0.1f; 21 [SerializeField] private float passiveSegmentMass = 1.0f; 22 [SerializeField] private float tailMass = 2.0f; 23 [SerializeField] private string twistInputName = "Horizontal"; 24 [SerializeField] private string linearInputName = "Vertical"; 25 [SerializeField] private string swingInputXName = "Horizontal2"; 26 [SerializeField] private string swingInputYName = "Vertical2"; 27 28 // 各部位 29 private ArticulationBody head; 30 private readonly List<ArticulationBody> activeSegments = new List<ArticulationBody>(); 31 private ArticulationBody lastActiveSegment; 32 private readonly List<ArticulationBody> passiveSegments = new List<ArticulationBody>(); 33 private ArticulationBody tail; 34 private ArticulationBody root; 35 36 // 目標角度 37 private Vector2 targetSwingAngle; 38 private float targetTwistAngle; 39 40 private void Awake() 41 { 42 // まず各部位を取得する 43 this.head = this.GetComponent<ArticulationBody>(); 44 var nextBody = this.head.transform.parent.GetComponent<ArticulationBody>(); 45 while (nextBody.name.Contains("Active")) 46 { 47 this.activeSegments.Add(nextBody); 48 nextBody = nextBody.transform.parent.GetComponent<ArticulationBody>(); 49 } 50 this.lastActiveSegment = this.activeSegments.Last(); 51 while (nextBody.name.Contains("Passive")) 52 { 53 this.passiveSegments.Add(nextBody); 54 nextBody = nextBody.transform.parent.GetComponent<ArticulationBody>(); 55 } 56 this.tail = nextBody; 57 this.root = this.tail.transform.parent.GetComponent<ArticulationBody>(); 58 Debug.Log($"Active segments: {this.activeSegments.Count}, Passive segments: {this.passiveSegments.Count}"); 59 60 // 頭の設定 61 this.head.mass = this.headMass; 62 this.head.jointType = ArticulationJointType.FixedJoint; 63 64 // アクティブセグメントの設定 65 var angleLimitOfSegment = this.angleLimit / this.activeSegments.Count; 66 foreach (var segment in this.activeSegments) 67 { 68 segment.mass = this.activeSegmentMass; 69 segment.jointType = ArticulationJointType.SphericalJoint; 70 segment.swingYLock = segment.swingZLock = ArticulationDofLock.LimitedMotion; 71 segment.twistLock = ArticulationDofLock.LockedMotion; 72 segment.yDrive = segment.zDrive = new ArticulationDrive 73 { 74 upperLimit = angleLimitOfSegment, 75 lowerLimit = -angleLimitOfSegment, 76 stiffness = this.activeSegmentStiffness, 77 damping = this.activeSegmentDamping, 78 forceLimit = float.MaxValue 79 }; 80 } 81 82 // 根元のアクティブセグメントだけ設定を一部変える 83 this.lastActiveSegment.twistLock = ArticulationDofLock.FreeMotion; 84 this.lastActiveSegment.xDrive = new ArticulationDrive 85 { 86 stiffness = this.activeSegmentStiffness, 87 damping = this.activeSegmentDamping, 88 forceLimit = float.MaxValue 89 }; 90 91 // パッシブセグメントの設定 92 foreach (var segment in this.passiveSegments) 93 { 94 segment.mass = this.passiveSegmentMass; 95 segment.jointType = ArticulationJointType.SphericalJoint; 96 segment.swingYLock = this.lockPassiveSegmentSwingH 97 ? ArticulationDofLock.LockedMotion 98 : ArticulationDofLock.LimitedMotion; 99 segment.swingZLock = this.lockPassiveSegmentSwingV 100 ? ArticulationDofLock.LockedMotion 101 : ArticulationDofLock.LimitedMotion; 102 segment.twistLock = this.lockPassiveSegmentTwist 103 ? ArticulationDofLock.LockedMotion 104 : ArticulationDofLock.FreeMotion; 105 segment.xDrive = new ArticulationDrive 106 { 107 stiffness = this.passiveSegmentTwistStiffness, 108 damping = this.passiveSegmentTwistDamping, 109 forceLimit = float.MaxValue 110 }; 111 segment.yDrive = segment.zDrive = new ArticulationDrive 112 { 113 upperLimit = 90.0f, 114 lowerLimit = -90.0f, 115 damping = this.passiveSegmentSwingDamping, 116 forceLimit = float.MaxValue 117 }; 118 } 119 120 // 尻尾の設定 121 this.tail.mass = this.tailMass; 122 this.tail.jointType = ArticulationJointType.PrismaticJoint; 123 this.tail.linearLockX = ArticulationDofLock.FreeMotion; 124 this.tail.linearLockY = this.tail.linearLockZ = ArticulationDofLock.LockedMotion; 125 this.tail.xDrive = new ArticulationDrive 126 { 127 damping = this.tailDamping, 128 forceLimit = float.MaxValue 129 }; 130 131 // ルートの設定 132 this.root.useGravity = false; 133 this.root.immovable = true; 134 } 135 136 private void FixedUpdate() 137 { 138 // アクティブセグメントの屈曲 139 var swingInput = new Vector2( 140 Input.GetAxis(this.swingInputXName), 141 Input.GetAxis(this.swingInputYName)); 142 this.targetSwingAngle += Time.deltaTime * this.angularSpeed * swingInput; 143 this.targetSwingAngle.x = Mathf.Clamp(this.targetSwingAngle.x, -this.angleLimit, this.angleLimit); 144 this.targetSwingAngle.y = Mathf.Clamp(this.targetSwingAngle.y, -this.angleLimit, this.angleLimit); 145 var targetAngleOfSegment = this.targetSwingAngle / this.activeSegments.Count; 146 foreach (var segment in this.activeSegments) 147 { 148 var segmentDrive = segment.yDrive; 149 segmentDrive.target = targetAngleOfSegment.x; 150 segment.yDrive = segmentDrive; 151 segmentDrive.target = targetAngleOfSegment.y; 152 segment.zDrive = segmentDrive; 153 } 154 155 // アクティブセグメントのひねり 156 var twistInput = Input.GetAxis(this.twistInputName); 157 this.targetTwistAngle += Time.deltaTime * this.angularSpeed * twistInput; 158 var lastActiveSegmentDrive = this.lastActiveSegment.xDrive; 159 lastActiveSegmentDrive.target = this.targetTwistAngle; 160 this.lastActiveSegment.xDrive = lastActiveSegmentDrive; 161 162 // 尻尾の押し引き 163 var linearInput = Input.GetAxis(this.linearInputName); 164 var tailDrive = this.tail.xDrive; 165 tailDrive.targetVelocity = linearInput * this.linearSpeed; 166 this.tail.xDrive = tailDrive; 167 } 168}

ヒエラルキーは壮絶な様相を呈しています。

図1

操作方法としては、左スティック上下で尻尾を押し引きする、左スティック左右で可動部をひねる、右スティックで可動部を上下左右に屈曲させる...といったもの想定してみました。
頭部を動かしてみましたが、今のところ操作をやめるとピタッと静止するという風にはできておらず、若干揺れています。もしかすると適当なところで妥協した方がいいかもしれません。静止させるために過剰な拘束を行うと、拘束に反する力がかかった時にそれを解消しようとして大暴れするようなこともありました...

図2

せっかくなので迷路の中を探検させてみましたが、コードが短くてあまり奥までは進めません。アーティキュレーションは最大64連結までの制限があるらしいので、あまり長くはできないかもしれませんね。

図3

コード部分を通常のRigidbodyの鎖にするような方針もあり得るかもしれませんが、RigidbodyArticulationBodyを手軽に接続できるジョイントみたいなものは見当たらず、ちょっと手間がかかりそうです。

投稿2020/11/15 22:22

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

DY2peace

2020/11/19 13:08

ご対応いただきありがとうございます。またご連絡遅れて申し訳ございません。 本物の内視鏡の動きにだいぶ近づいてきています。 また、いただいたやり方で試そうとしているのですが、ゲームコントローラの入力がうまく入らない状態です。 ・各関節に設けられているカプセルはどういった意味があるのでしょうか。 ・Headのみにスクリプトをアタッチするということでよろしかったでしょうか。 ・根本のTail(青カプセル)が重力で落ちないのですが、そういった設定でしょうか。 今回のご対応でうまく動きましたら、ベストアンサーとさせていただきたいと思います。
DY2peace

2020/11/19 13:28

関節が64個までだとやはり長さの問題がありますね・・・実際はかなり長いもの(10m~30mもある)ので、関節数はもう少し増やしておきたいところです。 また自分が以前検討していた別の方法ですが、Articulationを使わない方法で考えてました。 ・関節曲げトルクには最高角速度を設定。ピタ止めするため、必要以上にスピードが出ないようにする ・ゲームコントローラの入力あり⇒速度制限ありのトルクをかけて関節を曲げる。 ・ゲームコントローラの入力なし+関節に角速度が残っているとき⇒角速度ゼロまで、マイナス側にトルクをかける(ブレーキの役目) ⇒これだけだと計算が間に合わず、反対側に関節が行き過ぎてしまうためさらに↓ ・ローカル角度は、任意の角度位置をターゲットに止めるようにする。 ⇒例えば、30.3164…° の位置でトルクをかけるのを止めたとします(ゲームコントローラ入力ゼロ) ⇒マイナス側にトルクを働かせる(ブレーキの役目)、 ⇒ローカル角速度がゼロに近づいたら、たとえば近い角度のきりのいい位置、例えば、31°の位置を読み取って、強制的に位置固定させる。 みたいなことを考えてます。実際のロボットアームに使われるモーターの考え方に似ています。Unityの場合、物理計算が間に合ってくれるかどうかがあるため、角速度の速度規制とマイナストルク(ブレーキ)の調整がポイントになるかなと思ってます。  ConfigJointの接続だけだと、持ち上がりの動きができないため、各関節の重心を思いっきり後ろにもってます。ワイヤーで引っ張っているイメージに近いです。そうすれば持ち上がりの動きを作ることができます。 この内容はご興味いただいたらのご対応でかまいません。必要であれば別質問で立ち上げさせていただきます。
Bongo

2020/11/19 20:58

各関節のカプセルですが、あれらはメニューから作成できるただのCapsuleCollider付きカプセルです。役割としては画面上で目に見える形を与えること(MeshFilter、MeshRenderer)とArticulationBodyの物理的形状を与えること(CapsuleCollider)ですかね? カプセル自体にArticulationBodyをアタッチして階層構造を作ってもかまわなかったのですが、あんな風にカプセルだけArticulationBodyの子階層にぶら下げた理由としては、カプセルの向き(あるいはスケールも)をUnity上で調整したかったから...というのがありますね。 今回はカプセルをZ軸方向に並べて鎖状にしましたが(これも半ば恣意的に決めたもので、「Unityの慣習としては前後といったらZ軸だろう」みたいなイメージからです)、カプセルのモデルはY軸を向いた形に作られていますので回転させてやる必要がありました。そこでArticulationBodyの子として別オブジェクトにすることで、ArticulationBodyの向きはそのままにカプセルの向きだけ回せるようにしたわけです。 「Headのみにスクリプトをアタッチする」ということについてはおっしゃる通りです。他の部位にはスクリプトを付けておりません。 これもやはりこうである理由は特になく、自由な設計でかまわないと思います。ご質問者さんからご紹介いただいたArticulationBodyのサンプルプロジェクトに収録されていたロボットアームの場合は、各ArticulationBodyを直接に操作する役割はそれぞれの部位にアタッチされたスクリプトが担当しているようでした。 Tailが落下しないというのは、おっしゃる通りそういう仕様です... 今回の実験では、ルートオブジェクトを // ルートの設定 this.root.useGravity = false; this.root.immovable = true; という風に空間中に固定してしまいました。実は当初はこのルートオブジェクトはなく、Tailがルートで重力も作用させ、空中なら落下するような仕様で作り始めたのです。 内視鏡の挿入・引き出しはTailに対するAddForceで行おうと思ったのですが、なぜかこれだと謎の大暴れ現象の発生率が高く、試しに現在のPrismaticJointでスライドさせる方式にしたところ安定したため「アーティキュレーション全体の自由な運動は今回の主眼じゃないだろうからこれでいいか」と妥協したものです。 あの大暴れが仕方ないものなのか、やりようがまずかったのかは未調査です。内視鏡全体がワールド空間中で自由に運動できるようにしないとまずいようなら、この辺も調査しないとならなそうですね... かなり長い内視鏡を作るとなると、さらにハードルが上がりそうですね...ご紹介いただいたUnity Blogの記事でも、 従来のジョイントを使用することの問題点 第 1 の問題は、ソルバーに収束の問題を引き起こす可能性のある相反する要因の数が非常に多いことです。反復回数、接続された物体の相対的な質量、シーン内の制約のセットの全体の複雑度によって、解決不能なシナリオが作成されることがあります。このような場合、部分解が使用されるため、一部の制約が満たされないままになってしまいます。 第 2 の問題は、適用されるインパルスの大きさが、特定の時間に制約がどの程度破られているかを示す値である、ジョイントの誤差に依存していることです。この誤差補正の動作のため、特にジョイントが連結されている場合には、ボディが減衰スプリングのセットで連結されているような、ばねのような効果が常にいくらか発生します。 なんてことが挙げられていますね。内視鏡のコード部分は地形に沿って屈曲するので、やはり大量のオブジェクトが数珠繋ぎになった構造になってしまうでしょう。ですので記事で言うところの「シーン内の制約のセットの全体の複雑度」とか「ジョイントの誤差」だとかの要因がすごく効いてきそうな気がします。 見えないワイヤーで引っ張る設計もよさそうな気がしますね。私のような素人目から見るかぎりでは妥当な理屈のように感じます。 とはいえ今回のご質問に取り組んで、多数連結された物体のシミュレーションはかなりやっかいなのを体験した上では、果たしてどんな挙動をするか予想できないですね(もはやUnityの物理シミュレーションには頼らず、外部GPUをつないで計算するような高精度のシミュレーションシステムを作らないといけない領域なのかも)...
DY2peace

2020/11/20 00:04

ご回答いただきありがとうございます。もう少しこちらのほうでも試してみます。 また過去の自分の検討では、ジョイントは200連結(2m)相当まで、動作させることはできました。GPU積んだパソコンで、Physx3時代(Unity2017)、それほど高スペックPCでなくても動かすことはできました。 ・コードの部分は親子関係を作らず、ConfigJointで接続。(曲げ操作はないので) ・先端の曲げ・前後の挿入スピードをゆっくりにする ・挿入対称側(配管で実施)にRigidbodyを入れない(コライダーのみ) ・内視鏡直径は1mで。(実寸にしない)⇒得意なスケールがあるみたいです。 などの工夫を入れれば、動きました。 先述させていただいた、マイナストルク(ブレーキ)をかける方法ですが、こちらもすでに検討済でかなり近いところまで来ています。親子関係を作って、ローカル座標ストップを実施してみたいと思います。  また新たに質問させていただきながら検討の途中結果を報告させていただけたらと思います。
guest

0

ヒンジジョイントではどうなるか試してみました。
実験に使ったオブジェクトはメニューから作成できる単なるSphereで、それらを横に4個並べてそれぞれRigidbodyHingeJoint、後述のArmSegmentスクリプトをアタッチしています。RigidbodyHingeJointはアタッチしたての状態のままで、ジョイントの設定はスクリプト内で行いました。
また、ご質問者さんのスクリーンショットにならって親、子、孫、ひ孫の階層構造にしました。親には青いマテリアル、子には黄色いマテリアルを割り当てましたが、それらマテリアルの柄をビーチボールみたいな市松模様にして回転状態を見やすくしてみようと思いました。

ArmSegmentは下記のようになっています。インスペクター上で(または別のスクリプトから)targetAngleを操作すると、それに向かってヒンジの角度を調整しようとします。

C#

1using UnityEngine; 2 3#if UNITY_EDITOR 4using UnityEditor; 5#endif 6 7[RequireComponent(typeof(Rigidbody), typeof(HingeJoint))] 8public class ArmSegment : MonoBehaviour 9{ 10 // 目標角度 11 public float targetAngle; 12 13 // ヒンジの軸の向き 14 [SerializeField] private Vector3 axis = Vector3.forward; 15 16 // オフならヒンジのspring、damper、targetPositionでコントロールし 17 // オンならgain、derivativeTime、integrationTimeでコントロールする 18 [SerializeField] private bool usePidControl; 19 20 [Header("Spring-Damper Control")] 21 22 // ヒンジジョイントのバネ強度 23 [SerializeField] private float spring = 100000.0f; 24 25 // ヒンジジョイントのダンパー強度 26 [SerializeField] private float damper = 10000.0f; 27 28 [Header("PID Control")] 29 30 // 目標角からの偏差に比例したトルクをかける際の比例定数 31 [SerializeField] private float gain = 1000.0f; 32 33 // 微分項の効きの強さ 34 [SerializeField] private float derivativeTime = 0.2f; 35 36 // 積分項の効きの弱さ 37 [SerializeField] private float integrationTime = 100.0f; 38 39 // 前回のFixedUpdate時の目標角からの偏差...偏差変化率の算出に使う 40 // targetAngleが大きく変更された、あるいはアームに衝撃が加わって 41 // 角度が目標角から急激にずれたときにこの値が変動する 42 // derivativeTimeを上げるとずれを補正する追加のトルクがかかる 43 private float previousDeltaAngle; 44 45 // 目標角からの偏差を積算したもの 46 // もしこれがゼロからずれているときは、長時間にわたって目標角度を超えたまま、 47 // あるいは足りないままであると考えられる 48 // integrationTimeを下げるとずれを補正する追加のトルクがかかる 49 private float integratedDeltaAngle; 50 51 private ArmSegment parent; 52 private new Rigidbody rigidbody; 53 private HingeJoint joint; 54 55 private void Awake() 56 { 57 // ヒンジのanchorはVector3.zeroとし、ボールの中心を回転軸とする 58 var p = this.transform.parent; 59 this.parent = p == null ? null : p.GetComponent<ArmSegment>(); 60 this.rigidbody = this.GetComponent<Rigidbody>(); 61 this.joint = this.GetComponent<HingeJoint>(); 62 this.joint.autoConfigureConnectedAnchor = true; 63 this.joint.axis = this.axis; 64 this.joint.anchor = Vector3.zero; 65 } 66 67 private void Start() 68 { 69 // ひ孫のヒンジを孫のRigidbodyに、孫のヒンジを子のRigidbodyに、 70 // 子のヒンジを親のRigidbodyに繋ぎ、親のヒンジはワールド空間に繋ぐ 71 this.joint.connectedBody = this.parent == null ? null : this.parent.rigidbody; 72 } 73 74 private void FixedUpdate() 75 { 76 this.joint.useSpring = !this.usePidControl; 77 if (this.usePidControl) 78 { 79 // AddRelativeTorqueを使う場合 80 var deltaAngle = this.targetAngle - this.joint.angle; 81 this.integratedDeltaAngle += deltaAngle; 82 var deltaAngleVelocity = (deltaAngle - this.previousDeltaAngle) / Time.deltaTime; 83 var control = Mathf.Deg2Rad * this.gain * ( 84 deltaAngle + 85 (this.integratedDeltaAngle / this.integrationTime) + 86 (deltaAngleVelocity * this.derivativeTime)); 87 this.rigidbody.AddRelativeTorque(this.joint.axis * control); 88 this.previousDeltaAngle = deltaAngle; 89 } 90 else 91 { 92 // ヒンジのバネを使う場合 93 this.joint.spring = new JointSpring 94 { 95 spring = this.spring, 96 damper = this.damper, 97 targetPosition = this.targetAngle 98 }; 99 } 100 } 101 102 #if UNITY_EDITOR 103 private void OnDrawGizmos() 104 { 105 if (this.joint == null) 106 { 107 return; 108 } 109 110 Handles.Label(this.transform.position, $"T: {this.targetAngle:F1}\nA: {this.joint.angle:F1}"); 111 } 112 #endif 113}

各部位の角度としてはHingeJoint.angleを使用しました。ご質問者さんのおっしゃる通り、親子関係を持たない場合はTransformから相対角度を求めるのに座標系変換が必要で一手間かかるでしょう。今回はご質問者さんのやり方に沿って親子関係を構築しましたが、ジョイントによってはこういった手軽な方法で角度を得られますので、親子関係を作らない場合でもそれらに頼ればさほど面倒はないように思います。

ヒンジジョイントにも駆動機構がありますので、まずはそれを使って制御させてみました。usePidControlをオフにするとそのような動作になります。
ヒエラルキー上の4つのオブジェクトを全部選択して、インスペクターでtargetAngleを一斉に操作したところ下図のようになりました。

図1

一方、ご質問者さんが想定しているようなトルクを加える方法だと、トルクの大きさを自前で調整してやる必要があるでしょう。usePidControlをオンにすると下図のようになりました。

図2

PID制御 - Wikipedia」の記事をまねてみたつもりですが、ご覧の通り追従性が悪く、動きがプルプルしています...
以前申し上げたようにロボットとか制御工学は無知でして、計算が間違っているかパラメーター調整が甘いか、あるいは物理シミュレーション上の制限...タイムステップが長すぎるとかの原因があるのだろうと思います。まあポンコツロボットみたいでかわいいので、表現上面白い使い道があるかもしれませんが...

自前でトルクをかける方法は、Unity組み込みのヒンジ駆動機構より自由度が高く調整の余地があると言えるでしょう。おそらくご質問者さんの方がこういった制御についてお詳しいように思いますので、うまくやれば安定性と目標角度到達性を両立できるかもしれません。

投稿2020/11/12 22:31

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問