Sysid Broke for C++ and Python

We spent quite a bit of time last night fighting the sysid routine not logging the state every time. We identified the root cause as a stateInitialized variable not being initialized. We are using Python but the problem is ultimately in the C++ lib.

I submitted a PR that fixes this.

3 Likes

PR has been merged! Thanks for the fix.

I wanted to provide a quick work around that we utilized to get this working without having to build WPILib ourselves.

The API lets your pass your own recordState function so we basically duplicated the C++ version in Python without the bug. Iā€™m including the entire SysID code for robotpy.

class DriveTrain(Subsystem):
    def __init__(self):
        ... SNIP ...
        self.__loggerState = None
        
        self.__sysid_config = sysid.SysIdRoutine.Config(recordState=self.logState)
        self.__sysid_mechanism = sysid.SysIdRoutine.Mechanism(self.driveVoltage, self.driveLog, self, "drive")
        
        self.__sysid = sysid.SysIdRoutine(self.__sysid_config, self.__sysid_mechanism)
 
    def logState(self, state):
        if not self.__loggerState:
            self.__loggerState = StringLogEntry(DataLogManager.getLog(),
                                                "sysid-test-state-drive")
        self.__loggerState.append(sysid.SysIdRoutine.stateEnumToString(state))    
                  
    def driveVoltage(self, v):
        self._fl.driveMotor.set_control(VoltageOut(v, enable_foc=False)) 
        self._fr.driveMotor.set_control(VoltageOut(v, enable_foc=False))
        self._bl.driveMotor.set_control(VoltageOut(v, enable_foc=False))
        self._br.driveMotor.set_control(VoltageOut(v, enable_foc=False))
    
    def sysid_quasistatic_cmd(self, direction):
        return self.__sysid.quasistatic(direction)
        
    def sysid_dynamic_cmd(self, direction):
        return self.__sysid.dynamic(direction)
    
    def driveLog(self, log: SysIdRoutineLog):
        log.motor("fl") \
            .voltage(self._fl.driveMotor.get_motor_voltage().value) \
            .velocity(self._fl.get_state().speed) \
            .position(self._fl.get_position().distance)
        log.motor("fr") \
            .voltage(self._fr.driveMotor.get_motor_voltage().value) \
            .velocity(self._fr.get_state().speed) \
            .position(self._fr.get_position().distance)
        log.motor("bl") \
            .voltage(self._bl.driveMotor.get_motor_voltage().value) \
            .velocity(self._bl.get_state().speed) \
            .position(self._bl.get_position().distance)
        log.motor("br") \
            .voltage(self._br.driveMotor.get_motor_voltage().value) \
            .velocity(self._br.get_state().speed) \
            .position(self._br.get_position().distance)
1 Like

We worked around this by logging to the Phoenix 6 signal logger as a backup. This also allowed us to use the hoot logs with CAN signals transmitted faster than 50 Hz.