HAPTIX Simulation Scoring Plugin Example - modulabs/gazebo-tutorial GitHub Wiki
์ด ํํ ๋ฆฌ์ผ์ Valero-Cuevas ๋ฑ์ด 2003๋ ์์ฑํ "The strength-dexterity test as a measure of dynamic pinch performance"๋ผ๋ ์ ๋ชฉ์ ๋ ผ๋ฌธ์์ ์๊ฐ์ ์ป์ ์๋ฎฌ๋ ์ด์ ๊ธฐ๋ฐ ์์ฌ์ฃผ ํ ์คํธ๋ฅผ ์ํด ๋ง๋ค์ด์ง ์๋ก์ด ์ฌ์ฉ์ handsim world๋ฅผ ์ค์ตํ๋ค.
์ด ํํ ๋ฆฌ์ผ์์๋ HAPTIX handsim ์ค์น ๋จ๊ณ๋ฅผ ์ด๋ฏธ ์๋ฃํ๋ค๊ณ ๊ฐ์ ํ๋ฉฐ, ์๋ฎฌ๋ ์ด์ world API ํํ ๋ฆฌ์ผ์ ์๋ฃํ๋ ๊ฒ์ด ์ข๋ค.
๊ฐ์ ๋ณด hansim ์ฑ์ ํ๋ฌ๊ทธ์ธ์ ์์ํ๊ณ , ํฐ๋ฏธ๋์์ ๊ฐ์ ๋ณด๋ฅผ ์์ํ๋ค:
gazebo --verbose worlds/luke_hand.world
๊ธฐ๋ณธ์ ์ผ๋ก Luke Hand ๋ชจ๋ธ์ ์ฌ์ฉํ์ฌ ๋ฐ์คํฌํฑ ํ๊ฒฝ์ ์ ๊ณตํ๋ค.
์ด world์๋ SimEventsPlugin์ ์ฌ์ฉํ์ฌ ์์ ์ธ๊ธ ํ ์์ ์๋ฃ ์ํ๋ฅผ ์ถ์ ํ๋ ์คํ๋ง ์์ถ ํ ์คํธ๊ฐ ์๋ค.
์๋๋ ํค๋ณด๋์ spacenav options๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฒฉ ์กฐ์ข ๋๋ ์ฌ์ฉ์ world์ ์์ด๋ค:
์ด ๋น๋์ค์๋, ์ ์๊ฐํ GUI์ ์ค๋ฅธ์ชฝ ํ๋จ์ 3๊ฐ์ ์์ ์๋ฃ ํ์ ์ ์ด ์๋ค. ์ผ์ชฝ ๊ฐ์ฅ ์ํ์ ์ ์ ๊ตฌ๋ถ๋ฌ์ง ์๋ ์ ํํ ์์ถ์ ๋ํ๋ด๋ฉฐ, ๋ น์์ ๊ตฌ๋ถ๋ฌ์ง ์๋ ์์ถํ๋ฉด ๋ น์์ ๋ํ๋ด๋ฉฐ, ์์ถ๋์ง ์์๊ฑฐ๋ ๊ตฌ๋ถ๋ฌ์ง๋ฉด ๋นจ๊ฐ์์ ๋ํ๋ธ๋ค. ์ค๊ฐ์ ์ํ ์ ์ ์์ถ ์ ์ง ์๊ฐ์ ๋ํ๋ด๋ฉฐ, ์คํ๋ง์ด ์ฌ๋ฐ๋ฅด๊ฒ ์์ถ๋์ด 3์ด ๋์ ์ ์ง๋๋ฉด ํฐ์์์ ๋ น์์ผ๋ก ํฌ๋ฏธํด์ง๋ค. ์ค๋ฅธ์ชฝ ๊ฐ์ฅ ์ํ์ธ ์ ์ ์์ ์ฑ๊ณต์ ๋ํ๋ด๋ฉฐ ํฐ์์์ ๋ น์์ผ๋ก ๋ฐ๋๋ค.
์ฒ์์๋ ์ธ ๊ฐ์ ๋๊ทธ๋ผ๋ฏธ๊ฐ ๋นจ๊ฐ์-ํฐ์-ํฐ์์ผ๋ก, ์คํ๋ง์ด ๋ฐฉํด๋ฐ์ง ์์์์ ๋ํ๋ธ๋ค. ์คํ๋ง์ด ์ถฉ๋ถํ ์์ถ๋๊ณ (1~10cm์ ์์ถ ๊ธธ์ด) ์คํ๋ง์ ์๋์ ์ผ๋ก ์ง์ ์ด๋ฉด (์คํ๋ง ์ค์์์ ๋นํ๋ฆผ ์คํ๋ง ์กฐ์ธํธ๊ฐ 0.1 radian ๋ฏธ๋ง์ ๋ํ๋), ์ฒซ ๋ฒ์งธ ์์ด ๋ น์์ผ๋ก ๋ฐ๋๋ค. ์ด ์ฑ๊ณต์ ์ธ ์์ถ ํด์ ๋ฅผ ์ํ์ฌ ํ์ด๋จธ๊ฐ ์์๋๊ณ , ๋ ๋ฒ์งธ ์์ด ํฐ์์์ ๋ น์์ผ๋ก ์ฌ๋ผ์ง๋๋ค. ํ์ด๋จธ๊ฐ 3์ด์ ๋๋ฌํ๋ฉด ๋ ๋ฒ์งธ ๋ฐ ์ธ ๋ฒ์งธ ์์ด ๋ น์์ผ๋ก ๋ฐ๋๋ฉฐ, ํ๋ก๊ทธ๋จ์์ด ์ํ์ ์ฑ๊ณต์ ์ธ ์ํ ์ํ์ผ๋ก ๊ฐ์ฃผํ๋ค.
luke_hand.world์์ ์๋ก์ด libSimEventsPlugin.so ํ๋ฌ๊ทธ์ธ ๋ธ๋ญ์ ์ถ๊ฐํ๋ค:
<plugin name="SimEvents" filename="libSimEventsPlugin.so">
<!-- spring 3 -->
<event>
<name>compressed_bottom</name>
<type>joint</type>
<model>spring_buckle_test_3</model>
<joint>joint_bottom_1</joint>
<range>
<type>position</type>
<min>-0.10</min>
<max>-0.01</max>
</range>
</event>
<event>
<name>buckled_x</name>
<type>joint</type>
<model>spring_buckle_test_3</model>
<joint>joint_1_2</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
<event>
<name>buckled_y</name>
<type>joint</type>
<model>spring_buckle_test_3</model>
<joint>joint_2_3</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
<!-- spring 2 -->
<event>
<name>compressed_bottom</name>
<type>joint</type>
<model>spring_buckle_test_2</model>
<joint>joint_bottom_1</joint>
<range>
<type>position</type>
<min>-0.10</min>
<max>-0.01</max>
</range>
</event>
<event>
<name>buckled_x</name>
<type>joint</type>
<model>spring_buckle_test_2</model>
<joint>joint_1_2</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
<event>
<name>buckled_y</name>
<type>joint</type>
<model>spring_buckle_test_2</model>
<joint>joint_2_3</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
<!-- spring 1 -->
<event>
<name>compressed_bottom</name>
<type>joint</type>
<model>spring_buckle_test_1</model>
<joint>joint_bottom_1</joint>
<range>
<type>position</type>
<min>-0.10</min>
<max>-0.01</max>
</range>
</event>
<event>
<name>buckled_x</name>
<type>joint</type>
<model>spring_buckle_test_1</model>
<joint>joint_1_2</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
<event>
<name>buckled_y</name>
<type>joint</type>
<model>spring_buckle_test_1</model>
<joint>joint_2_3</joint>
<range>
<type>normalized_angle</type>
<min>-0.1</min>
<max> 0.1</max>
</range>
</event>
</plugin>(์ฐธ๊ณ ๋ฅผ ์ํด SDF ํ์์ ๋ํ ์ค๋ช ์๊ฐ ์์ผ๋ฉฐ ์ฌ๊ธฐ์๋ SDF๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฎฌ๋ ์ด์ world์ ๋ชจ๋ธ์ ์์ฑํ๋ ๊ฒ์ ๋ํ ๊ธฐ๋ณธ ํํ ๋ฆฌ์ผ์ด ์๋ค.)
๊ทธ๋ฆฌ๊ณ ์ด์ด์ง๋ ์ฝ๋ ๋ธ๋ก์ SimEventsPlugin ๋ฐ์ดํฐ์ ๊ฒฐ๊ณผ๋ฅผ ํด์ํ๋ ๊ฒ์ด๋ฉฐ HaptixGUIPlugin.cc์์ ์ฐพ์ ์ ์์ต๋๋ค:
void HaptixGUIPlugin::ScoringUpdate()
{
while(!quit)
{
if (this->hxInitialized)
{
// hardcoded, tasks 0, 1, 2 are the spring tests
// hide if task id is greater than 2
if (this->currentTaskId > 2)
{
this->springScoreItem[0]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[1]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[2]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[0]->setPen(QPen(QColor(153, 255, 0, 0)));
this->springScoreItem[1]->setPen(QPen(QColor(153, 255, 0, 0)));
this->springScoreItem[2]->setPen(QPen(QColor(153, 255, 0, 0)));
}
else
{
if (this->springCompressed && !this->springBuckled)
{
gazebo::common::Time compressDuration =
gazebo::common::Time::GetWallTime() -
this->springCompressedStartTime;
if (compressDuration > this->springCompressedPassDuration)
{
// success! spring compressed correctly for 3 seconds.
gzdbg << "task completed!\n";
this->springScoreItem[0]->setBrush(QBrush(QColor(0, 255, 0, 255)));
this->springScoreItem[1]->setBrush(QBrush(QColor(0, 255, 0, 255)));
this->springScoreItem[2]->setBrush(QBrush(QColor(0, 255, 0, 255)));
}
else
{
double timeLeft = (this->springCompressedPassDuration -
compressDuration).Double();
// spring compressed correctly, just a few more seconds...
gzdbg << "compressed, great work! Please hold it for"
<< " [" << timeLeft
<< "] more seconds!\n";
this->springScoreItem[0]->setBrush(
QBrush(QColor(0, 255, 0, 255)));
this->springScoreItem[1]->setBrush(
QBrush(QColor(static_cast<int>(
255*(timeLeft/this->springCompressedPassDuration.Double())),
255, 0, 255)));
this->springScoreItem[2]->setBrush(
QBrush(QColor(static_cast<int>(
255*(timeLeft/this->springCompressedPassDuration.Double())),
255, 0, 0)));
}
}
else
{
if (!this->springCompressed)
{
gzdbg << "spring not compressed, try squeezing it [some more]!\n";
this->springScoreItem[0]->setBrush(QBrush(QColor(255, 0, 0, 255)));
this->springScoreItem[1]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[2]->setBrush(QBrush(QColor(255, 0, 0, 0)));
}
else if (this->springBuckled)
{
gzdbg << "spring buckled, try to keep it straight!\n";
this->springScoreItem[0]->setBrush(QBrush(QColor(0, 255, 0, 255)));
this->springScoreItem[1]->setBrush(QBrush(QColor(0, 0, 255, 255)));
this->springScoreItem[2]->setBrush(QBrush(QColor(0, 0, 255, 0)));
}
else
{
// user has not started compressing the spring
// or the spring has bucked beyond tolerance.
// gzerr << "Red!\n";
// gzerr << "compressed [" << this->springCompressed
// << "] buckled [" << this->springBuckled << "]\n";
this->springScoreItem[0]->setBrush(QBrush(QColor(255, 0, 0, 255)));
this->springScoreItem[1]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[2]->setBrush(QBrush(QColor(255, 0, 0, 0)));
this->springScoreItem[0]->setPen(QPen(QColor(153, 255, 0, 255)));
this->springScoreItem[1]->setPen(QPen(QColor(153, 255, 0, 255)));
this->springScoreItem[2]->setPen(QPen(QColor(153, 255, 0, 255)));
}
}
}
}
usleep(100000); // 10Hz max on scoring check
}
}
/////////////////////////////////////////////////
void HaptixGUIPlugin::PollTracking์ธ ๊ฐ์ ์์ ์์ ์ ์ด ์ผ์ ์๊ฐํ์ ์ฌ์ฉ๋๋ ์ ๊ทธ๋ฆผ์ ์ค๋ฅธ์ชฝ ์๋์ชฝ์ผ๋ก ๋ณ๊ฒฝํ์ฌ ์์
์๋ฃ ์ํ๋ฅผ ์ถ์ ํ๋๋ก GUI ๋น์ฃผ์ผ์ ์
๋ฐ์ดํธํ๋ค.HaptixGUIPlugin::ScoringUpdate ํจ์๋ ์์ฒด ์ค๋ ๋์์ ์์ฑ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์ ์ฐธ๊ณ ํ HaptixGUIPlugin::OnSimEvents ํจ์๋ SimEventsPlugin์ ์ํด ๊ฒ์๋ ์ํ ์
๋ฐ์ดํธ'๋ฅผ ๊ตฌ๋
ํฉ๋๋ค.
์ฐธ๊ณ ๋ก, Gazebo SimEvents API ๋ฌธ์๋ ์ฌ๊ธฐ์์ ์ฐพ์ ์ ์์ต๋๋ค. ์ด ํ๋ฌ๊ทธ์ธ์ ์๋ฎฌ๋ ์ด์
๋ ์คํ๋ง ์กฐ์ธํธ์ ๋ณํ๋ฅผ ๋ชจ๋ํฐ๋งํ๊ธฐ ์ํด ๊ฐ์ ๋ณด topic /gazebo/sim_events๋ฅผ ๊ตฌ๋
ํ๋ค.
