[choreonoid-users-ja 00222] Re: 即座にkinematicStateの変化を反映するには

中岡 慎一郎 s.nakaoka @ aist.go.jp
2017年 1月 12日 (木) 22:30:57 JST


産総研の中岡です。

小島さん解決方法の提示いただきありがとうございます。

この解決方法については、ほぼ同じことをするAPIとして、MessageViewにflush()という関数があります。これを呼ぶと内部でイベントループを回して、即座にGUIの状態も更新されることになります。元々はMessageViewにメッセージを出力する際に、すぐにそのメッセージを表示させたいことがあるので、それを行うためにMessageViewの関数となっているのですが、内部的には小島さん提案の方法と同様の内容となっており、Qtのイベントループで処理されるGUI関連の処理は全てflushされることになります。Qtの内部的なところを気にしなくてよいという点で、こちらを使ったほうがよいかもしれません。

ところで、菅さんの質問では、Choreonoid内部のスレッドとは別のスレッドで動いているとのことでした。この場合は対応方法が変わってくるかと思います。

まず、Choreonoid内部スレッド(メインスレッド)以外のスレッドからは、基本的にGUIに関わる変数や関数に直接アクセスしてはいけません。ここではWorldItemやBodyItemにアクセスして、最終的にBodyItemのnotifyKinematicStateChangeを呼んでいるのだと思いますが、これは別スレッドでは行ってはいけない処理であり、タイミングによってはクラッシュする可能性があります。

この場合、src/Base/LazyCaller.h で定義されている(インクルードするときは#include
<cnoid/LazeCaller>
)関数を使って、別スレッドからメインスレッドの関数を呼んで、そこで希望の処理を行う必要があります。

例えばこの例だと、

void updatePose()
{
   ... (ポーズ更新)

   bodyItem->notifyKinematicStateChange();
}

みたいな関数を定義しておきます。

そしてサービスを実装している、別スレッドで動作する関数については、そこから上記ヘッダに定義されている
callLater もしくは callSynchronously
関数を使って、updatePoseをメインスレッドから呼ぶよう要請します。

これらの関数にはstd::function<void()> 型の関数オブジェクトを渡します。

この例だと、

 callLater(updatePose);

みたいな感じです。
何かのメンバ関数のときは、シグナルの時と同様に、ラムダ関数なりbindなりを使ってください。

すると裏スレッドからはメインスレッドのイベントキューにこの関数を呼ぶイベントがポストされ、メインスレッド側でそのイベントが処理されることで、上記updatePose関数が「メインスレッドから」呼ばれます。

callLaterだと非同期コールとなって呼び出し元にすぐ戻りますし、callSynchronouslyを使うと同期をとって上記関数の実行終了を待ってからこの関数も終了します。

後は、処理の実行自体は上記のように行いますが、裏スレッドと上記関数との間でデータのやりとりが必要な場合は、適切に同期(排他制御等)の処理をおこなってください。ただしcallSynchronouslyを使う場合は、他のスレッドからのアクセスがないのであれば、排他制御もいらなくなるかと思います。

すみません、ちょっと複雑に見えるかもしれませんが、GUIのプログラムで別スレッドからGUIの処理を行う場合は、これが標準的なやり方になりますので、ご理解のほどよろしくお願いします。

On 01/08/17 23:08, KUNIO KOJIMA wrote:
> 東京大学の小島と申します
> 
> 私も同じようなことを悩んだことがありまして,ひょっとしたら菅さんが求めているものと異なるかもしれませんが,自分の見つけた解決法を共有させて頂きます
> 
> QEventLoop eventLoop;
> で宣言したeventLoopに対して
> bodyItem->notifyKinematicStateChange();
> した直後に
>  eventLoop.processEvents();
> を行うと,自分の環境ではシグナルが出されて貯まっているeventが全て消化されているように思います
> 直後に,collision等を取得すると情報が更新されているはずです.
> 
> 他により適した方法があるかもしれませんが,参考までに.
> 
> 
> 2017年1月8日 18:14 Yuki Suga <ysuga @ ysuga.net>:
> 
>> Choreonoid-MLの皆様:
>> お世話になります.SSR/早大の菅です.
>>
>> もっとコードを読めば良いのですが・・・恥ずかしながらも質問です.
>>
>> Choreonoidで読み込んだモデルの姿勢を変えて干渉チェックをして干渉に関する情報を返すRTCのプラグインを作っています.
>> サービスポートで受け取った関節角度を対象としているモデルに入れてから,
>> WorldItemのcollisionsを使って干渉データを取り出すことができると考えているのですが,
>> 関節角度を送ってからnotifyKinematicStateChangeを送っても即座には関節(ひいては干渉チェック)
>> に反映されないようです(それが仕様だと思います)
>>
>> マニュアル通りにシグナルで受け取ってもいいのですが,サービスとして実装しているので,
>> シグナルが送ってくるまで処理を待つという処理が必要になり無駄と感じています.
>> この処理はサービスコール内で処理をしているので,Choreonoid内部のスレッドとは別のスレッドで動いています(動いているはずです)
>>
>> 無理矢理,関節角度の更新と干渉データのアップデートを促すには,
>> プラグインの中でどのように処理をしたら良いのでしょうか?
>>
>> お教えください.よろしくお願いします.
>>
>>
>> _______________________________________________
>> choreonoid-users-ja mailing list
>> choreonoid-users-ja @ choreonoid.org
>> https://choreonoid.org/mailman/listinfo/choreonoid-users-ja
>>
>>
>>
>>
>> _______________________________________________
>> choreonoid-users-ja mailing list
>> choreonoid-users-ja @ choreonoid.org
>> https://choreonoid.org/mailman/listinfo/choreonoid-users-ja

-- 
中岡 慎一郎 <s.nakaoka @ aist.go.jp>
産業技術総合研究所 知能システム研究部門
ヒューマノイド研究グループ



choreonoid-users-ja メーリングリストの案内