26 #include <Eigen/Dense> 30 using namespace Eigen;
145 RobotConfig() : r(), b(), e() {}
146 RobotConfig(
double aR,
double aB,
double aE)
147 : r(aR), b(aB), e(aE) {}
156 InitialPose() : timestampMs(0), pose(Vector3d::Zero()) {}
157 InitialPose(Vector3d aPose) : timestampMs(0), pose(aPose) {}
158 InitialPose(uint64_t aTimestampMs, Vector3d aPose) : timestampMs(aTimestampMs), pose(aPose) {}
159 uint64_t timestampMs;
166 WheelSpeeds() : timestampMs(), phi(Vector2d::Zero()) {}
167 WheelSpeeds(uint64_t ts, Vector2d aPhi) : timestampMs(ts), phi(aPhi) {}
168 WheelSpeeds(uint64_t ts,
double r,
double l) : timestampMs(ts), phi(r, l) {}
169 uint64_t timestampMs;
178 KalmanState() : pose(Vector3d::Zero()), error(Matrix3d::Zero()) {}
179 KalmanState(Vector3d aPose, Matrix3d aError) : pose(aPose), error(aError) {}
187 LocalizationBelief() : timestampMs(), state() {}
188 LocalizationBelief(uint64_t ts,
const KalmanState& aState) : timestampMs(ts), state(aState) {}
189 LocalizationBelief(uint64_t ts,
const Vector3d& aPose,
const Matrix3d& aError) : timestampMs(ts), state(aPose, aError) {}
190 uint64_t timestampMs;
197 GPSPosition() : timestampMs(), state() {}
198 GPSPosition(uint64_t ts,
const KalmanState& aState) : timestampMs(ts), state(aState) {}
199 uint64_t timestampMs;
204 double WrapAngle(
double th)
207 return std::remainder(th, 2. * M_PI);
210 class ExtendedKalmanPosePredictor :
public Detector,
218 ExtendedKalmanPosePredictor(
Graph* graph) :
Detector(graph), mCurrentBelief(), mConfig()
220 Subscribe<RobotConfig>(
this);
221 Subscribe<InitialPose>(
this);
222 Subscribe< Lagged<LocalizationBelief> >(
this);
223 Subscribe<WheelSpeeds>(
this);
224 SetupPublishing<LocalizationBelief>(
this);
227 virtual void Evaluate(
const RobotConfig& aConfig)
232 virtual void Evaluate(
const InitialPose& aInitialPose)
234 mCurrentBelief.state = KalmanState(aInitialPose.pose, Matrix3d::Zero());
235 mCurrentBelief.timestampMs = aInitialPose.timestampMs;
240 mCurrentBelief.state = aLaggedBelief.
data.state;
241 mCurrentBelief.timestampMs = aLaggedBelief.
data.timestampMs;
244 virtual void Evaluate(
const WheelSpeeds& aWheelSpeeds)
247 Vector2d wheelLinearSpeed = aWheelSpeeds.phi * mConfig.r;
250 double d_th = (wheelLinearSpeed.x() - wheelLinearSpeed.y()) / mConfig.b;
252 double d_travel = (wheelLinearSpeed.x() + wheelLinearSpeed.y()) / 2.;
254 double theta = mCurrentBelief.state.pose.z();
257 double th_dth2 = theta + d_th / 2.;
260 Vector3d cartesianSpeed;
261 cartesianSpeed << d_travel * cos(th_dth2),
262 d_travel * sin(th_dth2),
267 double tDelta = (aWheelSpeeds.timestampMs - mCurrentBelief.timestampMs) * 1e-3;
270 mCurrentBelief.state.pose = mCurrentBelief.state.pose + (cartesianSpeed * tDelta);
271 mCurrentBelief.state.pose.z() = WrapAngle(mCurrentBelief.state.pose.z());
275 G << 1, 0, -d_travel * sin(th_dth2),
276 0, 1, d_travel * cos(th_dth2),
280 Matrix<double, 3, 2> V;
281 V << 0.5 * cos(th_dth2) - (d_travel / (2. * mConfig.b)) * sin(th_dth2), 0.5 * cos(th_dth2) + (d_travel / (2. * mConfig.b)) * sin(th_dth2),
282 0.5 * sin(th_dth2) + (d_travel / (2. * mConfig.b)) * cos(th_dth2), 0.5 * sin(th_dth2) - (d_travel / (2. * mConfig.b)) * cos(th_dth2),
283 1./mConfig.b, -1./mConfig.b;
287 M << mConfig.e * abs(wheelLinearSpeed.x()), 0,
288 0, mConfig.e * abs(wheelLinearSpeed.y());
291 mCurrentBelief.state.error = G * mCurrentBelief.state.error * G.transpose() + V * M * V.transpose();
293 mCurrentBelief.timestampMs = aWheelSpeeds.timestampMs;
294 Publish(mCurrentBelief);
297 LocalizationBelief mCurrentBelief;
302 class KalmanPoseCorrector :
public Detector,
308 KalmanPoseCorrector(
Graph* graph) :
Detector(graph), mCurrentBelief()
310 Subscribe< Lagged<LocalizationBelief> >(
this);
311 Subscribe<GPSPosition>(
this);
312 SetupPublishing<LocalizationBelief>(
this);
318 mCurrentBelief.state = aLaggedBelief.
data.state;
319 mCurrentBelief.timestampMs = aLaggedBelief.
data.timestampMs;
322 virtual void Evaluate(
const GPSPosition& aCorrectionInput)
327 Matrix3d K = mCurrentBelief.state.error * (mCurrentBelief.state.error + aCorrectionInput.state.error).inverse();
330 mCurrentBelief.state.pose = mCurrentBelief.state.pose + K * (aCorrectionInput.state.pose - mCurrentBelief.state.pose);
333 mCurrentBelief.state.error = (Matrix3d::Identity() - K) * mCurrentBelief.state.error;
335 Publish(mCurrentBelief);
338 LocalizationBelief mCurrentBelief;
346 : mPosePredictor(&mGraph)
347 , mKalmanPoseCorrector(&mGraph)
348 , mBeliefFeedback(&mGraph)
352 ExtendedKalmanPosePredictor mPosePredictor;
353 KalmanPoseCorrector mKalmanPoseCorrector;
357 virtual void ProcessOutput()
362 const LocalizationBelief& belief = correctedStateTopic->
GetNewValue();
363 const auto& pose = belief.state.pose;
364 cout <<
"Pose = [" << pose.x() <<
", " << pose.y() <<
", " << pose.z() <<
"], ";
365 const auto& error = belief.state.error.diagonal();
366 cout <<
"Error = [" << error.x() <<
", " << error.y() <<
", " << error.z() <<
"]";
376 uint64_t kSimStep = 1000;
377 uint64_t dataIndex = 0;
379 robot.ProcessData(RobotConfig(1, 1, 0.1));
380 robot.ProcessData(InitialPose(Vector3d(10.0, 10.0, 0.0)));
386 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
387 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
388 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
389 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
390 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
391 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, -1));
392 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, -1));
393 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
394 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
395 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
396 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
397 robot.ProcessData(WheelSpeeds((dataIndex++)*kSimStep, 1, 1));
398 robot.ProcessData(GPSPosition((dataIndex++)*kSimStep,
399 KalmanState(Vector3d(10., 10., 4),
400 Vector3d(0.1,0.1,300000).asDiagonal()
403 cout <<
"---- Done ----" << endl;
406 analyzer.GenerateDotFile(
"robot_localization.dot");
Implements a graph of Topics & Detectors with Input/Output APIs.
bool HasNewValue() const
Returns true if the new Data is available.
A Base class for a basic Graph container.
Manage data and its handler.
A templated TopicState used as the output of Lag.
const T & GetNewValue() const
Returns reference to the new/latest value in the topic.
Base struct for topic data types.
Produces a Lagged<T> for T.
Base class that implements a Publisher behavior.
A unit of logic in a DetectorGraph.
A Pure interface that declares the Subscriber behavior.
Class that provides debugging/diagnostics to a DetectorGraph detector graph.