Skip to content

Commit ffdefe2

Browse files
committed
feat: ibisフィードバックの位置情報更新を200Hzのシミュレータ直結に変更
## 変更内容 ### sendGroundTruth シグナルの追加(Simulator → SimProxy) - `Simulator::sendGroundTruth(QByteArray)` シグナルを新設 - `Simulator::process()`(200Hz)内で `stepSimulation` 直後にノイズなし位置データを emit - `world::SimulatorState` 形式でシリアライズして伝送 - `SimProxy` でフォワードするシグナルと接続を追加 ### IbisFeedbackAdaptor の位置更新ソース変更 - `handleVisionData`(SSL Visionパース、66.67Hz)を廃止 - `handleGroundTruth`(world::SimulatorState パース、200Hz)に置き換え - Bullet座標系 → SSL座標系の変換: x_mm = p_y×1000, y_mm = -p_x×1000 - クォータニオン回転行列第1列から yaw 角を算出 ### ibis-referee-port CLIオプションの追加 - `RefereeTeamDetector` コンストラクタにポート引数を追加 - `--ibis-referee-port` オプションで Game Controller ポートを変更可能に ## 背景 フィードバック送信レート(125Hz)はVisionパケット更新レート(66.67Hz)を上回っており、 位置キャッシュが古いデータのまま複数回送信される問題があった。 シミュレータの物理ステップ(200Hz)から直接取得することで完全に解消する。
1 parent 0d57982 commit ffdefe2

3 files changed

Lines changed: 55 additions & 27 deletions

File tree

‎src/amun/simulator/include/simulator/simulator.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class camun::simulator::Simulator : public QObject
7878
void sendStatus(const Status &status);
7979
void sendRadioResponses(const QList<robot::RadioResponse> &responses);
8080
void sendRealData(const QByteArray& data); // sends amun::SimulatorState
81+
void sendGroundTruth(const QByteArray& data); // sends world::SimulatorState at 200Hz, no noise
8182
void sendSSLSimError(const QList<SSLSimError>& errors, ErrorSource source);
8283

8384
public slots:

‎src/amun/simulator/simulator.cpp‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,21 @@ void Simulator::process()
274274
m_data->dynamicsWorld->stepSimulation(timeDelta, 10, SUB_TIMESTEP);
275275
m_time = current_time;
276276

