Skip to content
Closed
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
7ff486b
project vimrc for FZF
itsarune Nov 23, 2023
e2258b1
filter out .swp files
itsarune Nov 25, 2023
3a6c5c5
Changes the implementation of MotorFaultIndicator
GrayHoang Jan 25, 2025
f76c4e3
adjusts dependencies and such to allow robot_auto_test.cpp to run as …
GrayHoang Jan 25, 2025
e4bae36
WIP transition from overall error tracking to per-motor tracking. Per…
GrayHoang Jan 26, 2025
3546bd7
adds capability to diusable robots
GrayHoang Jan 26, 2025
254b7e3
Finishes motor disabling loops
GrayHoang Feb 8, 2025
32c074c
Merge branch 'master' of github.com:UBC-Thunderbots/Software into Han…
GrayHoang Feb 8, 2025
2cb1c0c
removes commented code
GrayHoang Feb 8, 2025
60d7215
crosstool-ng config
itsarune Feb 9, 2025
cc78e24
Merge branch 'vimrc' of github.com:itsarune/Software into tbots_cross…
itsarune Feb 15, 2025
a184ae1
working on getting a nano compilation working
itsarune Feb 19, 2025
adf6f45
seems to compile
itsarune Feb 19, 2025
cc69121
Merge branch 'master' of github.com:UBC-Thunderbots/Software into Han…
GrayHoang Feb 21, 2025
74a219a
Working impl of x-compiler
itsarune Mar 1, 2025
329019d
updat headed_estop_reader test timeout
itsarune Mar 1, 2025
f84a945
Update clang-format to work on ubuntu 24
itsarune Mar 1, 2025
ea7edfb
Finished testing, patched some bugs and added disabling of motor when…
GrayHoang Mar 2, 2025
aab1eed
wip
itsarune Mar 2, 2025
60c1596
removes leftover code from testing. New patch that causes motors to r…
GrayHoang Mar 8, 2025
014458b
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 8, 2025
71ae4da
Update cross-compiler compilation paths
itsarune Mar 15, 2025
6dea991
delete my local computer config
itsarune Mar 15, 2025
9177e23
Merge branch 'master' of github.com:UBC-Thunderbots/Software into tbo…
itsarune Mar 19, 2025
6ebef9c
fix runtime issues with robot_info and robot_view widgets
itsarune Mar 19, 2025
02d6ecf
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 19, 2025
9795e96
moves the implementation to motor.cpp, removes some debugging vestigi…
GrayHoang Mar 22, 2025
2b2dece
Moves stopDisabledMotors() call to poll instead of updateMotorStatus
GrayHoang Mar 22, 2025
04bdeca
adds a helper for writing target velocities
GrayHoang Mar 22, 2025
8ad696e
better naming
GrayHoang Mar 22, 2025
9dd71eb
moves stopDisabledMotors to private methods and changes the method doc
GrayHoang Mar 22, 2025
fd3ef81
Merge branch 'master' of github.com:UBC-Thunderbots/Software into Han…
GrayHoang Mar 22, 2025
c022cfb
moves readwrite helper function to private
GrayHoang Mar 22, 2025
0eed37b
Merge branch 'HandleMotorFault' of github.com:potatoisagender/Softwar…
GrayHoang Mar 22, 2025
c1dc1fc
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 22, 2025
e8dc746
delete crosstool-ng config
itsarune Mar 27, 2025
3040133
Merge branch 'tbots_crosstool_compiler' of https://github.com/itsarun…
GrayHoang Mar 29, 2025
a6b3495
Add a getter for "cached_motor_faults_"
GrayHoang Mar 29, 2025
9924573
Bug fixing some stuff with robot diagnostics in arun's pr
GrayHoang Mar 30, 2025
0dbb9dd
Merge branch 'HandleMotorFault' of github.com:potatoisagender/Softwar…
GrayHoang Mar 30, 2025
52e77a0
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 30, 2025
b08b87f
Merge branch 'master' of github.com:UBC-Thunderbots/Software into Han…
GrayHoang May 8, 2025
d7d9d39
Merge branch 'HandleMotorFault' of github.com:potatoisagender/Softwar…
GrayHoang May 8, 2025
549e78a
remove boost 1.84 concept syntax flag
GrayHoang Jul 9, 2025
dd9d6e1
Changes documentation for removeMotorIfFaulty/removeFaultyMotor
GrayHoang Jul 9, 2025
a58ddf2
fixed encoder connections calibration function
GrayHoang Jul 9, 2025
765a66a
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Jul 9, 2025
c89c56e
Bugfix
GrayHoang Jul 15, 2025
904b66e
remove comment
GrayHoang Jul 15, 2025
137d4f6
Merge branch 'HandleMotorFault' of github.com:potatoisagender/Softwar…
GrayHoang Jul 15, 2025
8271f50
Remove unneccessary motor_fault_detector_ code?
GrayHoang Jul 15, 2025
89b8818
Debug
GrayHoang Jul 15, 2025
85f3454
Move MotorFaultIndicator constructor to cpp file
GrayHoang Jul 15, 2025
e209166
Merge branch 'HandleMotorFault' of github.com:potatoisagender/Softwar…
GrayHoang Jul 15, 2025
e30d993
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Jul 15, 2025
d56333d
Revert "Remove unneccessary motor_fault_detector_ code? Scan all moto…
GrayHoang Jul 19, 2025
7cd7fe2
Moves the Motor fault detection to poll()
GrayHoang Jul 19, 2025
b64b77b
change readThenWriteToEnabledMotor to return a velocity rather than u…
GrayHoang Jul 19, 2025
356dd45
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Jul 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 149 additions & 80 deletions src/software/embedded/services/motor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,65 @@ MotorService::MotorService(const RobotConstants_t& robot_constants,
setupGpio(MOTOR_DRIVER_RESET_GPIO, GpioDirection::OUTPUT, GpioState::HIGH)),
robot_constants_(robot_constants),
euclidean_to_four_wheel_(robot_constants),
motor_fault_detector_(0),
dribbler_ramp_rpm_(0),
tracked_motor_fault_start_time_(std::nullopt),
num_tracked_motor_resets_(0)
enabled_motors({0, 1, 2, 3, 4})
{
motorServiceInit(robot_constants, control_loop_frequency_hz);
}

