Eshia
@Eshia

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Unity】ROSで受け取った点群データに衝突判定を与えたい

解決したいこと

Jetson NanoからPublishされた点群データをUnityでSubscribeし、その点群データに衝突判定を与えたいです。

現状

@te26 さんの
https://qiita.com/te26/items/016051e23ad7ecba774a
の記事を参考にUnityで仮想のロボットを作成し、仮想の壁との衝突判定によって、実機も停止するシステムを再現させていただきました。

現在、実機にはrealsenseカメラを搭載させており、点群(PointCloud)をPublishし、Unityで表示させています。この点群に衝突判定を与えたら何か面白いものができないかと思い、色々調べているところです。

開発環境

<ノートPC>
・Windows10 Core-i7
・Unity 2020.3.17f1

<Jetson Nano>
・Ubuntu18.04
・realsense d435i

補足

Unityでの点群の描画は
https://github.com/inmo-jang/unity_assets/tree/master/PointCloudStreaming
を参考にしています。

知見がある方いらっしゃいましたら、ご教授お願いいたします。

0

3Answer

過去に点群を扱ったことがあるUnityエンジニアです

静的なMesh化

ROSは完全に素人なので全くわからんのですが点群をコライダーとして扱う際はMesh化していました
ball pivot法とpoisson法という二種類の代表的なアルゴリズムがPCLとかPyMeshLabなどの各種
点群処理系のライブラリで実装されているのでこれらを使ってMesh化してやっていました
コード書くのがめんどくさいならMeshLabやcloud compareとかのGUIフロントエンドがある点群処理ウェアでも可能です。

ただし話を見ているとリアルタイム処理が必要な気がしているのでどうかな・・とも思いました
これらのアプローチだとBallPivot法で1億点群クラスの場合数日、poisson法でも数分〜掛かるので
事前に点群データを処理する前提になってしまいます。
またどちらのアルゴリズムも癖が強く、BallPivot法がMeshが欠落しやすく、poisson法は丸っとした形になりがちです。

てっとり早く点群からコライダーを起こしたいという目的であればこれらのライブラリで点群からMeshを起こしてやってみるのもいいのかなあ・・と思います.

余談

若干話がそれますが、Unity分野で語られる点群処理のノウハウの多くはビジュアライゼーション用途が多くガッチリ当たり判定を取ろうみたいな処理系の話はあまりないと思うので(あったらごめんなさい)別分野の論文を参照して自力実装することになるのかなと思います(そもそも点群処理自体さまざまな分野に論文がバラついてて調べづらい印象があります、建築、精密機械、GIS、XR、自動運転と様々に散らばってて辛い・・)

点群のリアルタイム処理はかなりヘビーな処理なので愚直に毎フレーム点群とMeshの衝突判定をやっていると大変なことになるので実績ある手法を検討する必要があります(処理時間はLiDARの性能、点群数が数万〜数億とバラつきが激しいため)や前処理(点群の間引きなど)などでだいぶ変わります)

なんかよさそげな論文

日本語だと引っ掛かりやすい文献なので既読かもしれませんが大規模点群における衝突判定法 などの論文があります

https://sinlab.future-tech-association.org/tech-article/kusaba/daihyo1011/
一応軽く触れられてるブログなどもあって要約されています。

要約するとXYZ座標の持つ点群データをスキャンセンサからの(角度、仰角、距離)の形に変換した
球面の座標空間に展開したデプスマップに変換してデータを取扱いできるようする。
次に衝突対象物の表側をrs、裏側をrsというデプス値として扱い、デプス値同士の比較を行なって当たり判定を取ろうというものです。(めっちゃ雑に要約した)

論文を書かれている増田先生の研究室は点群関連を得意とする研究室なので他にもいくつかあり
多分@Eshiaさんがやりたいと思っている処理は大規模点群の衝突判定を用いた経路探索シミュレーションあたりがちょっと近いのかな?とは思いました(根本的な接触判定はデプスマップ使ってるのであくまでも応用の一事例ですね)

ここまでくると点群をデプス(深度情報)として扱えばなんとかなるんじゃね?という仮説が私のなかで生まれてきました
ただ、手を動かさんとなんとも言えないですね()

他に思いつく方法

これも実際手を動かしてない思いつきです
せっかくUnityでやるならUnityの機能でゴリ押せないかという考え方で思いついたのですが
UnityのVFX graphにはパーティクルに当たり判定をつけることができます 参考

一方VFX graphにPoint Cacheという機能を使って点群を流し込むことが可能です 参考
Point cacheは一応オープンな規格でかなり点群ライクなデータフォーマットになっています。

これらを組み合わせれば点群を当たり判定つきパーティクルとして扱うことでどうにかならんかなと思った次第です、ただROSから来る点群をPoint Cacheに落とし込む必要があったりでそここそこ骨が折れそう・・・

あまり答えになってない気もしますが・・・お役に立てば幸いです

1Like

Unity上の何かしらのオブジェクトに与えてやれば,簡単にコライダーをアタッチでき,衝突判定を与えられると考えました.

最近点を求められるのであれば無理にコライダー使わなくてもUpdate()で座標比較とかするだけでもよさそうですね・・・・

点群データをUnityに描画していますが,プログラムの内容的にMesh化しています.

なんとなくですが私見を書いてみます、PointCloudRenderer.csのLine31のgameObject.AddComponent<MeshRenderer>();でAddされたMesh Rendererの話を前提に進めていきます

Unity的なバグの可能性

