embedded-system-v1 / ACE2026 embedded code#49
Open
majockbim wants to merge 19 commits into
Open
Conversation
- can_common.c: short-circuit can_send_std/can_send_ext when no TX mailbox is free instead of relying solely on HAL_CAN_AddTxMessage's failure path. - can_imu.py: fall back to a flat 'import can_common' when the package-relative import fails, so the module can be run as a script for quick bench tests.
The previous troubleshooting entry only documented the F429 (PCLK1 = 84 MHz) prescaler/segment values. The joint-controller (F446, PCLK1 = 42 MHz) needs a different prescaler to land on the same 14-tq, 1 us bit time. Spell both out to avoid mis-tuned nodes on the bus.
Remove the per-joint ACCEL_*/GYRO_* macros from lsm6ds3tr.h now that all CAN addressing is built dynamically by can_common.h's CAN_BUILD_ID. The sensor driver and IMU ring buffer are otherwise unchanged.
Replace the ad-hoc CAN frame packing in main.c with the shared can_common / can_imu API so the joint-controller and Pi agree on node ids, message types, scaling, and endianness. - Vendor can_common and can_imu headers/sources into Core/ (matches the torque-controller layout). - main.c: bind to CAN_NODE_RIGHT_HIP at boot via can_common_init, send accel and gyro through can_send_imu_accel/gyro, and drain the RX ring each loop so future ESTOP / TORQUE_CMD frames don't get dropped. - main.c: enable AutoBusOff and AutoRetransmission so the controller recovers from transient bus faults; document the retransmit tradeoff for 500 Hz IMU. - stm32f4xx_it.c: dispatch CAN1 RX0/RX1/SCE IRQs into HAL_CAN_IRQHandler so can_common's interrupt-driven path actually runs.
Rewrite the Pi-side IMU buffer to consume the new CAN id scheme. - Route incoming standard frames by source node (NODE_LEFT_HIP ... NODE_RIGHT_KNEE) into one ring per joint, ignoring frames not addressed to NODE_PI. - Pair an accel + gyro before publishing a sample so downstream consumers always see a complete reading. - Downsample 500 Hz STM32 telemetry to a 187 Hz target with a Bresenham-style accumulator, sized to the ML model's expected input rate. - Narrow the parser exception handler to struct.error so unrelated bugs surface.
Move the torque-controller CubeIDE project from testing/stm32/ to src/stm32/, mirroring the joint-controller layout. The application code already uses the shared CAN API and the new node/message scheme, so this is a relocation with one IDE config retarget (debug log path) and the IDE=0 mask fix backported into its can_common.c.
…ogging Bring motor capability to the joint-controller by importing the AK70-9 VESC API and the can_motor / can_system message modules. The main loop now dispatches incoming TORQUE_CMD frames to comm_can_set_current, latches on ESTOP, refreshes the VESC current loop every 50 ms, and tracks motor feedback locally. can_set_motor_filter is added to can_common so each board only receives its own motor's extended frames. Add an edge-triggered fault logger over USART that captures IMU connection state, motor error transitions, ESTOP latch, first TORQUE_CMD, periodic CAN counters (RX overflow, TX drop, error ISR, I2C error) and bus-off / error-passive transitions. A LEC histogram attributes CAN errors to specific causes (Stuff/Form/Ack/BitR/BitD/CRC) so physical-layer faults can be diagnosed without a scope. Aggregated counters drain at 1 Hz so the 115200 UART doesn't get saturated by 500 Hz IMU traffic. Fix a CAN filter bug surfaced by the new defensive motor_id check: the standard-frame filters (ESTOP and TORQUE_CMD-to-self) didn't require IDE=0, so VESC extended frames whose ExtID[28:25]=0 accidentally leaked into FIFO0. Mask both filters to IDE=0 and add a runtime drop-and-warn for any extended frame whose controller_id doesn't match MY_MOTOR_CAN_ID.
Mirror the IDE=0 mask fix from the joint- and torque-controller copies into apis/can/Src/can_common.c so future projects copying from the canonical source pick up the corrected filters. Without this, every extended frame whose ExtID[28:25] = 0 (which is essentially every VESC packet) accidentally matches the type=0 condition in the ESTOP filter and leaks into FIFO0.
Add src/pi/pi_imu_torque.py — a combined IMU listener (paired, downsampled 500 -> 187 Hz) and torque-command CLI that mirrors pi_can_buffer.py + torque_cmd.py in one process. Includes per-node first-frame logging, stale-link watchdog (1 s threshold), motor-error edge logging, robust CAN exception recovery, and graceful bus-open failure with a setup hint. Designed as the template for the ML inference loop: the listener thread keeps IMU and motor-status state fresh in the background; the foreground only needs to call get_imu_data(node_id) / get_motor_status(node_id) and send_torque(bus, node_id, torque_nm).
End-to-end procedure for testing both hips + Pi: per-board firmware configuration, CubeIDE flashing flow, Pi setup, wiring (with termination rules), running and verifying with candump and the script, real-time logging reference (boot, edge events, aggregated 1 Hz counters, Pi console output), triage cheat sheet, safety notes, and known gaps.
Extend the IMU_GYRO frame from DLC 6 to DLC 8, packing motor position
(int16 * 10, +/-3276.7 deg range) into bytes 6-7 alongside the existing
gx/gy/gz fields. Position rides in the same CAN frame as the gyro
reading, so the joint angle and IMU sample share an exact on-wire
timestamp without needing a third frame per IMU tick — zero added bus
load.
C side:
- can_send_imu_gyro: gains a 5th arg motor_position (degrees).
- can_parse_imu_gyro: gains a 4th out-pointer motor_position; NULL-safe,
falls back to 0 on legacy DLC-6 frames.
Python side:
- send_imu_gyro: optional motor_position arg defaulting to 0.0.
- parse_imu_gyro: unchanged 3-tuple, kept for pi_can_buffer.py compat.
- parse_imu_gyro_pos: new 4-tuple (gx, gy, gz, position), returns None
for position on legacy DLC-6 frames.
Mirrored across apis/can/{Inc,Src,python} (canonical) and the
joint-controller's local copy.
…ility Strip the joint-controller back to torque-controller's structure plus the IMU send. Two motors moving simultaneously now works (single-motor already did). Remaining instability is suspected to be electrical / connectivity, not firmware. What was removed and why: - error_log module deleted (Inc/error_log.h, Src/error_log.c). The edge-triggered logging stack (LEC histogram, motor error transitions, IMU state changes, ESTOP latch, first TORQUE_CMD, filter leak, motor-feedback stale/resumed) drove multi-line UART bursts under bus errors. Each line at 115200 baud blocks the main loop ~10 ms; under load this missed the 50 ms motor refresh deadline and starved the VESC current loop. - HAL_CAN_ErrorCallback / HAL_I2C_ErrorCallback overrides removed — they fed the deleted counters. - Defensive motor_id check on every extended frame removed — the IDE=0 filter fix already prevents leaks at the hardware level. - Periodic IMU connection re-check (2 Hz I2C ping) removed. - Motor feedback stale warning removed — was a logging trigger only. - All edge-trigger state vars (prev_imu_state, prev_motor_error, motor_feedback_warned, first_torque_received, last_motor_rx_tick, last_imu_check_tick) removed. - UART command processor (HAL_UART_Receive_IT, HAL_UART_RxCpltCallback, process_command, rx_data/rx_buffer/tx_buffer/cmd_ready globals) removed. The Pi script is the user interface; the STM32 only emits status, doesn't take commands. - can_common.c rb_push no longer increments error_log overflow counter. What was kept: - Full motor control: TORQUE_CMD parse -> Iq compute -> clamp -> comm_can_set_current, with 50 ms refresh tick. - ESTOP latch. - IMU init + calibration + 200 Hz send (lowered from 500 Hz to free CAN bus contention with VESC extended frames). - Motor position co-located in the gyro frame (passed via motor_status.position from VESC feedback cache). - 1 Hz status dump over USART (mirrors torque-controller pattern): cmd current, motor pos/spd/cur/temp/err, ext rx count. - All API modules (can_motor, can_system, can/ak70_9, can_imu, can_common) — kept for future use, just not actively logged from. Pi side: hz = 200 in pi_imu_torque.py to match the firmware's new IMU rate. Without this, the Bresenham downsampler emits at the wrong rate.
…keleton/exoskeleton-embedded into embedded-system-v1
… origin Wiring is now physically secure, so the previous bandwidth concession can be reverted and the IMU can run at full rate again. - main.c: IMU_PERIOD_MS 5 -> 2 (500 Hz IMU send, was 200 Hz). - lsm6ds3tr.c: single-pole IIR lowpass on the DMA completion path (alpha = 0.2 -> ~18 Hz cutoff at 500 Hz fs). Suppresses motor switching noise and high-frequency vibration without adding meaningful latency. Seeded with the first raw sample to avoid a 0 -> raw ramp-up transient. - main.c: on the first VESC feedback frame received, fire comm_can_set_origin(MY_MOTOR_CAN_ID, 0) so the joint's encoder zero matches whatever pose it boots in. Mode 0 = temporary origin (lost on power cycle), so each fresh boot re-zeros without writing to VESC flash. - pi_imu_torque.py: hz = 500 to match the firmware. Bresenham accumulator still emits ~187 Hz to the ML-facing queue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
That's a wrap