MotorService::MotorFaultIndicator::MotorFaultIndicator(uint8_t id)
: drive_enabled(true),
last_motor_faults(),
motor_id(id),
num_critical_faults(0),
time_of_first_fault(std::nullopt),
total_duration_since_last_fault_s(0)
{
}

void MotorService::MotorFaultIndicator::update(
bool enabled, std::unordered_set<TbotsProto::MotorFault>& motor_faults)
{
const auto now = std::chrono::system_clock::now();
drive_enabled = enabled;
last_motor_faults = motor_faults;


if (time_of_first_fault.has_value())
{
total_duration_since_last_fault_s =
std::chrono::duration_cast<std::chrono::seconds>(now -
time_of_first_fault.value())
.count();
}
if (!enabled)
{
if (time_of_first_fault.has_value() &&
total_duration_since_last_fault_s < MOTOR_FAULT_TIME_THRESHOLD_S)
{
num_critical_faults++;
}
else
{
time_of_first_fault = std::make_optional(now);
num_critical_faults = 1;
}
}
}

void MotorService::MotorFaultIndicator::removeMotorIfFaulty(std::set<uint8_t>& motors)
{
if (num_critical_faults > MOTOR_FAULT_THRESHOLD_COUNT)
{
LOG(WARNING) << "In the last " << total_duration_since_last_fault_s
<< "s, the motor board has reset " << num_critical_faults
<< " times. The motor " << int(motor_id)
<< " is now disabled for safety";
motors.erase(motor_id);
}
}


void MotorService::motorServiceInit(const RobotConstants_t& robot_constants,
int control_loop_frequency_hz)
{
Expand Down Expand Up @@ -129,36 +180,6 @@ MotorService::~MotorService() {}

void MotorService::setup()
{
const auto now = std::chrono::system_clock::now();
long int total_duration_since_last_fault_s = 0;
if (tracked_motor_fault_start_time_.has_value())
{
total_duration_since_last_fault_s =
std::chrono::duration_cast<std::chrono::seconds>(
now - tracked_motor_fault_start_time_.value())
.count();
}

if (tracked_motor_fault_start_time_.has_value() &&
total_duration_since_last_fault_s < MOTOR_FAULT_TIME_THRESHOLD_S)
{
num_tracked_motor_resets_++;
}
else
{
tracked_motor_fault_start_time_ = std::make_optional(now);
num_tracked_motor_resets_ = 1;
}


if (tracked_motor_fault_start_time_.has_value() &&
num_tracked_motor_resets_ > MOTOR_FAULT_THRESHOLD_COUNT)
{
LOG(FATAL) << "In the last " << total_duration_since_last_fault_s
<< "s, the motor board has reset " << num_tracked_motor_resets_
<< " times. Thunderloop crashing for safety.";
}

prev_wheel_velocities_ = {0.0, 0.0, 0.0, 0.0};

// Clear faults by resetting all the chips on the motor board
Expand All @@ -168,12 +189,12 @@ void MotorService::setup()
reset_gpio_->setValue(GpioState::HIGH);
usleep(MICROSECONDS_PER_MILLISECOND * 100);

for (uint8_t motor = 0; motor < NUM_MOTORS; ++motor)
for (uint8_t motor : enabled_motors)
{
LOG(INFO) << "Clearing RESET for " << MOTOR_NAMES[motor];
tmc6100_writeInt(motor, TMC6100_GSTAT, 0x00000001);
cached_motor_faults_[motor] = MotorFaultIndicator();
encoder_calibrated_[motor] = false;
cached_motor_faults_.insert({motor, MotorFaultIndicator(motor)});
encoder_calibrated_[motor] = false;
}

// Drive Motor Setup
Expand All @@ -188,6 +209,7 @@ void MotorService::setup()
startController(DRIBBLER_MOTOR_CHIP_SELECT, true);
tmc4671_setTargetVelocity(DRIBBLER_MOTOR_CHIP_SELECT, 0);
checkEncoderConnections();
stopDisabledMotors();

// calibrate the encoders
for (uint8_t motor = 0; motor < NUM_DRIVE_MOTORS; motor++)
Expand All @@ -205,6 +227,22 @@ void MotorService::setup()
is_initialized_ = true;
}

void MotorService::stopDisabledMotors()
{
for (u_int8_t motor = 0; motor < NUM_DRIVE_MOTORS; motor++)
{
if (!motorInEnabledList(motor))
{
tmc4671_switchToMotionMode(motor, TMC4671_MOTION_MODE_STOPPED);
}
}
}

bool MotorService::motorInEnabledList(u_int8_t motor)
{
return enabled_motors.find(motor) != enabled_motors.end();
}

void MotorService::setUpDriveMotor(uint8_t motor)
{
startDriver(motor);
Expand All @@ -214,7 +252,7 @@ void MotorService::setUpDriveMotor(uint8_t motor)
tmc4671_setTargetVelocity(motor, 0);
}

MotorService::MotorFaultIndicator MotorService::checkDriverFault(uint8_t motor)
void MotorService::checkDriverFault(uint8_t motor)
{
bool drive_enabled = true;
std::unordered_set<TbotsProto::MotorFault> motor_faults;
Expand Down Expand Up @@ -329,7 +367,8 @@ MotorService::MotorFaultIndicator MotorService::checkDriverFault(uint8_t motor)
drive_enabled = false;
}

return MotorFaultIndicator(drive_enabled, motor_faults);
cached_motor_faults_.at(motor).update(drive_enabled, motor_faults);
cached_motor_faults_.at(motor).removeMotorIfFaulty(enabled_motors);
}

TbotsProto::MotorStatus MotorService::updateMotorStatus(double front_left_velocity_mps,
Expand All @@ -340,17 +379,15 @@ TbotsProto::MotorStatus MotorService::updateMotorStatus(double front_left_veloci
{
TbotsProto::MotorStatus motor_status;

cached_motor_faults_[motor_fault_detector_] = checkDriverFault(motor_fault_detector_);

for (uint8_t motor = 0; motor < NUM_MOTORS; ++motor)
{
if (motor != DRIBBLER_MOTOR_CHIP_SELECT)
{
TbotsProto::DriveUnit drive_status;
drive_status.set_enabled(cached_motor_faults_[motor].drive_enabled);
drive_status.set_enabled(cached_motor_faults_.at(motor).drive_enabled);

for (const TbotsProto::MotorFault& fault :
cached_motor_faults_[motor].motor_faults)
cached_motor_faults_.at(motor).last_motor_faults)
{
drive_status.add_motor_faults(fault);
}
Expand All @@ -376,9 +413,9 @@ TbotsProto::MotorStatus MotorService::updateMotorStatus(double front_left_veloci
{
TbotsProto::DribblerStatus dribbler_status;
dribbler_status.set_dribbler_rpm(static_cast<float>(dribbler_rpm));
dribbler_status.set_enabled(cached_motor_faults_[motor].drive_enabled);
dribbler_status.set_enabled(cached_motor_faults_.at(motor).drive_enabled);
for (const TbotsProto::MotorFault& fault :
cached_motor_faults_[motor].motor_faults)
cached_motor_faults_.at(motor).last_motor_faults)
{
dribbler_status.add_motor_faults(fault);
}
Expand All @@ -395,10 +432,6 @@ TbotsProto::MotorStatus MotorService::updateMotorStatus(double front_left_veloci
static_cast<float>(back_left_velocity_mps));
motor_status.mutable_back_right()->set_wheel_velocity(
static_cast<float>(back_right_velocity_mps));

motor_fault_detector_ =
static_cast<uint8_t>((motor_fault_detector_ + 1) % NUM_MOTORS);

return motor_status;
}

Expand All @@ -416,7 +449,7 @@ TbotsProto::MotorStatus MotorService::poll(const TbotsProto::MotorControl& motor
}

// checks if any motor has reset, sends a log message if so
for (uint8_t motor = 0; motor < NUM_MOTORS; ++motor)
for (uint8_t motor : enabled_motors)
{
if (requiresMotorReinit(motor))
{
Expand All @@ -440,37 +473,46 @@ TbotsProto::MotorStatus MotorService::poll(const TbotsProto::MotorControl& motor
// Get current wheel electical RPM (don't account for pole pairs). We will use these
// for robot status feedback We assume the motors have ramped to the expected RPM from
// the previous iteration.
double front_right_velocity =
static_cast<double>(tmc4671ReadThenWriteValue(
FRONT_RIGHT_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, front_right_target_rpm)) *
MECHANICAL_MPS_PER_ELECTRICAL_RPM;
double front_left_velocity =
static_cast<double>(tmc4671ReadThenWriteValue(
FRONT_LEFT_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, front_left_target_rpm)) *
MECHANICAL_MPS_PER_ELECTRICAL_RPM;
double back_right_velocity =
static_cast<double>(tmc4671ReadThenWriteValue(
BACK_RIGHT_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, back_right_target_rpm)) *
MECHANICAL_MPS_PER_ELECTRICAL_RPM;
double back_left_velocity =
static_cast<double>(tmc4671ReadThenWriteValue(
BACK_LEFT_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, back_left_target_rpm)) *
MECHANICAL_MPS_PER_ELECTRICAL_RPM;
double dribbler_rpm = static_cast<double>(
tmc4671ReadThenWriteValue(DRIBBLER_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, dribbler_ramp_rpm_));
double front_right_velocity;
double front_left_velocity;
double back_right_velocity;
double back_left_velocity;
double dribbler_rpm;

readThenWriteToEnabledMotor(FRONT_RIGHT_MOTOR_CHIP_SELECT, front_right_velocity,
front_right_target_rpm);
readThenWriteToEnabledMotor(FRONT_LEFT_MOTOR_CHIP_SELECT, front_left_velocity,
front_left_target_rpm);
readThenWriteToEnabledMotor(BACK_RIGHT_MOTOR_CHIP_SELECT, back_right_velocity,
back_right_target_rpm);
readThenWriteToEnabledMotor(BACK_LEFT_MOTOR_CHIP_SELECT, back_left_velocity,
back_left_target_rpm);

if (motorInEnabledList(DRIBBLER_MOTOR_CHIP_SELECT))
{
dribbler_rpm = static_cast<double>(tmc4671ReadThenWriteValue(
DRIBBLER_MOTOR_CHIP_SELECT, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, dribbler_ramp_rpm_));
}
else
{
dribbler_rpm = 0;
}
// Check motors for faults
for (uint8_t motor_to_scan : enabled_motors)
{
checkDriverFault(motor_to_scan);
}

// Construct a MotorStatus object with the current velocities and dribbler rpm
TbotsProto::MotorStatus motor_status =
updateMotorStatus(front_left_velocity, front_right_velocity, back_left_velocity,
back_right_velocity, dribbler_rpm);

stopDisabledMotors();
Comment thread
itsarune marked this conversation as resolved.
// This order needs to match euclidean_to_four_wheel converters order
// We also want to work in the meters per second space rather than electrical RPMs
// TODO #3424: three wheel drive
WheelSpace_t current_wheel_velocities = {front_right_velocity, front_left_velocity,
back_left_velocity, back_right_velocity};

Expand Down Expand Up @@ -594,13 +636,35 @@ TbotsProto::MotorStatus MotorService::poll(const TbotsProto::MotorControl& motor
return motor_status;
}

void MotorService::readThenWriteToEnabledMotor(uint8_t motor_chip, double& velocity,
int target)
{
if (motorInEnabledList(motor_chip))
{
velocity = static_cast<double>(
tmc4671ReadThenWriteValue(motor_chip, TMC4671_PID_VELOCITY_ACTUAL,
TMC4671_PID_VELOCITY_TARGET, target)) *
MECHANICAL_MPS_PER_ELECTRICAL_RPM;
}
else
{
// TODO #3424: When a motor is disabled use three-wheel velocity calculations.
velocity = 0;
}
}
std::unordered_map<int, MotorService::MotorFaultIndicator>
MotorService::getCachedMotorFaults()
{
return cached_motor_faults_;
}

bool MotorService::requiresMotorReinit(uint8_t motor)
{
auto reset_search =
cached_motor_faults_[motor].motor_faults.find(TbotsProto::MotorFault::RESET);
auto reset_search = cached_motor_faults_.at(motor).last_motor_faults.find(
TbotsProto::MotorFault::RESET);

return !cached_motor_faults_[motor].drive_enabled ||
(reset_search != cached_motor_faults_[motor].motor_faults.end());
return !cached_motor_faults_.at(motor).drive_enabled ||
(reset_search != cached_motor_faults_.at(motor).last_motor_faults.end());
}

void MotorService::spiTransfer(int fd, uint8_t const* tx, uint8_t const* rx, unsigned len,
Expand Down Expand Up @@ -1118,8 +1182,13 @@ void MotorService::checkEncoderConnections()
std::vector<bool> calibrated_motors(NUM_DRIVE_MOTORS, false);
std::vector<int> initial_velocities(NUM_DRIVE_MOTORS, 0);

for (uint8_t motor = 0; motor < NUM_DRIVE_MOTORS; ++motor)
for (uint8_t motor : enabled_motors)
{
if (motor == DRIBBLER_MOTOR_CHIP_SELECT)
Comment thread
itsarune marked this conversation as resolved.
{
// Dribblers don't use an encoder.
continue;
}
// read back current velocity
initial_velocities[motor] = tmc4671_readInt(motor, TMC4671_ABN_DECODER_COUNT);

Expand Down Expand Up @@ -1147,7 +1216,7 @@ void MotorService::checkEncoderConnections()
[](bool calibration_status) { return !calibration_status; });
++num_iterations)
{
for (uint8_t motor = 0; motor < NUM_DRIVE_MOTORS; ++motor)
for (uint8_t motor : enabled_motors)
{
if (calibrated_motors[motor])
{
Expand All @@ -1169,9 +1238,9 @@ void MotorService::checkEncoderConnections()
}

bool calibrated = true;
for (uint8_t motor = 0; motor < NUM_DRIVE_MOTORS; ++motor)
for (uint8_t motor : enabled_motors)
{
if (!calibrated_motors[motor])
if (!calibrated_motors[motor] && motor != DRIBBLER_MOTOR_CHIP_SELECT)
{
calibrated = false;
LOG(WARNING) << "Encoder calibration check failure. " << MOTOR_NAMES[motor]
Expand Down
Loading