@ -269,6 +269,14 @@ public:
int gen_frame_ ;
bool motion_available_ = false ;
// Measured robot state for context grounding (breaks planner self-feedback loop).
// When enabled, planner context is filled from motor encoders + IMU instead of
// the planner's own previous output. Toggle at runtime with 'm' key.
bool use_measured_context_ = true ;
std : : array < double , 4 > measured_base_quat_ = { 1.0 , 0.0 , 0.0 , 0.0 } ;
std : : array < double , 29 > measured_joint_positions_ = { } ;
bool has_measured_state_ = false ;
protected :
// Common configuration
PlannerConfig config_ ;
@ -287,6 +295,19 @@ public:
return planner_state_ . initialized & & planner_state_ . enabled ;
}
/**
* @ brief Set measured robot state for context grounding .
* Called from the planner thread before UpdatePlanning ( ) .
*/
void SetMeasuredRobotState (
const std : : array < double , 4 > & base_quat ,
const std : : array < double , 29 > & joint_positions )
{
measured_base_quat_ = base_quat ;
measured_joint_positions_ = joint_positions ;
has_measured_state_ = true ;
}
int GetValidModeValueRange ( ) const {
if ( config_ . version = = 2 )
{
@ -555,7 +576,11 @@ public:
// Update context and get frame information
gen_frame_ = gen_frame + config_ . motion_look_ahead_steps ;
UpdateContextFromMotion ( motion_sequence ) ;
if ( use_measured_context_ & & has_measured_state_ ) {
UpdateContextFromMeasuredState ( ) ;
} else {
UpdateContextFromMotion ( motion_sequence ) ;
}
auto gather_input_end_time = std : : chrono : : steady_clock : : now ( ) ;
@ -677,6 +702,41 @@ public:
}
}
/**
* @ brief Fill planner context from measured robot state ( motor encoders + IMU ) .
*
* Replaces the self - feedback loop of UpdateContextFromMotion where the planner
* reads its own previous output . All 4 context frames are set to the same
* measured snapshot . Follows the same write pattern as InitializeContext :
* motor_state is MuJoCo order , context buffer is MuJoCo order , direct write .
*/
void UpdateContextFromMeasuredState ( )
{
auto context_qpos_values = GetContextBuffer ( ) ;
// Strip yaw from IMU quaternion — planner expects zero-yaw frame.
auto heading_inv = calc_heading_quat_inv_d ( measured_base_quat_ ) ;
auto quat_no_yaw = quat_mul_d ( heading_inv , measured_base_quat_ ) ;
for ( int n = 0 ; n < 4 ; + + n ) {
int base = n * ( G1_NUM_MOTOR + 7 ) ;
// Root position: (0, 0, standing_height) — no global position tracking
context_qpos_values [ base + 0 ] = 0.0f ;
context_qpos_values [ base + 1 ] = 0.0f ;
context_qpos_values [ base + 2 ] = static_cast < float > ( config_ . default_height ) ;
// Root quaternion: pitch+roll from IMU, yaw stripped
context_qpos_values [ base + 3 ] = static_cast < float > ( quat_no_yaw [ 0 ] ) ;
context_qpos_values [ base + 4 ] = static_cast < float > ( quat_no_yaw [ 1 ] ) ;
context_qpos_values [ base + 5 ] = static_cast < float > ( quat_no_yaw [ 2 ] ) ;
context_qpos_values [ base + 6 ] = static_cast < float > ( quat_no_yaw [ 3 ] ) ;
// Joint positions: motor_state is MuJoCo order, context is MuJoCo order
for ( int i = 0 ; i < G1_NUM_MOTOR ; i + + ) {
context_qpos_values [ base + 7 + i ] =
static_cast < float > ( measured_joint_positions_ [ i ] ) ;
}
}
}
/**
* @ brief Common method signatures that derived classes should implement
*/