Skip to content

Minho - #3

Open
minodori wants to merge 14 commits into
robertchoi:masterfrom
minodori:minho
Open

Minho#3
minodori wants to merge 14 commits into
robertchoi:masterfrom
minodori:minho

Conversation

@minodori

@minodori minodori commented Aug 1, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Added Isaac Sim launchers for basic simulation and SpotMicro URDF visualization.
    • Added an ESP32 servo test tool with individual, sequential, angle, and all-servo controls.
    • Added a Spot stand 3D model.
  • Bug Fixes

    • Improved servo channel mapping, calibration, pulse-width support, and direct angle control.
    • Enhanced simulation error reporting and portability across environments.
  • Documentation

    • Added guides covering simulation, servo calibration, hardware assembly, Raspberry Pi migration, Isaac Lab setup, power, wiring, and project progress.

minodori and others added 12 commits June 19, 2026 01:33
- Add Kinematics/__init__.py so Python recognizes it as a package
- Fix spotmicroai.py: use __file__-based sys.path to resolve import regardless of CWD
- Fix spotmicroai.py: os.chdir to Simulation/ dir on Robot init so relative paths (URDF, textures) resolve correctly when run from project root in VS Code
- Add traceback.print_exc() in main exception handler for better error visibility
- Add SIMULATION_ANALYSIS.md: Korean documentation of simulation code structure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Week01: servo motor comparison (MG996R, CLS6336HV, DS3218), BOM, wiring
Week02: right leg assembly with mixed servos, ESP32 motor test, PyBullet gait simulation analysis

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Converted w02_RightLeg_Test.MOV (56MB) to mp4 to stay under GitHub's 50MB limit.
Updated work02.md video reference accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- work03.md: Isaac Sim 4.5 + Isaac Lab 2.3.2 installation guide with Windows patches
- work04.md: SpotMicro body stand fabrication (aluminum profile + 3D print)
- stl/: Stand STL file, drawing, BOM, and assembly photos
- Simulation/: Isaac Sim hello world and SpotMicro URDF import test scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cross-checked against the repo's active code path: the OLED display
dependency and legacy RobotDisplay reference are commented-out dead
code, and the camera/lidar fields in spotmicroai.py are PyBullet
simulation-only, so they don't affect the real I2C-only wiring.
Document which parts of the repo actually use ROS (URDF/RViz packaging
only) versus the real control/simulation/RL code, which is entirely
ROS-free — relevant for the RPi 5 migration since no ROS setup is needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
board.SCL_1/SDA_1 (Jetson Nano bus 0) don't exist on RPi5's board
module; use board.SCL/SDA (work05.md 4.2). Also fix val_list in
test_servos_cali.py, which had only 1 element but was indexed by
servo number 0-11, crashing on any servo other than 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…h calibration

servo_controller.py addressed kit2 with the raw servo index (6-11)
instead of wrapping to that board's own channels (0-5), which didn't
match the simpler wiring test_servos_cali.py already assumed. Both
files now wire PCA9685 robertchoi#2 the same way as robertchoi#1: channels 0-5.

Also apply the actual DS3230/DS3235 pulse width spec (500-2500usec)
instead of ServoKit's default range, which was compressing commanded
angle deltas to roughly half the real servo travel.

Add RPi5 40-pin GPIO pinout diagram to work05.md, and a per-servo
calibration checklist (existing _servo_offsets values vs expected
physical pose) to work06.md for verifying the RPi5 port.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes update Jetson servo control, add Isaac Sim scripts, improve PyBullet diagnostics, and add SpotMicro firmware, hardware models, simulation analyses, assembly notes, and Raspberry Pi migration documentation.

Changes

Servo control

Layer / File(s) Summary
Servo initialization and channel mapping
JetsonNano/examples/test_servos_cali.py, JetsonNano/servo_controller.py
The controllers use default I²C pins, configure 500–2500 µs pulse ranges, map the second controller to channels 0–5, and write calibration angles directly.

Simulation runtime and analysis

Layer / File(s) Summary
Isaac Sim launch and SpotMicro import
Simulation/isaac_hello.py, Simulation/isaac_spotmicro.py
The scripts start visible Isaac Sim applications, initialize worlds, import SpotMicro, step rendering, and close the applications.
Simulation diagnostics and path setup
Simulation/pybullet_automatic_gait.py, Simulation/spotmicroai.py
PyBullet errors now include full tracebacks. SpotMicroAI derives import and working-directory paths from its script location.
PyBullet architecture documentation
Simulation/SIMULATION_ANALYSIS.md
The document describes simulation initialization, robot control, LIDAR, inverse kinematics, gait generation, keyboard input, data flow, and execution commands.

SpotMicro hardware and study materials

Layer / File(s) Summary
Hardware design and servo testing
study/minho/src/test_motor.cpp, study/minho/stl/Spot_Stand.stl, study/minho/work01.md, study/minho/work04.md
The ESP32 sketch tests eight servos through serial commands. The study documents cover printed parts, servo selection, wiring, costs, compatibility, and stand construction.
Weekly hardware and simulation records
study/minho/work02.md, study/minho/work02_SIMULATION_ANALYSIS.md
The documents record leg assembly, servo tests, PyBullet behavior, gait control, inverse kinematics, keyboard input, and planned hardware work.
Isaac Lab setup and training procedures
study/minho/work03.md
The document describes Windows installation, compatibility patches, SpotMicro task settings, reward weights, PPO training, recording, and joint constraints.
Raspberry Pi migration, assembly, and calibration
study/minho/work05.md, study/minho/work06.md, study/minho/work07.md
The documents define Raspberry Pi wiring, power, CPU policy inference, servo calibration, Ubuntu and NVMe setup, PCIe configuration, and verification steps.
Power and PCA9685 wiring updates
study/minho/work08.md, study/minho/work09.md
The documents record separated logic and servo power, PCA9685 daisy chaining at addresses 0x40 and 0x41, channel mappings, validation, and follow-up hardware checks.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant isaac_spotmicro
  participant SimulationApp
  participant URDFImporter
  participant World
  isaac_spotmicro->>SimulationApp: create visible application
  isaac_spotmicro->>URDFImporter: import SpotMicro URDF
  isaac_spotmicro->>World: add ground plane and initialize world
  isaac_spotmicro->>World: step simulation with rendering
  isaac_spotmicro->>SimulationApp: close application
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "Minho" is too generic and does not describe the servo, simulation, hardware, or documentation changes. Replace "Minho" with a concise summary of the primary changes, such as "Add servo calibration and SpotMicro simulation support".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Nitpick comments (1)
Simulation/SIMULATION_ANALYSIS.md (1)

155-167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new working-directory change in the init-order section.

Robot.__init__ in Simulation/spotmicroai.py now calls os.chdir(os.path.dirname(os.path.abspath(__file__))) as its first statement, before step 1 in this list. This is an important behavior change: the script no longer depends on the caller's launch directory for resolving relative URDF and texture paths.

Add this step to the initialization order list so readers understand why the script now works regardless of the directory from which it is launched.

📝 Proposed doc update
 ### 초기화 순서 (`__init__`)
 

-1. PyBullet 연결 (SHARED_MEMORY → 실패 시 GUI)
-2. GUI 디버그 슬라이더 추가 (Kp, Kd, MaxForce)
-3. loadModels() → URDF 로드 (바닥면, 계단, 로봇)
+0. os.chdir() → 작업 디렉터리를 스크립트 위치로 변경 (상대 경로 안정화)
+1. PyBullet 연결 (SHARED_MEMORY → 실패 시 GUI)
+2. GUI 디버그 슬라이더 추가 (Kp, Kd, MaxForce)
+3. loadModels() → URDF 로드 (바닥면, 계단, 로봇)

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @Simulation/SIMULATION_ANALYSIS.md around lines 155 - 167, Update the
__init__ initialization-order section to add a new step 0 documenting
os.chdir() changing the working directory to the script location for stable
relative-path resolution, before the PyBullet connection step and renumber the
subsequent steps accordingly.


</details>

<!-- cr-comment:v1:7c32f98313b47f18a4b2b68b -->

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @JetsonNano/examples/test_servos_cali.py:

  • Around line 31-39: Validate motro_num is an integer in 0..11 and cur_angle is
    an integer in 0..180 before applying modulo or updating either servo. Handle
    invalid or non-integer interactive input without selecting a servo, then retain
    the existing kit/kit2 branch only for validated values.
  • Line 10: Update the I²C initialization in
    JetsonNano/examples/test_servos_cali.py at lines 10-10 and
    JetsonNano/servo_controller.py at lines 15-15 to use the Jetson Nano Bus 0
    aliases SCL_1 and SDA_1, matching the PCA9685 wiring; retain SCL/SDA for
    Raspberry Pi 5 Bus 1 targets.

In @Simulation/isaac_hello.py:

  • Around line 5-17: Wrap the World setup and render loop in
    Simulation/isaac_hello.py (lines 5-17) and the corresponding World setup, URDF
    import, and render loop in Simulation/isaac_spotmicro.py (lines 5-43) with
    try/finally blocks, and move each app.close() call into its finally block so
    SimulationApp cleanup occurs on both normal completion and exceptions.

In @Simulation/isaac_spotmicro.py:

  • Line 15: Update the urdf_path initialization in Simulation/isaac_spotmicro.py
    to resolve the root-level urdf/spotmicroai_gen.urdf.xml relative to the script’s
    own directory, rather than the process working directory. Use the script
    location to navigate to its parent repository directory, then construct the
    existing URDF path while preserving absolute, forward-slash normalization.
  • Around line 18-32: Update the URDF import flow around
    omni.kit.commands.execute calls for “URDFCreateImportConfig” and
    “URDFParseAndImportFile” to validate each result status and the returned
    import_config/prim_path before use. Raise a clear error immediately when
    configuration creation or file parsing fails, and only apply configuration
    fields or print the successful import details after validation.

In @study/minho/src/test_motor.cpp:

  • Around line 146-149: Validate every character in cmd.substring(2) as a decimal
    digit before calling toInt() or moveServoTo() in the command-handling branch;
    reject the command immediately when any non-digit is present, while preserving
    valid angle parsing and movement.