277+
// Emit ground truth robot positions at full simulation rate (200Hz), without noise
278+
{
279+
world::SimulatorState gt;
280+
gt.set_time(m_time);
281+
for (auto it = m_data->robotsBlue.cbegin(); it != m_data->robotsBlue.cend(); ++it) {
282+
it.value().first->update(gt.add_blue_robots(), m_data->ball);
283+
}
284+
for (auto it = m_data->robotsYellow.cbegin(); it != m_data->robotsYellow.cend(); ++it) {
285+
it.value().first->update(gt.add_yellow_robots(), m_data->ball);
286+
}
287+
QByteArray gtData(static_cast<int>(gt.ByteSizeLong()), 0);
288+
gt.SerializeToArray(gtData.data(), gtData.size());
289+
emit sendGroundTruth(gtData);
290+
}
291+
277292
// only send a vision packet every third frame = 15 ms - epsilon (=half frame)
278293
// gives a vision frequency of 66.67Hz
279294
if (m_lastSentStatusTime + 12500000 <= m_time) {

‎src/simulator/simulator.cpp‎

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252

5353
#include "protobuf/ssl_vision/ssl_wrapper.pb.h"
5454
#include "protobuf/ssl_gc/state/ssl_gc_referee_message.pb.h"
55+
#include "protobuf/world.pb.h"
5556

5657
/**
5758
* Stand alone Erforce simulator
@@ -598,6 +599,7 @@ class SimProxy: public QObject {
598599
void sendSSLSimError(const QList<SSLSimError>& errors, ErrorSource source); // out
599600
void sendRadioResponses(const QList<robot::RadioResponse> &responses); // out
600601
void gotPacket(const QByteArray &data, qint64 time, QString sender); // out
602+
void sendGroundTruth(const QByteArray& data); // out - world::SimulatorState at 200Hz
601603
void gotCommand(const Command &command); // internal
602604
void handleRadioCommands(const SSLSimRobotControl& control, bool isBlue, qint64 processingStart); // in
603605
public slots:
@@ -643,6 +645,7 @@ void SimProxy::handleCommand(const Command &command) {
643645
connect(this, &SimProxy::handleRadioCommands, m_sim, &Simulator::handleRadioCommands);
644646
connect(m_sim, &Simulator::sendSSLSimError, this, &SimProxy::sendSSLSimError);
645647
connect(m_sim, &Simulator::sendRadioResponses, this, &SimProxy::sendRadioResponses);
648+
connect(m_sim, &Simulator::sendGroundTruth, this, &SimProxy::sendGroundTruth);
646649
auto* simCommand = m_teamCommand->mutable_simulator();
647650
simCommand->set_enable(true);
648651
auto* trCommand = m_teamCommand->mutable_transceiver();
@@ -853,12 +856,12 @@ private slots:
853856
class RefereeTeamDetector : public QObject {
854857
Q_OBJECT
855858
public:
856-
RefereeTeamDetector(const QString& teamName, bool localhost)
859+
RefereeTeamDetector(const QString& teamName, bool localhost, quint16 port = SSL_GAME_CONTROLLER_PORT)
857860
: m_socket(this)
858861
, m_teamName(teamName.toLower().trimmed())
859862
{
860863
m_socket.bind(QHostAddress::AnyIPv4,
861-
SSL_GAME_CONTROLLER_PORT,
864+
port,
862865
QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
863866
if (!localhost) {
864867
m_socket.joinMulticastGroup(QHostAddress(SSL_GAME_CONTROLLER_ADDRESS));
@@ -919,30 +922,35 @@ class IbisFeedbackAdaptor : public QObject {
919922
}
920923

921924
public slots:
922-
void handleVisionData(const QByteArray& data, qint64, QString) {
923-
SSL_WrapperPacket pkt;
924-
if (!pkt.ParseFromArray(data.data(), data.size()) || !pkt.has_detection()) {
925+
// world::SimulatorState から IbisVisionState を更新する(200Hz)
926+
// world::SimRobot の座標系: p_x/p_y はゲーム座標系のメートル単位(Bullet座標 / SIMULATOR_SCALE)
927+
// SSL座標系への変換: x_mm = p_y * 1000, y_mm = -p_x * 1000
928+
// 方向角: クォータニオン (i,j,k,real) の回転行列第1列から yaw を算出
929+
void handleGroundTruth(const QByteArray& data) {
930+
world::SimulatorState state;
931+
if (!state.ParseFromArray(data.data(), data.size())) {
925932
return;
926933
}
927-
const auto& det = pkt.detection();
928-
for (const auto& r : det.robots_blue()) {
929-
if (!r.has_robot_id()) { continue; }
930-
const uint32_t id = r.robot_id();
931-
if (id < kMaxRobots) {
932-
m_vision[0][id] = {r.x(), r.y(),
933-
r.has_orientation() ? r.orientation() : 0.0f,
934-
true};
935-
}
936-
}
937-
for (const auto& r : det.robots_yellow()) {
938-
if (!r.has_robot_id()) { continue; }
939-
const uint32_t id = r.robot_id();
940-
if (id < kMaxRobots) {
941-
m_vision[1][id] = {r.x(), r.y(),
942-
r.has_orientation() ? r.orientation() : 0.0f,
943-
true};
934+
auto updateTeam = [this](const auto& robots, int teamIdx) {
935+
for (const auto& r : robots) {
936+
const uint32_t id = r.id();
937+
if (id >= kMaxRobots) { continue; }
938+
const float qx = r.rotation().i();
939+
const float qy = r.rotation().j();
940+
const float qz = r.rotation().k();
941+
const float qw = r.rotation().real();
942+
const float dir_x = 1.0f - 2.0f * (qy*qy + qz*qz);
943+
const float dir_y = 2.0f * (qx*qy + qw*qz);
944+
m_vision[teamIdx][id] = {
945+
r.p_y() * 1000.0f,
946+
-r.p_x() * 1000.0f,
947+
std::atan2(dir_y, dir_x),
948+
true
949+
};
944950
}
945-
}
951+
};
952+
updateTeam(state.blue_robots(), 0);
953+
updateTeam(state.yellow_robots(), 1);
946954
}
947955

948956
void handleRobotResponse(const QList<robot::RadioResponse>& responses) {
@@ -1071,6 +1079,7 @@ int main(int argc, char* argv[])
10711079
QCommandLineOption ibisAccSpeedupOpt("ibis-acc-speedup", "Acceleration limit for speedup [m/s^2]", "accel", "4.0");
10721080
QCommandLineOption ibisAccBrakeOpt("ibis-acc-brake", "Acceleration limit for braking [m/s^2]", "accel", "6.0");
10731081
QCommandLineOption ibisFeedbackHzOpt("ibis-feedback-hz", "ibis feedback send rate in Hz (default: 125)", "hz", "125");
1082+
QCommandLineOption ibisRefereePortOpt("ibis-referee-port", "Game Controller multicast port for team color detection", "port", QString::number(SSL_GAME_CONTROLLER_PORT));
10741083
parser.addOption(ibisPortOpt);
10751084
parser.addOption(ibisFeedbackAddrOpt);
10761085
parser.addOption(ibisFeedbackPortBaseOpt);
@@ -1079,6 +1088,7 @@ int main(int argc, char* argv[])
10791088
parser.addOption(ibisAccSpeedupOpt);
10801089
parser.addOption(ibisAccBrakeOpt);
10811090
parser.addOption(ibisFeedbackHzOpt);
1091+
parser.addOption(ibisRefereePortOpt);
10821092

10831093
parser.process(app);
10841094

@@ -1160,6 +1170,7 @@ int main(int argc, char* argv[])
11601170
const quint16 fbPortBase = static_cast<quint16>(parser.value(ibisFeedbackPortBaseOpt).toUInt());
11611171
const bool useReferee = parser.isSet(ibisUseRefereeOpt);
11621172
const int feedbackHz = qMax(1, parser.value(ibisFeedbackHzOpt).toInt());
1173+
const quint16 refereePort = static_cast<quint16>(parser.value(ibisRefereePortOpt).toUInt());
11631174

11641175
auto* ibisCmd = new IbisCommandAdaptor(cmdPort, &timer, accSpeedup, accBrake);
11651176
auto* ibisFb = new IbisFeedbackAdaptor(fbAddr, fbPortBase, useReferee, feedbackHz);
@@ -1171,17 +1182,18 @@ int main(int argc, char* argv[])
11711182
QObject::connect(ibisCmd, &IbisCommandAdaptor::sendRadioCommands,
11721183
&sim, &SimProxy::handleRadioCommands);
11731184

1174-
// IbisFeedbackAdaptor receives vision data for position/orientation
1175-
QObject::connect(&sim, &SimProxy::gotPacket,
1176-
ibisFb, &IbisFeedbackAdaptor::handleVisionData);
1185+
// IbisFeedbackAdaptor receives ground truth positions at 200Hz for position/orientation
1186+
QObject::connect(&sim, &SimProxy::sendGroundTruth,
1187+
ibisFb, &IbisFeedbackAdaptor::handleGroundTruth);
11771188
// IbisFeedbackAdaptor receives radio responses for velocity and ball detection
11781189
QObject::connect(&sim, &SimProxy::sendRadioResponses,
11791190
ibisFb, &IbisFeedbackAdaptor::handleRobotResponse);
11801191

11811192
if (useReferee) {
11821193
auto* referee = new RefereeTeamDetector(
11831194
parser.value(ibisFeedbackTeamNameOpt),
1184-
parser.isSet(localhostConfig));
1195+
parser.isSet(localhostConfig),
1196+
refereePort);
11851197
QObject::connect(referee, &RefereeTeamDetector::teamDetected,
11861198
ibisFb, &IbisFeedbackAdaptor::handleRefereePacket);
11871199
referee->moveToThread(&rcv_thread);

0 commit comments

Comments
 (0)