インスペクターみないとなんとも言えないんですがそもそもUnity的なコライダーの扱いがよくないだけの可能性もあるのでここらへんチェックしてみてもいいかもしれません、物理演算が切られていたみたいな些細なミスのことが自分でもよくあります

本質的な話

単純なミス以外の要素以外だと実際にコードをサラーっと読んで見ましたが PointCloudRenderer.csUpdateMesh()あたりで確かに毎フレームMeshを更新しているように見えます。

    void UpdateMesh()
    {
        
        
        //positions = subscriber.pcl;
        positions = subscriber.GetPCL();
        colours = subscriber.GetPCLColor();
        if (positions == null)
        {
            return;
        }
        mesh.Clear();
        mesh.vertices = positions; //ここで頂点(点群のXYZ座標)
        mesh.colors = colours; //(色付き点群の色を渡している
        int[] indices = new int[positions.Length];

        for (int i = 0; i < positions.Length; i++)
        {
            indices[i] = i;
        }

        mesh.SetIndices(indices, MeshTopology.Points, 0);// MeshTopologyを面ではなく線、点で定義
        mf.mesh = mesh;
        
    }

ただこんな簡単にXYZ情報しか持たない点の集まりを都合よく毎フレームMeshを生成できるならPCLやOpen3Dの開発者がなんであんなしんどいアルゴリズムを実装してるんだろ?という疑問が湧いてきたのでおそらくなんか省略しているだろうと思って調べたんですが、そもそもこのMesh生成実装はMeshを生成する要件をかなり端折っている(見た目さえ確保できればいいので)ように思えました。

UnityEngine.Meshの内部構造解説あたり改めて読み直してみるとコード中に法線に関する実装が欠落してるなと思えてきました、一応Unity EngineのMesh RendererクラスではMesh.verticesをいじると自動的にRecalculateBoundsが呼び出されて領域定義まではしてくれるらしいですが・・・

記事中でScriptから動的にMeshを作る際の手順としては

・mesh.verticesで頂点を設定
・mesh.trianglesで頂点インデックスを作成
・mesh.RecalculateBounds()で領域定義
・mesh.RecalculateNormals()で法線を求める

って感じなんですけどPointCloudStreamingではUnityのMeshに対して頂点とカラー座標しか渡してないように見えます、カスタムシェーダをなんか使ってたのでうまく色を渡してレンダリングすることはできてもMeshコライダーが機能しないのはここらへんが原因なのではないかな?と思いました。
ようはポリゴンになりきれてないわけであります。

私見ではありますがここらへんが原因じゃないかなーと思いました。。。がいかがでしょう
(ちゃんと3DCG基礎数学を勉強しなおしたい気持ちになりました)

追記

mesh.SetIndices()で面ではなく点でMeshのレンダリングモードを定義しているのでやはり
MeshColliderは機能しないように見えます
類似ケース

1Like

Comments

  1. @Eshia

    Questioner

    @stf-unitys 様、お返事ありがとうございます。

    「【Unity】『当たり判定』でチェックすべき10のポイント」を確認いたしましたが、改善されませんでした。
    @stf-unitys 様から提示いただいたMeshの条件的に、PointCloudStreamingでは不十分であることが理解できました。原因はここかもしれませんね。

    まずは、「最近点を求める方法」と「Meshを正しく作ること」を試してみたいと思います。

    貴重なお話ありがとうございました。再度、質問するかもしれませんので、お時間あるときに回答いただけると幸いです。

    以上、よろしくお願いいたします。

@stf-unitys 様,ご回答ありがとうございます.

Mesh化するためにPCLを使っています.@stf-unitys 様のおっしゃる通り,私の案件ではリアルタイム処理が必要となりますので,膨大な点群データを扱うことは障害となります.
この問題は,点群データの数を許せる程度減らして処理を軽減することで対処しました.

大規模点群の衝突判定を用いた経路探索シミュレーションも一度拝見しました.やりたいことに近いという印象でした.
改めて自分の頭の中で整理したところ,点群はリアルタイムで更新されていき,見えている範囲だけしか点群データが残っていない上,ロボットが障害物に当たらなければ良いと考えると,ロボット(カメラ)から最も近い点の位置座標が最も重要となり,それ以外の点群データに衝突判定を与えなくても良いと思いました.
したがって,ロボット(カメラ)から最も近い点の位置座標を取得して,この位置座標をUnity上の何かしらのオブジェクトに与えてやれば,簡単にコライダーをアタッチでき,衝突判定を与えられると考えました.
質問と考えが変わってしまっていて申し訳ございません.

せっかくUnityを使っているというのもありますので,Unityの機能でゴリ押すというのも大変面白いと感じます.VFX graphは存じておりませんでしたので,VFX graphも試してみたいと思います.

最近,点群を扱い始めたばかりでしたので,@stf-unitys 様の回答は,情報量が多く,大変勉強になりました.ありがとうございます.

<追加の質問>
Mesh化に関してですが,
https://github.com/inmo-jang/unity_assets/tree/master/PointCloudStreaming
の記事を参考にして,点群データをUnityに描画していますが,プログラムの内容的にMesh化しています.つまり,これにコライダーをアタッチすれば,良いと思い,BoxColliderやMeshColliderをアタッチしてみたのですが,仮想ロボットが貫通しました.コライダーのアタッチ方法に問題があるのでしょうか.

お時間あるときに回答いただけたら幸いです.

0Like

Your answer might help someone💌