In @study/minho/work01.md:

  • Around line 138-149: Update the DS3230 Pro section to consistently use the
    selected DS3218 PRO model identifier and verify its price, torque, voltage,
    dimensions, and horn compatibility against the comparison table and BOM. Also
    correct the related search term so all purchase guidance references the same
    servo model and matching specifications.
  • Around line 263-266: Make the PCA9685 quantity consistent throughout the
    document: reconcile the “구성 요소” entry with the BOM entry around the PCA9685
    quantity, and update the associated cost wherever necessary. Use one controller
    if it supports all 12 servos; otherwise retain two controllers consistently.

In @study/minho/work02_SIMULATION_ANALYSIS.md:

  • Around line 93-98: Align the leg ordering used by the Lp definition and
    inverse-kinematics inputs with positions() in Simulation/kinematicMotion.py,
    which returns [FR, FL, RR, RL]. Update the Lp rows and any associated documented
    input ordering so each row consistently maps to the correct leg while preserving
    the existing coordinates.

In @study/minho/work03.md:

  • Around line 72-131: Remove the manual IsaacLab patch block anchored at the
    “Windows 필수 패치” section in study/minho/work03.md (lines 72-131), including the
    rsl_rl nan_to_num patch referenced at lines 170-177; do not prescribe edits to
    installed IsaacLab or rsl_rl sources. Replace these instructions only with a
    supported, user-owned configuration or code approach that avoids modifying
    installed library files, or remove the guidance if no such approach exists.

In @study/minho/work05.md:

  • Around line 56-61: Add appropriate language identifiers to every fenced code
    block in study/minho/work05.md at lines 56-61, 74-82, 143-148, and 194-200, and
    in study/minho/work07.md at lines 66-70 and 75-78, preserving each block’s
    content.
  • Around line 63-64: Correct the PCA9685 mapping documentation to state that
    address 0x40 controls global servo indices 0–5 and address 0x41 controls global
    servo indices 6–11, while retaining each board’s local channel range of 0–5.
  • Around line 88-92: Update the servo power-sizing section around “서보 수” and “필요
    전류” to calculate the worst-case load for all 12 servos using the datasheet stall
    current, rather than assuming only six operate simultaneously. Include the
    battery discharge and fuse limits, and document the measured peak current before
    concluding that the recommended UBEC capacity is sufficient.
  • Around line 192-222: 보충된 Isaac Lab 정책 배포 설명에서 policy-to-servo 계약을 정의하고 검증하라.
    정책의 관절 순서, 각도 단위, action scale, observation 정규화, 그리고
    JetsonNano/servo_controller.py의 angleToServo(), _servo_offsets 및 부호 변환을 거쳐
    ServoKit.angle에 전달하는 절차를 명시하고 raw [1, 12] 출력을 직접 전송하지 않도록 한다. 또한 policy.pt를 생성하는
    TorchScript export 단계를 추가해 torch.save() 체크포인트와 구분하고, 실제 export 모델을 RPi 5에서 벤치마크한
    결과로 50Hz 및 1ms 미만 주장을 검증하라.
  • Around line 77-82: Update the power-tree entry feeding the RPi 5 USB-C PD
    input to explicitly require a USB-PD-capable 27W, 5V/5A source rather than a
    generic DC-DC 5V/5A converter. Alternatively, document a validated manual 5V/5A
    override and load-test voltage at the Pi.

In @study/minho/work07.md:

  • Around line 127-129: Remove the checklist item directing users to change
    board.SCL_1 to board.SCL, and replace it with a verification step confirming
    that JetsonNano/servo_controller.py constructs the bus with board.SCL and
    board.SDA.
  • Around line 103-104: Add rpi-eeprom to the prerequisite package list alongside
    i2c-tools, python3-venv, python3-pip, git, and build-essential so the later
    rpi-eeprom-config command is available.
  • Around line 80-91: Revise sections 5.2 and 5.3 to avoid treating the Gen2 and
    Gen3 measurements as equivalent: retain the removal of dtparam=pci ex1_gen=3 for
    stability, but describe the Gen2 performance conclusion as preliminary because
    Gen2 has a wide range and Gen3 has only one sample. Note that repeated
    controlled benchmarks are still needed to account for cache state, thermal
    throttling, workload differences, and single-sample variance.

Nitpick comments:
In @Simulation/SIMULATION_ANALYSIS.md:

  • Around line 155-167: Update the __init__ initialization-order section to add
    a new step 0 documenting os.chdir() changing the working directory to the
    script location for stable relative-path resolution, before the PyBullet
    connection step and renumber the subsequent steps accordingly.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `2812465b-f05b-4d46-a474-01eb1aa3045f`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9afd91d5905222df8b1d5a918cb7edeea0bd9488 and 488b49ce418e0323c33b91a595b2a49f3d2fc968.

</details>

<details>
<summary>⛔ Files ignored due to path filters (48)</summary>

* `study/minho/images/Assembly_original.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/CLS6336HV .jpg` is excluded by `!**/*.jpg`
* `study/minho/images/CLS6336HV _drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/CLS6336HV _specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/CLS6336HV_largerthan_MG996Rby_30mm.png` is excluded by `!**/*.png`
* `study/minho/images/DS3230Pro.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/DS3230Pro_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/DS3230Pro_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Leg_Horn_MG996R_vs_CLS6336HV.png` is excluded by `!**/*.png`
* `study/minho/images/MG996R_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/MG996R_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/MG996R_vs_CLS6336HV.png` is excluded by `!**/*.png`
* `study/minho/images/PDI-HV5523MG_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/PDI-HV5523MG_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/PDI-HV5523MG_vs_DS3230.png` is excluded by `!**/*.png`
* `study/minho/images/Parts_new.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Parts_original.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Pre-assembly_new.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/RPi5_GPIO_Pinout.svg` is excluded by `!**/*.svg`
* `study/minho/images/SPT5435LV-180.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/SPT5435LV-180_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/SPT5435LV-180_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Shoulder_Horn_MG996R_vs_CLS6336HV.png` is excluded by `!**/*.png`
* `study/minho/images/SpotMicro_new.xlsx` is excluded by `!**/*.xlsx`
* `study/minho/images/TD8120MG.png` is excluded by `!**/*.png`
* `study/minho/images/TD8135MG.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/TD8135MG_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/TD8135MG_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Wiring_new.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/Wiring_robertchoi_gitbook.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YP3235MG.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YP3235MG_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YP3235MG_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YP3235MG_vs_SPT5435LV-180.png` is excluded by `!**/*.png`
* `study/minho/images/YPinervo.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YPinervo_drawings.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/YPinervo_specs.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/image_511899841_14.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/image_511899841_9.jpg` is excluded by `!**/*.jpg`
* `study/minho/images/w02_RightLeg_Assembly.png` is excluded by `!**/*.png`
* `study/minho/images/w02_RightLeg_Test.mp4` is excluded by `!**/*.mp4`
* `study/minho/images/w02_pybullet_sim.mp4` is excluded by `!**/*.mp4`
* `study/minho/stl/Spot_Stand_Drawing.png` is excluded by `!**/*.png`
* `study/minho/stl/Spot_Stand_img1.JPG` is excluded by `!**/*.jpg`
* `study/minho/stl/Spot_Stand_img2.JPG` is excluded by `!**/*.jpg`
* `study/minho/stl/Spot_Stand_img3.JPG` is excluded by `!**/*.jpg`
* `study/minho/stl/Spot_Stand_img4.JPG` is excluded by `!**/*.jpg`
* `study/minho/stl/bom.png` is excluded by `!**/*.png`

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `JetsonNano/examples/test_servos_cali.py`
* `JetsonNano/servo_controller.py`
* `Kinematics/__init__.py`
* `Simulation/SIMULATION_ANALYSIS.md`
* `Simulation/isaac_hello.py`
* `Simulation/isaac_spotmicro.py`
* `Simulation/pybullet_automatic_gait.py`
* `Simulation/spotmicroai.py`
* `study/minho/src/test_motor.cpp`
* `study/minho/stl/Spot_Stand.stl`
* `study/minho/work01.md`
* `study/minho/work02.md`
* `study/minho/work02_SIMULATION_ANALYSIS.md`
* `study/minho/work03.md`
* `study/minho/work04.md`
* `study/minho/work05.md`
* `study/minho/work06.md`
* `study/minho/work07.md`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

# Default is to Bus 1; We are using Bus 0, so we need to construct the busio first ...
print("Initializing Servos")
i2c_bus0=(busio.I2C(board.SCL_1, board.SDA_1))
i2c_bus0=(busio.I2C(board.SCL, board.SDA))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 \
  'board\.SCL(_1)?|board\.SDA(_1)?|busio\.I2C|i2cdetect|pins? (27|28|3|5)' \
  JetsonNano study/minho

Repository: robertchoi/SpotMicroJetson

Length of output: 9042


🌐 Web query:

Adafruit Blinka Jetson Nano SCL_1 SDA_1 SCL SDA I2C bus GPIO pins board.SCL_1

💡 Result:

In the context of the Adafruit Blinka library on the NVIDIA Jetson Nano, board.SCL and board.SDA refer to the primary I2C bus (typically bus 1, accessible on pins 3 and 5 of the 40-pin J41 header) [1][2][3]. The identifiers board.SCL_1 and board.SDA_1 refer to the secondary I2C bus (typically bus 0, accessible on pins 27 and 28 of the J41 header) [1][2][4][3]. When using CircuitPython code on the Jetson Nano via Blinka, you can access these buses by passing the corresponding board constants to the busio.I2C class [5][2][4]: For the primary I2C bus: i2c = busio.I2C(board.SCL, board.SDA) For the secondary I2C bus: i2c = busio.I2C(board.SCL_1, board.SDA_1) Key details regarding these pins include: - Primary I2C (Bus 1): Accessible on pins 3 (SDA) and 5 (SCL) [3]. This is the default bus often used for standard I2C peripherals [2]. - Secondary I2C (Bus 0): Accessible on pins 27 (SDA) and 28 (SCL) [3]. This bus is sometimes used if the primary bus is occupied or if specific hardware requirements necessitate the use of the secondary bus [6][4]. - Troubleshooting: If you encounter issues communicating with an I2C device, ensure you are using the correct bus and pins for your wiring [4]. You can verify the bus availability and device addresses using system-level commands like sudo i2cdetect -r -y 1 (for bus 1) or sudo i2cdetect -r -y 0 (for bus 0) [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Changed I²C initialization sites:"
python3 - <<'PY'
import pathlib, re
paths = [
    pathlib.Path("JetsonNano/examples/test_servos_cali.py"),
    pathlib.Path("JetsonNano/servo_controller.py"),
]
pattern = re.compile(r"board\.(?:SCL|SDA)(?:_1)?")
for p in paths:
    text = p.read_text()
    print(f"\n--- {p} ---")
    for i, line in enumerate(text.splitlines(), 1):
        if "busio.I2C" in line or "SCL" in line or "SDA" in line:
            print(f"{i}: {line}")

docs = list(pathlib.Path("study/minho").glob("work*.md"))
for p in docs:
    text = p.read_text(errors="ignore")
    if "SCL_1" in text or "SCL," in text:
        print(f"\n--- {p} ---")
        for i, line in enumerate(text.splitlines(), 1):
            if "SCL_1" in line or "SCL," in line or "i2cdetect" in line or "pin" in line.lower() and ("27" in line or "28" in line or "3" in line or "5" in line):
                print(f"{i}: {line}")
PY

echo
echo "Git diff names/status:"
git diff -- name-status | sed -n '1,120p'

Repository: robertchoi/SpotMicroJetson

Length of output: 1587


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked Jupyter notebooks and docs mentioning PCA9685 wiring or I²C bus:"
python3 - <<'PY'
import pathlib, re
for p in pathlib.Path('.').rglob('*'):
    if p.is_file() and p.suffix.lower() in {'.ipynb', '.md'}:
        try:
            text = p.read_text(errors='ignore')
        except UnicodeError:
            continue
        if 'PCA9685' in text or '0x40' in text or 'i2cdetect' in text or 'Pin 28' in text or 'SCL_1' in text or 'SCL,' in text:
            lines = text.splitlines()
            hits = [(i+1, line.strip()) for i, line in enumerate(lines)
                    if any(s in line for s in ['PCA9685', '0x40', 'i2cdetect', 'Pin 28', 'SCL_1', 'SCL,', 'Bus 0 (pins 28,27)'])]
            if hits:
                print(f"\n--- {p} ---")
                for idx, line in hits[:30]:
                    print(f"{idx}: {line}")
PY

Repository: robertchoi/SpotMicroJetson

Length of output: 2386


Keep Jetson Nano on Bus 0 aliases.

JetsonNano/examples/test_servos_cali.py and JetsonNano/servo_controller.py now call busio.I2C(board.SCL, board.SDA), which selects Jetson Nano Bus 1 (J41 pins 3/5) instead of Bus 0 (pins 27/28) where the PCA9685 wiring is documented. Match each target to the I²C bus tied to its PCA9685 wiring: keep SCL_1/SDA_1 for Jetson Nano Bus 0, use SCL/SDA for Raspberry Pi 5 Bus 1.

📍 Affects 2 files
  • JetsonNano/examples/test_servos_cali.py#L10-L10 (this comment)
  • JetsonNano/servo_controller.py#L15-L15
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@JetsonNano/examples/test_servos_cali.py` at line 10, Update the I²C
initialization in JetsonNano/examples/test_servos_cali.py at lines 10-10 and
JetsonNano/servo_controller.py at lines 15-15 to use the Jetson Nano Bus 0
aliases SCL_1 and SDA_1, matching the PCA9685 wiring; retain SCL/SDA for
Raspberry Pi 5 Bus 1 targets.

Comment on lines 31 to +39
motro_num=int(input("Enter Servo to rotate (0-11): "))

# new angle to be written on selected motor
cur_angle=int(input("Enter new angles (0-180): "))

# increase(decrease) prev_angle to angle by 1 degree
if prev_angle:
sweep = range(prev_angle, cur_angle, 1) if (prev_angle < cur_angle) else range(prev_angle, cur_angle, -1)

for degree in sweep:
if motro_num < 6:
kit.servo[int(motro_num%6)].angle = cur_angle
else:
kit2.servo[int(motro_num%6)].angle = cur_angle
time.sleep(0.01)
else:
if motro_num < 6:
kit.servo[int(motro_num%6)].angle = cur_angle
else:
kit2.servo[int(motro_num%6)].angle = cur_angle


prev_angle = val_list[motro_num] No newline at end of file
if motro_num < 6:
kit.servo[int(motro_num % 6)].angle = cur_angle
else:
kit2.servo[int(motro_num % 6)].angle = cur_angle No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject invalid servo indexes before applying % 6.

The modulo operation is valid only after motro_num is confirmed to be in 0..11. With the current code, 12 selects kit2.servo[0], and -1 selects kit.servo[5]. Both inputs silently select a different servo.

Validate the servo index and angle before the branch. Handle non-integer input if this remains an interactive tool.

Proposed validation
-        motro_num=int(input("Enter Servo to rotate (0-11): "))
+        try:
+            motro_num = int(input("Enter Servo to rotate (0-11): "))
+            cur_angle = int(input("Enter new angles (0-180): "))
+        except ValueError:
+            print("Servo index and angle must be integers")
+            continue

-        cur_angle=int(input("Enter new angles (0-180): "))
+        if not 0 <= motro_num < 12 or not 0 <= cur_angle <= 180:
+            print("Servo index must be 0-11 and angle must be 0-180")
+            continue
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
motro_num=int(input("Enter Servo to rotate (0-11): "))
# new angle to be written on selected motor
cur_angle=int(input("Enter new angles (0-180): "))
# increase(decrease) prev_angle to angle by 1 degree
if prev_angle:
sweep = range(prev_angle, cur_angle, 1) if (prev_angle < cur_angle) else range(prev_angle, cur_angle, -1)
for degree in sweep:
if motro_num < 6:
kit.servo[int(motro_num%6)].angle = cur_angle
else:
kit2.servo[int(motro_num%6)].angle = cur_angle
time.sleep(0.01)
else:
if motro_num < 6:
kit.servo[int(motro_num%6)].angle = cur_angle
else:
kit2.servo[int(motro_num%6)].angle = cur_angle
prev_angle = val_list[motro_num]
\ No newline at end of file
if motro_num < 6:
kit.servo[int(motro_num % 6)].angle = cur_angle
else:
kit2.servo[int(motro_num % 6)].angle = cur_angle
try:
motro_num = int(input("Enter Servo to rotate (0-11): "))
cur_angle = int(input("Enter new angles (0-180): "))
except ValueError:
print("Servo index and angle must be integers")
continue
if not 0 <= motro_num < 12 or not 0 <= cur_angle <= 180:
print("Servo index must be 0-11 and angle must be 0-180")
continue
if motro_num < 6:
kit.servo[int(motro_num % 6)].angle = cur_angle
else:
kit2.servo[int(motro_num % 6)].angle = cur_angle
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@JetsonNano/examples/test_servos_cali.py` around lines 31 - 39, Validate
motro_num is an integer in 0..11 and cur_angle is an integer in 0..180 before
applying modulo or updating either servo. Handle invalid or non-integer
interactive input without selecting a servo, then retain the existing kit/kit2
branch only for validated values.

Comment thread Simulation/isaac_hello.py
Comment on lines +5 to +17
app = SimulationApp({"headless": False})

from omni.isaac.core import World

world = World()
world.reset()

print("Isaac Sim 실행 중...")

while app.is_running():
world.step(render=True)

app.close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files:\n'
git ls-files | rg '(^|/)isaac_(hello|spotmicro|spotmicroai_gen).*' || true

printf '\nRelevant snippets:\n'
for f in Simulation/isaac_hello.py Simulation/isaac_spotmicro.py; do
  if [ -f "$f" ]; then
    echo "--- $f"
    cat -n "$f" | sed -n '1,90p'
  else
    echo "MISSING $f"
  fi
done

printf '\nSearch SimulationApp usage:\n'
rg -n "SimulationApp|app\.close|finally|is_running|while .*is_running" Simulation 2>/dev/null || true

Repository: robertchoi/SpotMicroJetson

Length of output: 2668


🌐 Web query:

Isaac Sim SimulationApp close cleanup and shutdown Python documentation

💡 Result:

To properly close and shut down an Isaac Sim application using the SimulationApp class, you should call the close method [1][2]. This method is the standard way to perform cleanup and terminate the Omniverse Toolkit application [1][3]. The close method supports several optional parameters to control the shutdown behavior: - wait_for_replicator (bool): When set to True (default), the system waits for any pending Replicator workflows to complete before shutting down [1][3]. - skip_cleanup (bool): When set to True, the application performs an immediate exit without executing standard cleanup procedures. If False (default), it performs a graceful shutdown with full cleanup [1][4]. - exit_code (int): Allows you to specify the process exit status when using fast shutdown paths [1][3]. Basic usage example: from isaacsim import SimulationApp simulation_app = SimulationApp({"headless": True}) #... perform simulation work... # Graceful shutdown with cleanup simulation_app.close For immediate exit without performing standard cleanup: simulation_app.close(skip_cleanup=True) Additional state-tracking methods are available to check the lifecycle of the application: - is_exiting: Returns True if close has already been called [1][4]. - is_running: Returns True if the application is currently running with a valid stage [4]. Note that when building advanced custom integrations, some low-level implementations may involve direct calls to shutdown and framework.unload_all_plugins, but for standard standalone Python scripts, the SimulationApp.close method is the recommended interface [2][5].

Citations:


Close each SimulationApp in a finally block.

Both scripts call app.close() only from the normal execution path. If setup or World.step() raises an exception, Isaac Sim cleanup and shutdown are skipped. Wrap each script’s World setup, URDF import, and render loop in try, then call app.close() in finally.

try:
    # setup and render loop
    ...
finally:
    app.close()
📍 Affects 2 files
  • Simulation/isaac_hello.py#L5-L17 (this comment)
  • Simulation/isaac_spotmicro.py#L5-L43
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Simulation/isaac_hello.py` around lines 5 - 17, Wrap the World setup and
render loop in Simulation/isaac_hello.py (lines 5-17) and the corresponding
World setup, URDF import, and render loop in Simulation/isaac_spotmicro.py
(lines 5-43) with try/finally blocks, and move each app.close() call into its
finally block so SimulationApp cleanup occurs on both normal completion and
exceptions.

import omni.kit.commands
from isaacsim.asset.importer.urdf import _urdf

urdf_path = os.path.abspath(r"urdf\spotmicroai_gen.urdf.xml").replace("\\", "/")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Resolve the URDF from the script location.

Line 15 resolves urdf/spotmicroai_gen.urdf.xml from the process working directory. The repository stores that file in the root-level urdf/ directory, which is a sibling of Simulation/. Starting this script from Simulation/ therefore resolves a nonexistent Simulation/urdf/... path. (github.com)

Proposed fix
-import os
+from pathlib import Path
 ...
-urdf_path = os.path.abspath(r"urdf\spotmicroai_gen.urdf.xml").replace("\\", "/")
+urdf_path = (
+    Path(__file__).resolve().parent.parent
+    / "urdf"
+    / "spotmicroai_gen.urdf.xml"
+)
+if not urdf_path.is_file():
+    raise FileNotFoundError(f"URDF file not found: {urdf_path}")
+urdf_path = str(urdf_path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Simulation/isaac_spotmicro.py` at line 15, Update the urdf_path
initialization in Simulation/isaac_spotmicro.py to resolve the root-level
urdf/spotmicroai_gen.urdf.xml relative to the script’s own directory, rather
than the process working directory. Use the script location to navigate to its
parent repository directory, then construct the existing URDF path while
preserving absolute, forward-slash normalization.

Comment on lines +18 to +32
result, import_config = omni.kit.commands.execute("URDFCreateImportConfig")
import_config.merge_fixed_joints = False
import_config.fix_base = False
import_config.make_default_prim = True
import_config.self_collision = False
import_config.default_drive_type = _urdf.UrdfJointTargetType.JOINT_DRIVE_POSITION

result, prim_path = omni.kit.commands.execute(
"URDFParseAndImportFile",
urdf_path=urdf_path,
import_config=import_config,
)

print(f"임포트 결과: {result}")
print(f"prim_path: {prim_path}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files matching isaac_spotmicro.py:\n'
fd -a 'isaac_spotmicro\.py$' . || true

file="$(fd 'isaac_spotmicro\.py$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  printf '\nFile: %s\n' "$file"
  wc -l "$file"
  sed -n '1,220p' "$file" | cat -n
fi

printf '\nSearch URDF command usage in repo:\n'
rg -n "URDFCreateImportConfig|URDFParseAndImportFile|omni\.kit\.commands\.execute" . || true

printf '\nSearch Isaac Sim documentation references in repo:\n'
rg -n "isaacsim\.asset\.importer\.urdf|URDFCreateImportConfig|URDFParseAndImportFile" . || true

Repository: robertchoi/SpotMicroJetson

Length of output: 2522


🌐 Web query:

Isaac Sim API URDFCreateImportConfig URDFParseAndImportFile omni.kit.commands.execute status prim_path import_config

💡 Result:

In modern versions of NVIDIA Isaac Sim, the use of omni.kit.commands for URDF importing is considered deprecated [1][2]. The recommended approach is to use the direct Python API provided by the isaacsim.asset.importer.urdf extension [3][4]. Deprecated Command Approach In older workflows, the URDF import process was typically managed through omni.kit.commands.execute [5][6]: 1. Configuration: A configuration object was created using the URDFCreateImportConfig command [5][6]. status, import_config = omni.kit.commands.execute("URDFCreateImportConfig") 2. Importing: The URDFParseAndImportFile command was then executed with the configuration and the file path [5][6]: result, prim_path = omni.kit.commands.execute( "URDFParseAndImportFile", urdf_path=path_to_urdf, import_config=import_config) The prim_path returned by this command indicates the location of the imported robot on the stage [5][2]. Recommended Modern Approach Current documentation advises using the URDFImporter class directly [1][3]: 1. Define Configuration: Use the URDFImporterConfig class to set parameters like mesh merging, collision settings, and output paths [3][4]. 2. Import: Use the URDFImporter class to perform the import, which returns the path to the resulting USD asset [3][4]. from isaacsim.asset.importer.urdf import URDFImporter, URDFImporterConfig # Define settings import_config = URDFImporterConfig( urdf_path="path/to/robot.urdf", usd_path="path/to/output.usd", merge_mesh=False, collision_from_visuals=False) # Perform import importer = URDFImporter(import_config) output_path = importer.import_urdf

Citations:


Stop when URDF configuration or import fails.

If URDFCreateImportConfig or URDFParseAndImportFile returns a failure status, do not continue importing. Check both statuses and validate the returned prim_path/configuration before use, and raise a clear error when import creation or parsing fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Simulation/isaac_spotmicro.py` around lines 18 - 32, Update the URDF import
flow around omni.kit.commands.execute calls for “URDFCreateImportConfig” and
“URDFParseAndImportFile” to validate each result status and the returned
import_config/prim_path before use. Raise a clear error immediately when
configuration creation or file parsing fails, and only apply configuration
fields or print the successful import details after validation.

Comment thread study/minho/work05.md
Comment on lines +88 to +92
| 서보 1개 동작 전류 | ~500mA (피크) |
| 서보 수 | 12개 |
| 동시 동작 가정 | 최대 6개 동시 |
| 필요 전류 | 6 × 500mA = **3A 이상** |
| 권장 UBEC | **5A 이상** (여유 고려) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files around study/minho/work05.md:\n'
if [ -f study/minho/work05.md ]; then
  nl -ba study/minho/work05.md | sed -n '1,140p'
else
  echo 'study/minho/work05.md not found'
  fd -a 'work05\.md$' .
fi

printf '\nSearch for servo, UBEC, current, stall, load related terms:\n'
rg -n --ignore-case '서보|servo|ubec|current|전류|stall|stall current|500|동시|simultaneous' . || true

Repository: robertchoi/SpotMicroJetson

Length of output: 251


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Repository files around study/minho/work05.md:\n'
if [ -f study/minho/work05.md ]; then
  awk '{printf "%6d  %s\n", NR, $0}' study/minho/work05.md | sed -n '1,140p'
else
  echo 'study/minho/work05.md not found'
  find . -type f -name 'work05.md' 2>/dev/null
fi

printf '\nSearch for servo, UBEC, current, stall, load related terms:\n'
grep -RIn --ignore-case '서보\|servo\|ubec\|current\|전류\|stall\|stall current\|500\|동시\|simultaneous' . || true

Repository: robertchoi/SpotMicroJetson

Length of output: 50383


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'study/minho/work05.md lines 80-100:\n'
sed -n '80,100p' study/minho/work05.md | awk '{printf "%4d  %s\n", NR+79, $0}'

printf '\nRelevant servo/UBEC/current/load lines in study/minho/work05.md:\n'
grep -nIn --ignore-case 'сер보\|servo\|ubec\|current\|전류\|stall\|동시\|load\|12\|6 \* 500' study/minho/work05.md || true

printf '\nSearch repository for stall current documentation or measured load data:\n'
grep -RIn --ignore-case 'stall current\|stall\|servo current\|500mA\|동시\|12 servos\|12*servo\|UBEC rating\|BEC thermal' . --exclude-dir=.git || true

Repository: robertchoi/SpotMicroJetson

Length of output: 7505


Size the servo supply from the maximum possible load.

The calculation uses six servos × 500 mA and recommends a 5 A UBEC, but the robot uses 12 servos and the source can output 12 joint angles at once. Use the servo datasheet stall current for the worst-case simultaneous load, add battery discharge and fuse limits, and document the measured peak load before treating the UBEC as sufficient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work05.md` around lines 88 - 92, Update the servo power-sizing
section around “서보 수” and “필요 전류” to calculate the worst-case load for all 12
servos using the datasheet stall current, rather than assuming only six operate
simultaneously. Include the battery discharge and fuse limits, and document the
measured peak current before concluding that the recommended UBEC capacity is
sufficient.

Comment thread study/minho/work05.md
Comment on lines +192 to +222
Isaac Lab RSL-RL로 학습한 SpotMicro 정책 네트워크 구조:

```
입력 (관측값): ~48차원
→ Linear(48 → 256) → ELU
→ Linear(256 → 128) → ELU
→ Linear(128 → 64) → ELU
→ Linear(64 → 12) (출력: 서보 12개 목표각)
```

- 파라미터 수: 약 50,000개 (매우 작음)
- 실행 주기: 50Hz (20ms 마다 1회)
- 1회 forward pass 소요 시간 (RPi 5 CPU): **< 1ms**

→ RPi 5 Cortex-A76 @ 2.4GHz에서 실시간 실행에 전혀 문제없음

### 5.3 RPi 5에서 실행 방법

```python
import torch

# CPU로 명시 로드 (CUDA 없어도 동작)
policy = torch.jit.load("policy.pt", map_location="cpu")
policy.eval()

obs = torch.tensor(observation, dtype=torch.float32) # shape: [1, 48]
with torch.no_grad():
action = policy(obs) # shape: [1, 12]
```

PyTorch CPU 버전(`pip install torch --index-url https://download.pytorch.org/whl/cpu`)만 설치하면 된다. CUDA 드라이버 불필요.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n \
  'torch\.jit\.(load|save)|export_policy_as_(jit|onnx)|policy\.pt|observation|action|servo_offsets|angleToServo' \
  --glob '*.py' --glob '*.md' .

Repository: robertchoi/SpotMicroJetson

Length of output: 9895


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,160p'

echo "== study/minho/work05.md relevant =="
sed -n '1,260p' study/minho/work05.md

echo "== JetsonNano/servo_controller.py =="
cat -n JetsonNano/servo_controller.py

echo "== study/minho/work06.md relevant =="
sed -n '1,140p' study/minho/work06.md

Repository: robertchoi/SpotMicroJetson

Length of output: 19521


🌐 Web query:

PyTorch TorchScript saved model format .pt file torch.jit.load documentation

💡 Result:

In PyTorch, a.pt file generated via torch.jit.save represents a serialized TorchScript model, which is distinct from a standard pickle-based torch.save file [1][2]. This TorchScript format is designed for deployment, allowing models to be loaded and executed in both Python and C++ environments without requiring the original Python source code [3][1][2]. Key Technical Details: - Functionality: The torch.jit.load function is used to deserialize these models into a ScriptModule or ScriptFunction [4][5]. - Format Anatomy: Internally, the.pt file is a ZIP archive [3][6]. It contains various components, including: - data.pkl: Contains the pickled top-level module state [3][6]. - constants.pkl: Stores tensor constants referenced within the model's code [3][6]. - Code files: Human-readable.py files containing the model's methods and class definitions in valid Python syntax [3][6]. - Loading Behavior: When loading a model with torch.jit.load, all tensors are initially loaded onto the CPU before being moved to their original devices (or specified mapping devices) [4][7][5]. - Security: As with any deserialization process, you should never load model data from untrusted sources, as the process can execute arbitrary code [5]. Basic Usage Example: import torch # Load the model model = torch.jit.load('scriptmodule.pt') # Optionally, load onto a specific device model = torch.jit.load('scriptmodule.pt', map_location='cuda:0') For further information on the internals of this serialization format, you can refer to the official PyTorch documentation on serialization notes or the technical documentation provided in the PyTorch source code [3][8][1].

Citations:


Define and validate the policy-to-servo deployment contract.

The policy.pt example says the output is twelve target servo angles, but the current repository only shows the policy output format. JetsonNano/servo_controller.py uses angleToServo() mappings with per-joint _servo_offsets and sign flips, so a raw [1, 12] tensor must not be sent directly to the servos. Document the joint order, angle units, action scale, observation normalization, and any offset/sign conversion needed to reach ServoKit.angle.

Also show the TorchScript export step that creates policy.pt; a training checkpoint saved with torch.save() is not compatible with torch.jit.load(). Benchmark the actual exported model on the target RPi 5 before claiming 50Hz inference at <1 ms.

🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 194-194: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work05.md` around lines 192 - 222, 보충된 Isaac Lab 정책 배포 설명에서
policy-to-servo 계약을 정의하고 검증하라. 정책의 관절 순서, 각도 단위, action scale, observation 정규화,
그리고 JetsonNano/servo_controller.py의 angleToServo(), _servo_offsets 및 부호 변환을 거쳐
ServoKit.angle에 전달하는 절차를 명시하고 raw [1, 12] 출력을 직접 전송하지 않도록 한다. 또한 policy.pt를 생성하는
TorchScript export 단계를 추가해 torch.save() 체크포인트와 구분하고, 실제 export 모델을 RPi 5에서 벤치마크한
결과로 50Hz 및 1ms 미만 주장을 검증하라.

Comment thread study/minho/work07.md
Comment on lines +80 to +91
### 5.2 실측 — 속도 차이 없음

| 상태 | `hdparm -Tt` buffered disk reads |
|------|-----------------------------------|
| Gen2 (기본) | 430.62 → 735.40 MB/sec (측정 변동) |
| Gen3 (적용 후) | 736.38 MB/sec |

Gen3로 링크를 올려도 실측 속도는 Gen2 최고치와 사실상 동일 → **병목이 PCIe 링크가 아니라 SSD 자체의 순차읽기 한계(또는 hdparm의 싱글스레드 측정 한계)** 였던 것으로 판단.

### 5.3 결론 — Gen2로 원복

RPi 5의 외장 PCIe 커넥터(M.2 HAT용) Gen3 동작은 **공식 지원 대상이 아님** (신호 무결성 문제로 데이터 커럽션 위험 가능). 실측 이득이 없는 상황에서 위험을 감수할 이유가 없어 Gen2로 되돌림.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
sed -n '1,170p' study/minho/work07.md | cat -n

echo
echo "== occurrences of rpi-eeprom/Gen2/Gen3/hdparm in related docs =="
rg -n "rpi-eeprom|Gen[23]|hdparm|SCL|SCL_1|SDA|NVMe|M.2|board\.SCL|board\.SDA" study/minho/work07.md study/minho/work05.md 2>/dev/null || true

Repository: robertchoi/SpotMicroJetson

Length of output: 7516


🌐 Web query:

Raspberry Pi 5 M.2 HAT Gen3 PCIe support unstable data corruption

💡 Result:

Raspberry Pi 5 is officially designed and certified for PCIe Gen 2.0 speeds (5 GT/s) [1][2]. While users can manually enable PCIe Gen 3.0 (8 GT/s) via the dtparam=pciex1_gen=3 setting in /boot/firmware/config.txt, this configuration is explicitly unsupported and not certified, which can lead to unstable connections and data integrity risks [1][2]. Technical factors contributing to instability include: Clock Jitter: Professional-level testing has indicated that the Raspberry Pi 5’s PCIe interface exhibits high clock jitter near the threshold of Gen 3.0 requirements, making stable operation inconsistent [2]. Signal Integrity: The high-frequency signals required for Gen 3.0 are highly sensitive to physical factors, including cable quality, length, shielding, and the electrical properties of the M.2 HAT or adapter board [3][2]. Flimsy or poorly shielded cables often fail to maintain signal integrity at these speeds [3]. Advanced Error Reporting (AER): Users frequently observe "BadTLP" (Transaction Layer Packet) errors and other PCIe bus errors in system logs when forcing Gen 3.0 [4][5]. These correctable errors can increase significantly under I/O load, potentially leading to kernel panics, system freezes, or the SSD "falling off the bus" [3][5][6][7]. DRAM-less NVMe Limitations: When using DRAM-less SSDs, the interaction between the Raspberry Pi’s default firmware settings—which often disable Host Memory Buffer (HMB) to conserve memory—and PCIe instability can exacerbate failure modes, sometimes masking the underlying cause as a drive crash or timeout [8]. Official Recommendation: Because Gen 3.0 connections may be unstable, Raspberry Pi Ltd does not recommend it for production or critical data storage [1]. If you experience instability, data corruption, or system hangs, the primary recommended solution is to revert to the stable default of PCIe Gen 2.0 [3][7]. If you must attempt to optimize performance, ensure you are running the latest firmware and consider if the specific hardware combination (SSD and M.2 adapter) is known to be compatible, though this does not bypass the fundamental lack of Gen 3.0 certification [6][7].

Citations:


🌐 Web query:

Raspberry Pi 5 PCIe Gen3 dtparam pciex1_gen=3 official support data corruption

💡 Result:

The Raspberry Pi 5 is officially not certified for PCIe Gen 3.0 speeds [1][2]. While the device allows users to force PCIe Gen 3.0 via the dtparam=pciex1_gen=3 setting in /boot/firmware/config.txt, Raspberry Pi explicitly warns that this configuration may result in unstable connections [1][2]. Technical reports and community feedback indicate that using PCIe Gen 3.0 on the Raspberry Pi 5 can lead to several stability and reliability issues, including: - Data Errors and Link Instability: Users have documented high rates of "Corrected" PCIe bus errors, indicating data integrity issues at the link layer [3][4]. - Unpredictable Behavior: Forced Gen 3.0 speeds may cause devices to intermittently disappear, fail to initialize during boot, or experience system hangs under I/O load [5][6][7]. - Signal Integrity Challenges: The instability is frequently attributed to signal jitter and the physical limitations of the FPC (flexible printed circuit) cable and adapter boards, which are not designed to reliably carry the higher-frequency signals required for PCIe Gen 3.0 [3][5][4]. Because these issues stem from the hardware's physical limits and the lack of official certification, there is no software fix to guarantee stability at Gen 3.0. If you experience instability, data corruption, or boot failures, the standard recommendation is to revert to the default PCIe Gen 2.0 speed by removing the dtparam=pciex1_gen=3 line or explicitly setting dtparam=pciex1_gen=2 [3][5][6][7]. If necessary for stability, some users have even resorted to forcing PCIe Gen 1.0 speeds to further mitigate signal integrity issues [5].

Citations:


Do not treat the Gen2/Gen3 result as equivalent.

The Gen2 baseline ranges from 430.62 to 735.40 MB/sec, while Gen3 has one 736.38 MB/sec sample. Keep dtparam=pci ex1_gen=3 removed for stability, but describe the Gen2 conclusion as preliminary until repeated controlled benchmark results rule out cache state, thermal throttling, workload, or single-sample variance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work07.md` around lines 80 - 91, Revise sections 5.2 and 5.3 to
avoid treating the Gen2 and Gen3 measurements as equivalent: retain the removal
of dtparam=pci ex1_gen=3 for stability, but describe the Gen2 performance
conclusion as preliminary because Gen2 has a wide range and Gen3 has only one
sample. Note that repeated controlled benchmarks are still needed to account for
cache state, thermal throttling, workload differences, and single-sample
variance.

Comment thread study/minho/work07.md
Comment on lines +103 to +104
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y i2c-tools python3-venv python3-pip git build-essential

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the referenced document and relevant commands.
if [ -f study/minho/work07.md ]; then
  echo "== file exists =="
  wc -l study/minho/work07.md
  echo "== relevant lines =="
  sed -n '1,90p;95,110p' study/minho/work07.md | cat -n
else
  echo "study/minho/work07.md not found"
  fd -a 'work07.md' . || true
fi

echo "== occurrences of rpi-eeprom-config and apt install =="
rg -n "rpi-eeprom-config|rpi-eeprom|apt install|apt full-upgrade|apt update" study/minho/work07.md || true

echo "== package docs lookup: rpi-eeprom on Ubuntu =="
python3 - <<'PY'
from urllib.request import urlopen
for url in [
    "https://packages.ubuntu.com/search?keywords=rpi-eeprom&searchon=contents&suite=focal&section=all",
    "https://packages.ubuntu.com/search?keywords=rpi-eeprom&searchon=contents&section=restricted&suite=jammy&arch=amd64",
    "https://packages.ubuntu.com/wireless/rpi-eeprom",
]:
    print("URL:", url)
    try:
        text = urlopen(url, timeout=12).read().decode("utf-8", "replace")
        hits = [
            line.strip()
            for line in text.splitlines()
            if "rpi-eeprom-config" in line or "package rpi-eeprom" in line or "rpi-eeprom" in line
        ][:20]
        if hits:
            for line in hits:
                print("| ", line)
        else:
            print("[no hits]")
    except Exception as e:
        print("[failed]", e)
PY

Repository: robertchoi/SpotMicroJetson

Length of output: 4847


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check installed package metadata if this sandbox has Ubuntu package metadata available.
if command -v apt-cache >/dev/null 2>&1; then
  echo "== apt-cache show rpi-eeprom =="
  apt-cache show rpi-eeprom | sed -n '1,120p'
  echo "== apt-cache policy rpi-eeprom =="
  apt-cache policy rpi-eeprom || true
else
  echo "apt-cache not available"
fi

Repository: robertchoi/SpotMicroJetson

Length of output: 188


Install rpi-eeprom before using rpi-eeprom-config.

This document runs sudo rpi-eeprom-config, but the prerequisite installs do not install rpi-eeprom. Add rpi-eeprom to the install line, or update the install prerequisites to the actual minimal packages if rpi-eeprom is not required by later steps.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work07.md` around lines 103 - 104, Add rpi-eeprom to the
prerequisite package list alongside i2c-tools, python3-venv, python3-pip, git,
and build-essential so the later rpi-eeprom-config command is available.

Comment thread study/minho/work07.md
Comment on lines +127 to +129
- [ ] PCA9685 배선 후 `i2cdetect -y 1`로 `0x40`, `0x41` 응답 확인
- [ ] `servo_controller.py`의 `board.SCL_1` → `board.SCL` 수정 (work05.md 4.2절)
- [ ] `JetsonNano/examples/test_servos_cali.py`로 서보 개별 동작 테스트 (work06.md 조립 절차와 연계)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the stale board.SCL_1 migration step.

The current JetsonNano/servo_controller.py already constructs the bus with board.SCL and board.SDA. After cloning this branch, instruct the user to verify the mapping instead of editing a line that is already fixed.

Proposed checklist fix
- [ ] `servo_controller.py`의 `board.SCL_1` → `board.SCL` 수정 (work05.md 4.2절)
+ [ ] `JetsonNano/servo_controller.py`가 `busio.I2C(board.SCL, board.SDA)`를 사용하는지 확인
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- [ ] PCA9685 배선 후 `i2cdetect -y 1``0x40`, `0x41` 응답 확인
- [ ] `servo_controller.py``board.SCL_1``board.SCL` 수정 (work05.md 4.2절)
- [ ] `JetsonNano/examples/test_servos_cali.py`로 서보 개별 동작 테스트 (work06.md 조립 절차와 연계)
- [ ] PCA9685 배선 후 `i2cdetect -y 1``0x40`, `0x41` 응답 확인
- [ ] `JetsonNano/servo_controller.py``busio.I2C(board.SCL, board.SDA)`를 사용하는지 확인
- [ ] `JetsonNano/examples/test_servos_cali.py`로 서보 개별 동작 테스트 (work06.md 조립 절차와 연계)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work07.md` around lines 127 - 129, Remove the checklist item
directing users to change board.SCL_1 to board.SCL, and replace it with a
verification step confirming that JetsonNano/servo_controller.py constructs the
bus with board.SCL and board.SDA.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
study/minho/work08.md (1)

26-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add language identifiers to fenced code blocks.

markdownlint-cli2 reports MD040 at these fences. Use text for diagrams and command output, and bash for shell commands.

Also applies to: 42-47, 71-83, 90-96, 133-137

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 26 - 28, Add language identifiers to
every fenced code block in study/minho/work08.md, including the shown diagram
and the additional referenced blocks. Use text for diagrams or command output
and bash for shell-command blocks, resolving all MD040 warnings.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@study/minho/work08.md`:
- Around line 102-105: “전기적으로 문제 없음” 주장을 완화하고, 최종 하네스 기준으로 활성화된 I2C 풀업 구성과 케이블
길이를 기록하세요. 사용하려는 버스 속도와 함께 신호 무결성을 검증한 뒤에만 해당 배선을 전기적으로 신뢰할 수 있다고 기술하세요.
- Around line 45-46: Update the diagram around the PCA9685 boards to show
separate power paths from the 7.4V supply to each board’s servo V+ output,
including the 0x41 board used by servo positions 6–11. Label the logic
connection as VCC and the servo supply connection as V+, ensuring the 7.4V path
is not shown feeding either board’s logic rail.
- Around line 55-63: Revise the conclusion in the “검증 결과” section to apply only
to the tested 12V 어댑터 → 300W Buck → XL4015 → RPi 5 cascade, rather than all
cascaded buck converters. Remove the universal claim that one-stage topology is
always required, and state only that this tested two-stage configuration failed
while the listed one-stage configurations booted successfully.
- Around line 43-53: Revise the power-distribution section and diagram to
identify RPi 5 pins 2/4 as 5V header power, not GPIO power, and explicitly
address the USB-C protection bypass. Replace the generic “UBEC 5V/3A (or
XL4015)” recommendation with exact regulator voltage/current settings, servo and
PCB peak-current margin, fuse rating, overvoltage and reverse-polarity
protection, and the required config.txt current-limit setting.
- Around line 133-140: Update the scan-result interpretation in the PCA9685
verification notes: identify 0x40 and 0x41 as the validated configured device
addresses, describe 0x70 as the shared All-Call response, and remove or
reclassify 0x71 unless the driver explicitly enables Sub-Call 1.

---

Nitpick comments:
In `@study/minho/work08.md`:
- Around line 26-28: Add language identifiers to every fenced code block in
study/minho/work08.md, including the shown diagram and the additional referenced
blocks. Use text for diagrams or command output and bash for shell-command
blocks, resolving all MD040 warnings.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eeb010b4-e926-4e26-a436-516c78b750cf

📥 Commits

Reviewing files that changed from the base of the PR and between 488b49c and 8ec7f88.

⛔ Files ignored due to path filters (1)
  • study/minho/images/diagram_v1.0.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • study/minho/work08.md

Comment thread study/minho/work08.md
Comment on lines +43 to +53
[LiPo 11.1V] ─┬─→ [UBEC 5V/3A (or XL4015)] → RPi 5 GPIO Pin 2/4 ← 로직 전원
└─→ [300W Buck 7.4V] → 서보 12개 ← 파워 전원
(PCA9685 V+ 입력)
```

**핵심 원칙**: 로직 전원과 서보 전원을 분리하고, **GND만 공통**으로 연결.

- **로직 전원**: UBEC(Switching BEC) 또는 XL4015 → 응답 빠르고 노이즈 낮음 → RPi 5 PMIC 요구사항 충족
- **파워 전원**: 300W buck은 원래 대전류 지속 부하용 → 서보 12개 병렬 구동에 적합
- **분리 효과**: 서보 구동 시 발생하는 스파이크 노이즈가 RPi 로직으로 전파되지 않음

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching work08:"
fd -a 'work08\.md$' . || true

file="$(fd 'work08\.md$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  echo
  echo "Full file: $file"
  wc -l "$file"
  sed -n '1,120p' "$file" | cat -n
fi

echo
echo "Raspberry Pi references in repository:"
rg -n "RPi|Raspberry|GPIO|Pin 2|Pin 4|5V|XL4015|UBEC|Buck|PCA9685" study || true

echo
echo "Line 43-53 exact context if present:"
if [ -n "${file:-}" ]; then
  sed -n '35,60p' "$file" | cat -n
fi

Repository: robertchoi/SpotMicroJetson

Length of output: 20716


🌐 Web query:

Raspberry Pi 5 GPIO header pins 2 and 4 5V power pins

💡 Result:

On the Raspberry Pi 5, physical pins 2 and 4 on the 40-pin GPIO header are dedicated 5V power pins [1][2][3]. Key characteristics and considerations for these pins include: 1. Function: Pins 2 and 4 are directly connected to the 5V supply line of the board [1]. They can be used to provide power to external components or, conversely, to power the Raspberry Pi itself from an external 5V source [2][4]. 2. Voltage and Tolerance: The 5V pins provide the board's supply voltage. It is important to note that all GPIO logic pins on the Raspberry Pi 5 operate at 3.3V and are not 5V tolerant [5]. Connecting 5V signals directly to GPIO pins will damage the board [5]. 3. Powering the Pi via GPIO: While it is possible to power the Raspberry Pi 5 through pins 2 and 4, doing so bypasses the protections and USB-PD negotiation features present on the USB-C power input [4]. If you provide power through the GPIO header, ensure your power source is stable and appropriately rated for your setup [6]. 4. Current Configuration: When using high-power setups, the Raspberry Pi's power management system may need to be informed of the available current (e.g., via EEPROM configuration) to ensure stable operation, particularly if you are expecting to draw higher currents (up to 5A) [4]. Always verify your power source stability before connecting it to the GPIO header, as unfiltered or fluctuating voltage can cause the system to halt or reboot [6].

Citations:


🌐 Web query:

Raspberry Pi 5 power input USB-C PD 5V 5A guidelines

💡 Result:

The Raspberry Pi 5 uses USB-C Power Delivery (PD) to negotiate its power requirements [1][2]. While it operates well with standard 3A power supplies, the official recommendation for maximum performance and full peripheral support is a 5V/5A (25W) power supply [1][3][4]. Key guidelines regarding 5V/5A power input: Automatic Negotiation: The Raspberry Pi 5 firmware automatically detects if a connected power supply supports the 5V/5A Power Delivery profile [1][2]. Downstream USB Current: When a 5V/5A supply is detected, the board increases the total power available to its four USB ports from the default 600mA to 1.6A [1][3][4]. This is critical for driving high-power peripherals, such as SSDs and hard drives [1][5][2]. 3A Operation: If a 5V/5A supply is not detected (e.g., when using a standard 3A supply), the board defaults to a 600mA limit for downstream USB devices [1][4]. The board will still function for standard tasks, and users have the option to manually override this limit via firmware settings if needed, though this is generally intended for specific use cases [1][4]. USB Booting: USB booting is disabled by default if a 5V/5A supply is not detected [1]. This can be re-enabled by modifying firmware settings (e.g., setting PSU_MAX_CURRENT=5000 or usb_max_current_enable=1) [1]. Official Hardware: The Raspberry Pi 27W USB-C Power Supply is the officially recommended adapter that implements the 5V/5A PD profile, providing 5.1V at 5A [5][2]. Note that 5V/5A is an optional, rather than required, set of PDOs (Power Data Objects) in the USB-PD specification, so not all third-party PD chargers will support it [1].

Citations:


🌐 Web query:

Raspberry Pi 5 GPIO power in reverse supply fuse over voltage protection

💡 Result:

Powering a Raspberry Pi 5 through its GPIO header pins (specifically pins 2 and 4 for 5V) bypasses the dedicated protection circuitry built into the board's USB-C power input, such as overvoltage, overcurrent, and reverse polarity protection [1][2][3][4]. Key technical implications include: 1. Absence of Protection: The GPIO 5V rails are connected directly to the internal power plane [1][4]. Consequently, there is no onboard fuse or transient voltage suppressor (TVS) guarding these pins against power surges, accidental reverse polarity, or voltage spikes [1][5][4]. Applying reverse voltage or excessive voltage to these pins can cause immediate and permanent damage to the board's PMIC and other sensitive components [1][5][3][4]. 2. Power Delivery Limitations: Unlike the official USB-C power input, which supports USB Power Delivery (PD) negotiation to request up to 5A at 5V, the GPIO method does not perform this negotiation [6][3]. When powering via GPIO, the system defaults to a 3A limit [6][3]. To enable higher peripheral current limits when using a stable, high-current supply via GPIO, you must manually override the limit by adding the parameter usb_max_current_enable=1 to your configuration (e.g., in config.txt) [6][7][3]. 3. Recommendations: Using the official USB-C power input is the only recommended and safe method for most users [8][3][4]. If you choose to power via GPIO for specialized applications (e.g., custom HATs, battery-powered projects), you must provide your own external, high-quality, regulated 5V power supply that includes its own integrated overcurrent, overvoltage, and reverse polarity protection [8][5][3]. It is also critical to use robust cabling to handle the required current and to ensure you do not connect power to both the USB-C port and the GPIO pins simultaneously, as this lacks protection and can damage the board [3].

Citations:


Specify the GPIO power contract before replacing the design.

RPi 5 header pins 2 and 4 are 5V power pins, not GPIO signal pins, so the text should not label this as GPIO power. Powering through them bypasses USB-C power protections, and GPIO-only default current limits may disable USB services in this robot setup. Replace UBEC 5V/3A (or XL4015) with the exact regulator settings, peak-current margin required by the servos/PCBs, fuse, overvoltage protection, reverse-polarity protection, and any required config.txt current limit setting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 43 - 53, Revise the power-distribution
section and diagram to identify RPi 5 pins 2/4 as 5V header power, not GPIO
power, and explicitly address the USB-C protection bypass. Replace the generic
“UBEC 5V/3A (or XL4015)” recommendation with exact regulator voltage/current
settings, servo and PCB peak-current margin, fuse rating, overvoltage and
reverse-polarity protection, and the required config.txt current-limit setting.

Comment thread study/minho/work08.md
Comment on lines +45 to +46
└─→ [300W Buck 7.4V] → 서보 12개 ← 파워 전원
(PCA9685 V+ 입력)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)study/minho/work08\.md$|JetsonNano/servo_controller\.py$' || true

echo "== work08 relevant lines =="
if [ -f study/minho/work08.md ]; then
  nl -ba study/minho/work08.md | sed -n '35,110p'
fi

echo "== servo controller relevant references =="
if [ -f JetsonNano/servo_controller.py ]; then
  nl -ba JetsonNano/servo_controller.py | sed -n '1,180p'
  echo "== i2c / address references =="
  rg -n "0x4[01]|PCA|channel|PWM|VCC|V\\+|7\\.4|servo" JetsonNano/servo_controller.py study/minho/work08.md || true
fi

Repository: robertchoi/SpotMicroJetson

Length of output: 297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== work08 relevant lines =="
awk 'NR>=35 && NR<=110 { printf "%6d\t%s\n", NR, $0 }' study/minho/work08.md

echo "== servo controller outline / references =="
awk '{ printf "%6d\t%s\n", NR, $0 }' JetsonNano/servo_controller.py | sed -n '1,200p'
awk 'NR<=200 && /0x4[01]|PCA|channel|PWM|VCC|V\+|7\.4|servo/i { printf "%6d\t%s\n", NR, $0 }' JetsonNano/servo_controller.py study/minho/work08.md

Repository: robertchoi/SpotMicroJetson

Length of output: 15674


Show the servo-power path for both PCA9685 boards.

JetsonNano/servo_controller.py reads servo positions 6–11 from the 0x41 board. The new diagram shows only the four-pin logic chain from 0x40 to 0x41, with the servo power arrow above the whole block and no explicit link to 0x41 servo V+. Add both boards’ power paths, and label VCC as logic and V+ as servo supply. Otherwise, the rear servos can remain unpowered, or 7.4V can be applied to the logic rail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 45 - 46, Update the diagram around the
PCA9685 boards to show separate power paths from the 7.4V supply to each board’s
servo V+ output, including the 0x41 board used by servo positions 6–11. Label
the logic connection as VCC and the servo supply connection as V+, ensuring the
7.4V path is not shown feeding either board’s logic rail.

Comment thread study/minho/work08.md
Comment on lines +55 to +63
### 2.3 검증 결과

| 구성 | RPi 5 부팅 |
|------|------------|
| 12V 어댑터 → 300W Buck → XL4015 → RPi 5 (2단 종속) | ❌ 부팅 실패 |
| 12V 어댑터 → XL4015 → RPi 5 (1단) | ✅ 정상 부팅 |
| 12V 어댑터 → UBEC 5V/3A → RPi 5 (1단) | ✅ 정상 부팅 |

**결론**: 벅 컨버터를 종속(cascade)으로 연결하면 두 컨버터의 피드백 루프가 간섭하고 소프트 스타트 지연이 누적되어 RPi 5 PMIC 부팅 요구사항을 만족 못함. 1단 구조가 필수.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files matching study/minho/work08.md:"
git ls-files | grep -F 'study/minho/work08.md' || true

if [ -f study/minho/work08.md ]; then
  echo
  echo "Relevant lines:"
  nl -ba study/minho/work08.md | sed -n '45,75p'
  echo
  echo "Search for related conclusions/root-cause mentions:"
  rg -n "cascade|종속|소프트 스타트|soft start|peak current|input voltage|startup waveform|부팅 요구사항|결론" study/minho/work08.md -S || true
else
  echo "File not found; listing nearby files:"
  git ls-files | grep -F 'study/minho' || true
fi

Repository: robertchoi/SpotMicroJetson

Length of output: 290


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant lines:"
awk 'NR>=45 && NR<=75 { printf "%6d\t%s\n", NR, $0 }' study/minho/work08.md

echo
echo "Search for related conclusion/root-cause mentions:"
rg -n "cascade|종속|소프트 스타트|soft start|peak current|input voltage|startup waveform|부팅 요구사항|결론" study/minho/work08.md -S || true

echo
echo "Measurement/data mentions:"
rg -n "전압|current|waveform|파형|부하|load|측정|peak|maximum|max|정격" study/minho/work08.md -i -S || true

Repository: robertchoi/SpotMicroJetson

Length of output: 1963


Scope the power-converter conclusion to the tested chain.

The failure result comes from one cascaded configuration. Avoid stating that any buck converter chain fails and that 1-stage topology is always required unless supported by broader testing or measured startup/current waveforms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 55 - 63, Revise the conclusion in the “검증
결과” section to apply only to the tested 12V 어댑터 → 300W Buck → XL4015 → RPi 5
cascade, rather than all cascaded buck converters. Remove the universal claim
that one-stage topology is always required, and state only that this tested
two-stage configuration failed while the listed one-stage configurations booted
successfully.

Comment thread study/minho/work08.md
Comment on lines +102 to +105
**장점**:
- RPi 측 배선이 단순화 (분기 커넥터 불필요)
- I2C 버스는 원래 다중 슬레이브 지원이므로 전기적으로 문제 없음
- 두 보드가 물리적으로 인접 배치되면 배선 길이 최소화

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== context around target file =="
wc -l study/minho/work08.md
sed -n '80,120p' study/minho/work08.md

echo
echo "== search I2C / PCA9685 / wiring mentions =="
rg -n -i "I2C|i2c|PCA9685|pull|pull-up|pullup|capacitance|bus|length|배선|분기" study/minho/work08.md || true

echo
echo "== repo files around work08 =="
git ls-files study/minho/work08.md

Repository: robertchoi/SpotMicroJetson

Length of output: 3163


Qualify the “electrically problem-free” I2C claim.

Multi-slave support does not guarantee signal integrity for the assembled harness. Record the enabled pull-up configuration and validate the final cable length and bus speed before treating the wiring as electrically reliable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 102 - 105, “전기적으로 문제 없음” 주장을 완화하고, 최종 하네스
기준으로 활성화된 I2C 풀업 구성과 케이블 길이를 기록하세요. 사용하려는 버스 속도와 함께 신호 무결성을 검증한 뒤에만 해당 배선을 전기적으로
신뢰할 수 있다고 기술하세요.

Comment thread study/minho/work08.md
Comment on lines +133 to +140
```
0 1 2 3 4 5 6 7 8 9 a b c d e f
40: 40 41 -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- -- -- -- -- -- -- -- -- --
```
- `0x40`, `0x41` 두 PCA9685 모두 검출
- `0x70`, `0x71`은 각 PCA9685의 All-Call 주소 (자동 응답, 정상)
- `test_servos_cali.py`로 채널 0~11 개별 동작 확인 완료

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching servo_controller.py:"
fd -a 'servo_controller\.py$' . || true

echo
echo "Relevant sections in study/minho/work08.md:"
if [ -f study/minho/work08.md ]; then
  nl -ba study/minho/work08.md | sed -n '120,150p'
fi

echo
echo "Search PCA9685 address config:"
rg -n "All-Call|Sub-Call|Sub Call|0x70|0x71|70|71|sub[ -]?call|all_call|All Call|all-call|I2C 0" -S . --glob '!node_modules' --glob '!dist' --glob '!build' || true

Repository: robertchoi/SpotMicroJetson

Length of output: 336


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Reported file:"
cat -n home/jailuser/git/JetsonNano/servo_controller.py | sed -n '1,240p' 2>/dev/null || sed -n '1,240p' home/jailuser/git/JetsonNano/servo_controller.py 2>/dev/null || true

echo
echo "Study file relative path:"
cat -n study/minho/work08.md | sed -n '120,150p'

echo
echo "PCA9685 config text search:"
rg -n "All-Call|Sub-Call|Sub Call|0x70|0x71|70|71|sub[ -]?call|all_call|All Call|all-call|I2C 0" -S home/jailuser/git/JetsonNano study 2>/dev/null || true

Repository: robertchoi/SpotMicroJetson

Length of output: 2993


🌐 Web query:

PCA9685 default All-Call address 0x70 Sub Call 1 0x71 power-up disabled

💡 Result:

The PCA9685 I2C communication protocol includes an All-Call address and three Sub-Call addresses that allow for grouping devices [1][2]. These addresses have specific power-up states regarding their activation: 1. All-Call Address: The default power-up value for the LED All Call I2C-bus address (ALLCALLADR register) is 0xE0 (7-bit address 0x70) [1][3]. At power-up, this address is enabled by default [1][4]. Consequently, every PCA9685 on an I2C bus will acknowledge 0x70 immediately upon booting [1][3]. Because it is enabled by default, it is recommended not to use 0x70 as a regular device slave address to avoid bus conflicts [1][2]. 2. Sub-Call Addresses: There are three Sub-Call addresses (SUBADR1, SUBADR2, SUBADR3) with the following default power-up values [1][2]: - SUBADR1: 0xE2 (7-bit address 0x71) [1][2] - SUBADR2: 0xE4 (7-bit address 0x72) [1][2] - SUBADR3: 0xE8 (7-bit address 0x74) [1][2] Unlike the All-Call address, these Sub-Call addresses are disabled at power-up [1][2]. The PCA9685 will not acknowledge these addresses until they are enabled via software configuration [1][2]. Because they are disabled by default, they can safely be used as regular I2C slave addresses unless you explicitly enable them [1][2].

Citations:


🌐 Web query:

Adafruit PCA9685 Python CircuitPython enable_all_call enable_sub_call1 0x70 0x71

💡 Result:

The official Adafruit CircuitPython PCA9685 library does not include direct methods or parameters named enable_all_call or enable_sub_call1 [1][2][3]. The PCA9685 hardware chip itself supports "All Call" and "Sub Call" I2C addresses as defined in the NXP datasheet [4]. These features are managed by specific bits in the MODE1 register (specifically the ALLCALL and SUBADR bits) and associated configuration registers (ALLCALLADR and SUBADR1–3) [4]. However, the current standard Adafruit CircuitPython library abstracts these low-level features away and does not expose them in its API [1][3]. Regarding your mention of 0x70 and 0x71: - The default I2C address for the PCA9685 is 0x40 [3]. - Addresses 0x70 and 0x71 (or others in the 0x70 range) are often used for PCA9685 boards when the solder jumpers on the back are modified to change the device's I2C base address. - While the hardware supports using "All Call" or "Sub Call" to respond to multiple addresses, you cannot simply pass 0x70 or 0x71 as the address parameter in the PCA9685 constructor if those addresses have not been set on the hardware or configured via the relevant registers [1][3]. If you need to use these advanced addressing features, you would have to manually manipulate the PCA9685 registers (MODE1, SUBADR1, etc.) using the I2C bus object directly, bypassing the standard library's higher-level interface [3].

Citations:


Correct the scan result interpretation.

0x70 is the PCA9685 default shared All-Call address. 0x71 is the default Sub-Call 1 address and remains disabled until the driver enables it. The controller configures only 0x40 and 0x41, so report those as the validated device addresses and remove or reclassify 0x71 unless the subaddress is enabled.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 133-133: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work08.md` around lines 133 - 140, Update the scan-result
interpretation in the PCA9685 verification notes: identify 0x40 and 0x41 as the
validated configured device addresses, describe 0x70 as the shared All-Call
response, and remove or reclassify 0x71 unless the driver explicitly enables
Sub-Call 1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@study/minho/work09.md`:
- Around line 32-34: Update the logic-power description and the wiring details
around the PCA9685 connections to use one explicit path: UBEC 5V to RPi 5V, then
RPi 3.3V to PCA9685 VCC. Keep the 7.4V Buck supply connected only to the PCA9685
V+ terminals, and ensure the documented GND connections remain common.
- Around line 163-167: Update the documented default i2cdetect output in
work09.md so the PCA9685 appears at 0x70 only; remove the default 0x71 response
and state that 0x71 should be shown only when initialization explicitly enables
SUB1.
- Around line 20-30: Add the text language identifier to each fenced code block
in the document, including the diagrams and sample output near the visible block
and the additional blocks around the referenced sections, while preserving their
contents unchanged.
- Line 28: Update the power-rail design documented near the “300W Buck 7.4V”
entry so DS3230 is not connected directly to V+. Record the exact DS3230
manufacturer and variant, and regulate V+ to a voltage within the DS3230 4.8–6.8
V range and the ratings of every installed servo.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f3f5500e-275b-4a99-ab28-39fe2b828c26

📥 Commits

Reviewing files that changed from the base of the PR and between 8ec7f88 and ebaeca8.

📒 Files selected for processing (1)
  • study/minho/work09.md

Comment thread study/minho/work09.md
Comment on lines +20 to +30
```
RPi 5
├─ 3.3V ─┐
├─ GND ─┤
├─ SDA ─┤──→ PCA9685 #1 (0x40) ──→ PCA9685 #2 (0x41) [I2C 데이지 체인]
└─ SCL ─┘ │ │
├ CH0~CH5 ├ CH0~CH5 [서보 신호]
│ │
V+ ← 300W Buck 7.4V V+ ← 300W Buck 7.4V [서보 전원 공유]
GND ← 공통 GND GND ← 공통 GND
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to the fenced blocks.

markdownlint-cli2 reports MD040 at Lines 20, 144, 164, and 181. Add text to the diagrams and sample output.

Also applies to: 144-150, 164-167, 181-197

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work09.md` around lines 20 - 30, Add the text language identifier
to each fenced code block in the document, including the diagrams and sample
output near the visible block and the additional blocks around the referenced
sections, while preserving their contents unchanged.

Source: Linters/SAST tools

Comment thread study/minho/work09.md
└─ SCL ─┘ │ │
├ CH0~CH5 ├ CH0~CH5 [서보 신호]
│ │
V+ ← 300W Buck 7.4V V+ ← 300W Buck 7.4V [서보 전원 공유]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '20,36p;48,56p;126,136p' study/minho/work09.md
printf '\nReferences to DS3230 and 7.4 V:\n'
rg -n -C 2 'DS3230|7\.4 ?V|VCC|PCA9685|0x70|0x71' study/minho/work09.md

Repository: robertchoi/SpotMicroJetson

Length of output: 4838


🌐 Web query:

DS3230 servo exact voltage variants 4.8-6.8 V 4.8-7.4 V datasheet

💡 Result:

The DSServo DS3230 is widely documented with an operating voltage range of 4.8V to 6.8V [1][2][3][4][5][6]. There is no standard manufacturer datasheet that specifies a 4.8V-7.4V voltage variant for the base DS3230 model; technical specifications consistently list the upper limit as 6.8V [1][3][4][7][8][5][9][10][6]. While some users or hobbyist retailers may occasionally suggest using a 7.4V (2S LiPo) BEC to power the servo, the official technical documentation from the manufacturer maintains a maximum limit of 6.8V [2][3][6]. Operating the device above this specified 6.8V threshold may exceed its design parameters and could result in reduced lifespan or hardware failure [2][6]. If you encounter a product listing claiming 7.4V compatibility for a standard DS3230, it is likely an unofficial modification or an error in the retailer's technical description, as the industry-standard specification for this model remains 4.8-6.8V [1][3][8][5].

Citations:


🏁 Script executed:

python3 - <<'PY'
from pathlib import Path
p = Path("study/minho/work09.md")
lines = p.read_text(encoding="utf-8").splitlines()
for n in [28, 32, 53, 110, 131, 132, 163, 164, 165, 166, 167]:
    if 1 <= n <= len(lines):
        print(f"{n}: {lines[n-1]}")
PY

Repository: robertchoi/SpotMicroJetson

Length of output: 718


🌐 Web query:

DSservo DS3230 Pro 30kg servo operating voltage 4.8 7.4 datasheet

💡 Result:

The DSservo DS3230 Pro 30kg servo is specified with an operating voltage range of 4.8V to 6.8V DC [1][2][3]. While some retail listings may occasionally reference varying performance figures, official-style technical documentation consistently identifies this voltage range for the device [2][4]. Electrical performance characteristics within this voltage range are generally documented as follows: Operating Voltage Range: 4.8V - 6.8V DC [2][5][4] Stall Torque at 5.0V: Typically 28.5 kg-cm to 29.5 kg-cm [2][5][3][4] (Note: Some vendor data sheets may inconsistently list higher values up to 39.5 kg-cm [1][6][7]) Stall Torque at 6.8V: Typically 34.5 kg-cm to 38 kg-cm [2][5][3][4] Operating Speed at 5.0V: Typically 0.11 s/60° to 0.2 s/60° [2][8][3][4] Operating Speed at 6.8V: Typically 0.09 s/60° to 0.17 s/60° [2][8][3][4] Users should note that performance specifications for this model can vary slightly between different distributors and production batches [1][2][9]. For critical applications, it is recommended to rely on the specific documentation provided with the unit you have acquired. Ensure the power supply remains within the 4.8V–6.8V range to avoid potential damage to the servo's internal digital controller or motor [2][10][11].

Citations:


Do not power DS3230 from the 7.4 V rail.

DS3230 documentation specifies 4.8–6.8 V. Record the exact manufacturer and variant, then regulate V+ to a voltage within the ratings of every installed servo. A 7.4 V supply can damage the DS3230.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work09.md` at line 28, Update the power-rail design documented
near the “300W Buck 7.4V” entry so DS3230 is not connected directly to V+.
Record the exact DS3230 manufacturer and variant, and regulate V+ to a voltage
within the DS3230 4.8–6.8 V range and the ratings of every installed servo.

Source: MCP tools

Comment thread study/minho/work09.md
Comment on lines +32 to +34
- **로직 전원 (RPi, PCA9685 VCC)**: UBEC 5V → RPi 5V → PCA9685 VCC (3.3V 로직)
- **파워 전원 (V+)**: 300W Buck 7.4V → 두 PCA9685의 V+ 터미널
- **GND 공통**: 로직/파워 GND 모두 한 점에서 묶음

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use one explicit logic-power path.

Line 32 says UBEC 5V → RPi 5V → PCA9685 VCC, but Line 110 connects RPi Pin 1 (3.3V) to VCC. Rewrite the power description to show UBEC 5V → RPi 5V and RPi 3.3V → PCA9685 VCC, or document a separate regulated VCC source. Keep the 7.4 V supply on V+ only. VCC and V+ have different electrical roles. (learn.adafruit.com)

Also applies to: 106-113

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work09.md` around lines 32 - 34, Update the logic-power
description and the wiring details around the PCA9685 connections to use one
explicit path: UBEC 5V to RPi 5V, then RPi 3.3V to PCA9685 VCC. Keep the 7.4V
Buck supply connected only to the PCA9685 V+ terminals, and ensure the
documented GND connections remain common.

Source: MCP tools

Comment thread study/minho/work09.md
Comment on lines +163 to +167
정상 출력 (work08.md에서 확인됨):
```
40: 40 41 -- -- -- -- ...
70: 70 71 -- -- -- -- ... ← All-Call 주소 (자동 응답, 정상)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the default i2cdetect expectation.

The PCA9685 default ALLCALL address is 0x70. Subaddress responses such as 0x71 are disabled until explicitly enabled. Show 0x71 only when initialization enables SUB1; otherwise this diagnostic reports a false extra device. (nxp.com)

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 164-164: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@study/minho/work09.md` around lines 163 - 167, Update the documented default
i2cdetect output in work09.md so the PCA9685 appears at 0x70 only; remove the
default 0x71 response and state that 0x71 should be shown only when
initialization explicitly enables SUB1.

Source: MCP tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants