VEX V5 PYTHON | 09.6 TESTING CHANGING CONDITIONS
Guide objective
Record electrical and thermal conditions, diagnose changing grip, and test whether an autonomous routine remains repeatable across realistic trials.
01 | SAME CODE, DIFFERENT RUN
The first autonomous run finishes neatly. After several trials, a turn becomes slower or a stop slides farther. The code has not changed, but the operating conditions may have.
Repeatability means meeting the required outcome across a defined range of conditions. Battery state, motor temperature, load and traction all belong in the test record.
Do not use one successful cold-robot run as proof that a route is ready for repeated competition use.
02 | SEPARATE THE THREE EFFECTS
| Condition | Possible change | Useful evidence |
|---|---|---|
| Battery and electrical load | Available supply under load and ability to meet requests | Capacity estimate, voltage and current over the action |
| Motor heating | Reduced performance as thermal protection acts | Individual temperatures, current, duration and repeated-run trend |
| Traction | Wheel rotation differs from ground travel; braking changes | Tracking wheels or measured displacement, surface, load and stop range |
Battery level tends to fall while motors warm during repeated runs. A simple run-number correlation cannot identify which factor caused the change. Compare carefully matched conditions and record them together.
03 | CAPACITY IS NOT THE SAME AS LOADED VOLTAGE
Battery capacity reports an estimated percentage remaining. Voltage describes the electrical supply at the time of measurement. Current describes electrical demand.
capacity_pct = brain.battery.capacity() voltage_V = brain.battery.voltage(VoltageUnits.VOLT) battery_A = brain.battery.current()
A resting voltage reading does not describe every brief load event. Compare voltage before an action with readings during the same action phase, along with current and battery identity.
Sampled minima can miss short dips. Label them as observed sampled values, not guaranteed worst cases. Investigate connections, battery health and mechanical demand as well as charge state.
04 | DO NOT BLINDLY SCALE OUTPUT BY BATTERY VOLTAGE
A percentage velocity request is not a direct measurement of ground speed. Smart-motor regulation, load and traction influence the result. A motor may be unable to meet its requested speed.
A direct voltage request has a different meaning, but it is still limited by the available supply and platform behaviour. Multiplying every request by nominal_voltage / measured_voltage can increase saturation or slip without restoring the intended motion.
Use feedback, bounded requests, acceleration limits and tested battery operating bands. Treat any feedforward compensation as a calibrated model, not a universal correction.
05 | MEASURE INDIVIDUAL MOTOR TEMPERATURES
Read temperatures in an explicit unit and inspect each motor. A shared average can hide one motor running hotter than its partners.
temperature_C = left_1.temperature(TemperatureUnits.CELSIUS)
VEX documents reduced maximum current when V5 motors become too hot, with full-performance guidance below 55°C and a motor stopping at 70°C until it cools. Do not use those values as targets for routine operation.
The logger uses illustrative team review levels of 45°C and 50°C. These are example operational thresholds, not manufacturer safety limits or proof that a motor is safe to touch.
Investigate friction, gearing, alignment, prolonged pushing and holding a load. Increasing controller gains does not remove a thermal limit.
06 | WATCH LOAD AND COOLING TRENDS
Current and temperature answer different questions. Current can change rapidly with load; temperature reflects accumulated heating and cooling.
| Observation | Possible cause | Useful next check |
|---|---|---|
| One drive motor hotter | Unequal load, friction or assembly problem | Inspect that side; compare all partner motors |
| All drive motors warming | Repeated demanding routes or sustained effort | Measure run duty cycle and cooling interval |
| Intake heating while objects stop moving | Possible jam or unnecessary sustained request | Review object path and jam policy from 05.6 |
| Lift heats while stationary | Active holding or load imbalance | Review gearing, support and mechanism-specific stop policy |
Let the robot cool and remeasure before repeating a demanding test. Do not keep forcing a mechanism because the software has not reported a fault.
07 | TRACTION CHANGES THE MEANING OF WHEEL TRAVEL
Drive encoders report wheel rotation. If the wheel slips, that rotation can overestimate chassis travel. Passive tracking wheels can also slip or lose contact.
Grip depends on wheel material, surface condition, dust, load distribution and the acceleration or braking demand. An extended mechanism can change weight distribution even when the total robot mass is unchanged.
Compare a low-speed straight drive with an independent measured distance. Avoid using a turn as the first slip test because wheel paths and scrub complicate the comparison.
08 | USE A DISCREPANCY AS A DIAGNOSTIC
| Low-speed straight trial | Drive-derived travel | Independent travel | Difference |
|---|---|---|---|
| Illustrative A | 1000 mm | 990 mm | 10 mm |
| Illustrative B | 1000 mm | 850 mm | 150 mm |
# Only for sufficiently long, aligned straight travel with calibrated scales. difference_mm = drive_travel_mm - independent_travel_mm relative_discrepancy = abs(difference_mm) / abs(drive_travel_mm)
Trial B gives 15% relative discrepancy. It is not automatically a precise slip ratio: wheel scale error, external gearing, independent-sensor error and path geometry can contribute.
Do not divide by a near-zero travel value. Establish a minimum useful test distance and confirm both measurements refer to the same interval and direction.
09 | ADAPT MOTION WITHOUT HIDING THE PROBLEM
Reduce acceleration and braking demand before increasing output. Revisit slew limits from 03.1, deceleration from 03.2 and speed profiles from 03.5.
A lower cap can reduce demand, but it can also make a route miss its deadline. Revalidate the whole route when changing caps, gains, ramps, stopping modes or loads.
Use a defined pause, inspection or fallback policy when conditions leave the tested operating band. Do not automatically resume a cancelled or thermally paused route. Retain the ownership and deadline rules from 09.4.
10 | COMPLETE PASSIVE CONDITION LOGGER
Use a separate teaching project with no other movement code. Paste below # Begin project code, retaining generated VEX imports and Brain setup. Do not duplicate manual devices in Devices.
The logger reads four drive motors on ports 3/4 and 1/2, plus two intake motors on 7/6. Adjust names, ports and cartridges for your robot. Include lift and scoring motors if they are relevant to the trial.
CONFIGURATION_VERIFIED starts False. The standalone logger runs for thirty seconds while disabled and issues no motor commands. Use it for before/after checks; it cannot measure an active loaded route on its own.
# =============================================================================
# TEAM [MY TEAM NAME / NUMBER] | PASSIVE CONDITION LOGGER
# =============================================================================
# Keep generated VEX imports and Brain setup above '# Begin project code'.
# Separate teaching project: no motor commands and no other movement code.
import math
left_1 = Motor(Ports.PORT3, GearSetting.RATIO_6_1, False)
left_2 = Motor(Ports.PORT4, GearSetting.RATIO_6_1, False)
right_1 = Motor(Ports.PORT1, GearSetting.RATIO_6_1, True)
right_2 = Motor(Ports.PORT2, GearSetting.RATIO_6_1, True)
intake_1 = Motor(Ports.PORT7, GearSetting.RATIO_18_1, False)
intake_2 = Motor(Ports.PORT6, GearSetting.RATIO_18_1, False)
MOTORS = [('left_3',left_1),('left_4',left_2),('right_1',right_1),
('right_2',right_2),('intake_7',intake_1),('intake_6',intake_2)]
controller = Controller(PRIMARY)
CONFIGURATION_VERIFIED = False
TRIAL_ID = 'trial_01' # Simple CSV-safe ID; record battery/surface/load in your notes.
LOG_PERIOD_MS = 1000
READ_BUDGET_MS = 200
MONITOR_MS = 30000
# Example TEAM REVIEW thresholds, not manufacturer safety limits.
MIN_CAPACITY_PCT = 30.0
LOW_VOLTAGE_V = 11.5
WARM_C = 45.0
WARM_CLEAR_C = 40.0
PAUSE_C = 50.0
PAUSE_CLEAR_C = 45.0
class TeamReview:
def __init__(self):
self.thermal = 'COOL'
self.pause_latched = False
def assess(self, frame):
if not frame['valid']:
self.thermal = 'UNKNOWN'
return 'UNKNOWN_INSPECT_DATA'
hottest = max(frame['temperatures'])
if hottest >= PAUSE_C:
self.thermal = 'PAUSE'
self.pause_latched = True
elif self.thermal == 'PAUSE' and hottest > PAUSE_CLEAR_C:
self.thermal = 'PAUSE'
elif hottest >= WARM_C:
self.thermal = 'WARM'
elif self.thermal == 'WARM' and hottest > WARM_CLEAR_C:
self.thermal = 'WARM'
else:
self.thermal = 'COOL'
# Once PAUSE was observed, this logger never automatically rearms it.
if self.pause_latched:
return 'PAUSE_LATCHED_MANUAL_REVIEW'
if frame['capacity'] < MIN_CAPACITY_PCT or frame['voltage'] < LOW_VOLTAGE_V:
return 'REVIEW_BATTERY'
if self.thermal == 'WARM':
return 'WARM_REVIEW_LOAD'
return 'WITHIN_EXAMPLE_TEAM_THRESHOLDS'
def valid_settings():
numbers = (LOG_PERIOD_MS,READ_BUDGET_MS,MONITOR_MS,MIN_CAPACITY_PCT,
LOW_VOLTAGE_V,WARM_C,WARM_CLEAR_C,PAUSE_C,PAUSE_CLEAR_C)
return (bool(MOTORS) and all(math.isfinite(x) for x in numbers)
and 0 < READ_BUDGET_MS < LOG_PERIOD_MS and MONITOR_MS > 0
and 0 <= MIN_CAPACITY_PCT <= 100 and 0 < LOW_VOLTAGE_V < 20
and WARM_CLEAR_C < WARM_C < PAUSE_C
and WARM_CLEAR_C <= PAUSE_CLEAR_C < PAUSE_C
and isinstance(TRIAL_ID,str) and TRIAL_ID
and all(c.isalnum() or c in '_-' for c in TRIAL_ID))
def read_conditions():
frame = {'valid':False,'capacity':None,'voltage':None,'battery_current':None,
'temperatures':[],'currents':[],'reason':''}
before = brain.timer.time(MSEC)
try:
frame['capacity'] = brain.battery.capacity()
frame['voltage'] = brain.battery.voltage(VoltageUnits.VOLT)
frame['battery_current'] = brain.battery.current() # Amps.
for name,motor in MOTORS:
frame['temperatures'].append(motor.temperature(TemperatureUnits.CELSIUS))
frame['currents'].append(motor.current(CurrentUnits.AMP))
values = [frame['capacity'],frame['voltage'],frame['battery_current']]
values += frame['temperatures']+frame['currents']
if (not all(math.isfinite(v) for v in values)
or not 0 <= frame['capacity'] <= 100
or not 0 < frame['voltage'] < 20
or frame['battery_current'] < 0
or any(t < -20 or t > 100 for t in frame['temperatures'])
or any(a < 0 for a in frame['currents'])):
frame['reason'] = 'IMPLAUSIBLE_DATA'
elif brain.timer.time(MSEC)-before > READ_BUDGET_MS:
frame['reason'] = 'READ_OVERRUN'
else:
frame['valid'] = True
except Exception:
frame['reason'] = 'READ_EXCEPTION'
frame['ms'] = brain.timer.time(MSEC)
return frame
def emit_row(frame,status,elapsed):
fields = [TRIAL_ID,str(elapsed),status,frame['reason']]
for value in [frame['capacity'],frame['voltage'],frame['battery_current']]:
if not frame['valid']:
value = None
fields.append('NA' if value is None else str(round(value,2)))
for i in range(len(MOTORS)):
if frame['valid']:
fields += [str(round(frame['temperatures'][i],2)),
str(round(frame['currents'][i],2))]
else:
fields += ['NA','NA']
print(','.join(fields))
def log_conditions():
if not valid_settings():
return 'INVALID_SETTINGS'
if competition.is_enabled():
return 'REQUIRES_DISABLED_MODE'
header = ['trial_id','elapsed_ms','team_review','data_reason',
'capacity_pct','battery_V','battery_A']
for name,motor in MOTORS:
header += [name+'_C',name+'_A']
print(','.join(header))
started = brain.timer.time(MSEC)
next_log = started
review = TeamReview()
while True:
now = brain.timer.time(MSEC)
if competition.is_enabled() or controller.buttonB.pressing():
return 'CANCELLED'
if now-started >= MONITOR_MS:
return 'MONITOR_FINISHED'
if now >= next_log:
frame = read_conditions()
if competition.is_enabled() or controller.buttonB.pressing():
return 'CANCELLED'
if frame['ms']-started >= MONITOR_MS:
return 'MONITOR_FINISHED'
status = review.assess(frame)
emit_row(frame,status,frame['ms']-started)
next_log = frame['ms']+LOG_PERIOD_MS # No catch-up burst after a delay.
if not frame['valid']:
return 'DATA_FAULT'
if review.pause_latched:
return 'TEAM_PAUSE_REVIEW'
wait(20,MSEC)
# No driver or autonomous motion in this standalone logger.
def autonomous():
pass
def driver_control():
pass
competition = Competition(driver_control,autonomous)
if CONFIGURATION_VERIFIED:
print('logger result',log_conditions())
else:
print('Verify motor declarations and team thresholds before logging')
while True:
wait(100,MSEC)
Copy CSV data rows from the console into your trial record. The final result line is a status message, not a CSV data row. Each row has units in its header and a data-quality reason.
A temperature pause latches for manual review. The logger ends on that pause or invalid data. Cooling does not automatically rearm the same review object. A new test requires deliberate inspection and restart.
WITHIN_EXAMPLE_TEAM_THRESHOLDS is an advisory result for the listed observations. It does not certify battery health, traction, mechanism condition or overall safety.
11 | ADD LOW-RATE READINGS TO AN EXISTING ROUTINE
For loaded data, reuse the read_conditions, TeamReview and emit_row helpers in your existing project. Keep its existing device declarations and Competition object. Do not paste the standalone top-level loop or create duplicate motor objects.
# Sketch inside your existing output-owning loop:
# review and next_health_ms are created ONCE at route entry.
if now >= next_health_ms:
frame = read_conditions()
status = review.assess(frame)
next_health_ms = frame['ms'] + LOG_PERIOD_MS
# Apply an explicit project policy before further movement:
# invalid data -> stop/review; pause latch -> cancel and cleanup.
# Log only as often as the measured control timing allows.This sketch is not a complete movement controller. Connect the result to your own cancellation and cleanup policy. Include all owned mechanisms and their appropriate stop behaviour.
Measure acquisition and logging time. A 200 ms logger read budget is not permission to block a 20 ms motion loop for 200 ms. Split or schedule reads within a measured control budget, or shorten the measurement set. Low-rate health logging is not a fast emergency-stop system.
12 | BUILD A FAIR REPEATED-TRIAL TEST
- Fix the code version, start pose, route, robot load and success tolerances.
- Record battery identity, capacity, voltage and individual motor temperatures before each run.
- Record surface, wheel condition, run order and time since the previous run.
- Measure endpoint error, heading error, duration and stopping distance.
- Repeat each realistic condition enough times to see variation.
- Use matched comparisons or alternate condition order where practical.
- Record failures, cancellations and timeouts as outcomes.
- Change one setting and repeat the comparison.
Test normally encountered battery and thermal bands. Do not deliberately exhaust a battery, overheat a motor or obstruct a powered mechanism to manufacture an extreme test.
| Trial record field | Example entry |
|---|---|
| Trial / code version | T07 / commit or version label |
| Battery / initial state | Battery B / capacity and voltage readings |
| Motor temperatures | Individual values with °C units |
| Surface / load | Practice tiles / normal mechanism load |
| Outcome | Success, timeout or named fault |
| Measured errors | Endpoint mm; heading degrees |
| Timing / recovery | Route duration; cooling or battery change |
13 | COMMON MISTAKES
- Retuning after every bad run: first compare conditions and evidence.
- Using only a motor-group average: inspect individual temperatures.
- Calling capacity a loaded-voltage measurement: log both.
- Assuming current proves a jam: combine it with motion and mechanism evidence.
- Calling encoder discrepancy exact slip: verify scales and independent travel.
- Testing only a fresh battery and cold motors: cover realistic operating bands.
- Increasing demand when traction is poor: test controlled ramps and caps.
- Logging too much in the control loop: measure overhead and preserve timing checks.
CHALLENGES | LOG, COMPARE AND IMPROVE
- Inspect: adapt the motor list to your team’s full robot.
- Trace: test the warm-state hysteresis and latched pause using simulated temperatures.
- Compare: record the same route with matched starting temperatures and two normal battery states.
- Measure: compare encoder-derived and independently measured straight travel.
- Ramp: test two conservative acceleration limits and compare discrepancy and duration.
- Brake: measure stop range on two normally encountered surface conditions.
- Integrate: define a health-data fault policy using 09.4 cancellation and cleanup.
- Evaluate: justify an operating band using all runs, including failures.
Success evidence
CSV condition records, matched trial results, failure reasons, endpoint and timing measurements, and justified thresholds. Distinguish observed correlation from a proven cause.
OFFICIAL REFERENCES
Follow current VEX guidance for battery care and motor operation. The team thresholds and logging policies here are independently written examples requiring validation on your setup.