Compare commits

...

44 Commits

Author SHA1 Message Date
DevTekVE e28a7a5bee Refactor modeld for flexible input rates and output parsing.
Add support for both 20Hz and variable input rates in `modeld.py` by introducing conditional data updates based on a new `is_20hz` flag. Additionally, enhance `parse_outputs` in `parse_model_outputs.py` to handle `desired_curvature` parsing when relevant input keys are present.
2025-01-06 21:37:05 +01:00
DevTekVE 5e889d9ec6 Refactor DrivingModelFrame to remove template parameters
Transitioned DrivingModelFrame from a templated implementation to a single class structure, simplifying type usage and memory management across the codebase. Adjusted related buffer sizes and usage to accommodate these changes, improving maintainability and clarity. Legacy and float-specific implementations remain unchanged.
2025-01-06 20:02:03 +01:00
DevTekVE 5ebd6746d1 Testing diff approach 2025-01-06 18:46:33 +01:00
DevTekVE 9e7203567e Increase buffer size for image processing in DrivingModelFrameLegacy
Doubled the buffer size allocated for `img_buffer_cl` to accommodate larger frames and updated the copy operation accordingly. These changes ensure proper handling of image data and improve robustness in frame processing.
2025-01-06 18:46:33 +01:00
DevTekVE aba1f4b5fc kinda works 2025-01-06 18:46:33 +01:00
DevTekVE 5a3ee2d376 Clean 2025-01-06 18:46:33 +01:00
DevTekVE ae5a2430d5 Introduce DrivingModelFrameLegacy for float-based model handling
Added DrivingModelFrameLegacy class to handle float-based inputs, replacing the previous DrivingModelFrame_float. This includes the implementation of initialization, preparation, and teardown methods. Updated imports and references across the codebase to reflect the new class.
2025-01-06 18:46:33 +01:00
DevTekVE 6e56f37823 force 2025-01-06 18:46:33 +01:00
DevTekVE d77d0cf622 needs 2025-01-06 18:46:32 +01:00
DevTekVE 49888d2b7c Refactor model frame handling to support flexible data types
Updated `ModelFrame` and its derived classes to use templated types, enabling support for multiple data types like `float` and `uint8_t`. Introduced a separate OpenCL kernel for `float` processing and adjusted related methods across headers, Pyx, and C++ files to accommodate this refactor. This enables more versatile data representation for improved model operations.
2025-01-06 18:46:32 +01:00
DevTekVE efc1f6378e Update input handling logic in modeld.py
Refactored the assignment to `self.numpy_inputs` to ensure only specific keys from `inputs` are copied, excluding 'desire'. This improves accuracy and prevents unintended data overwrites in the model pipeline.
2025-01-06 18:46:32 +01:00
DevTekVE b0fef4b5ea Add lateral_control_params to model inputs if available
This change introduces an additional check for "lateral_control_params" in model inputs. If present, it adds the lateral control parameters to the input dictionary, including vehicle speed and steering delay. This ensures compatibility with models requiring these parameters.
2025-01-06 18:46:32 +01:00
DevTekVE e6b9497099 Refactor curvature handling in modeld input processing
Simplified and unified the handling of "desired_curvature" by removing redundant code and centralizing logic. Improved clarity and maintainability by determining the appropriate input key dynamically and implementing consistent array updates.
2025-01-06 18:46:32 +01:00
DevTekVE 18bc3c5920 Refactor model runner initialization and input handling.
Added a memory model detection mechanism in `TinygradRunner` and adjusted input preparation logic based on the detected model type. Fixed the `TinygradRunner` constructor to correctly pass `frames` for TICI hardware, ensuring proper model initialization.
2025-01-06 18:46:32 +01:00
DevTekVE cfffc94a07 Refine input handling logic for non-TICI devices.
This change adds an explicit check for non-TICI devices in the input processing logic, ensuring correctness and preventing unnecessary operations. The updated logic improves maintainability and clarity in device-specific handling.
2025-01-06 18:46:31 +01:00
DevTekVE f72447245c Refactor TinygradRunner to support non-TICI hardware
Adjust TinygradRunner to handle frame data on non-TICI hardware by leveraging device and dtype mappings. Simplify input preparation logic for better compatibility and ensure proper assertion for required frames. This refactor improves flexibility and maintains existing functionality for TICI.
2025-01-06 18:46:31 +01:00
DevTekVE c959947b8f [WIP] Adding support for NDv2 2025-01-06 18:46:31 +01:00
DevTekVE c9a23395ea Refactor model runner and input initialization logic.
Simplified and improved input handling for both `TinygradRunner` and `ONNXRunner`, introducing better mappings for data types and shapes. Updated buffer initialization, reshaping logic, and index calculations to enhance readability and maintainability. This ensures consistency across the model runner's input/output processing.
2025-01-06 18:46:31 +01:00
DevTekVE 50382d7c01 Refactor type annotations and return types in model_runner
Removed specific type hints and return annotations for `self.inputs` and `run_model` methods to enhance flexibility and maintain consistency. These changes streamline the code and improve compatibility with varying input/output types during model inference.
2025-01-06 18:46:31 +01:00
DevTekVE 68b240d09b Refactor type hints for improved code consistency.
Replaced specific type hints like `dict[str, np.ndarray]` with generic `dict` in several method signatures to simplify annotations. This improves overall readability and maintains functionality, while aligning with existing code practices.
2025-01-06 18:46:31 +01:00
DevTekVE 556c72cb90 Clean 2025-01-06 18:46:31 +01:00
DevTekVE c050fa7c49 Add type annotations to inputs and prepare_inputs method
This commit adds explicit type annotations for the `inputs` dictionary and the return value of the `prepare_inputs` method in `model_runner.py`. These changes improve code readability and ensure type consistency, enhancing maintainability and reducing potential errors.
2025-01-06 18:46:31 +01:00
DevTekVE 0784570ead Rename TinyGradRunner to TinygradRunner for consistency.
The class name and references were updated to maintain naming consistency across the codebase. This aligns with naming conventions and improves code clarity. No functional changes were introduced.
2025-01-06 18:46:30 +01:00
DevTekVE f67e8aca47 Refactor prepare_inputs to use explicit CLMem type.
This update replaces the generic `any` type with the more explicit `CLMem` type for better type safety and clarity. It ensures consistency across the `prepare_inputs` method implementations in derived classes, improving code readability and robustness.
2025-01-06 18:46:30 +01:00
DevTekVE 5d1b403015 Refactor model inference to use internal state for inputs
Simplified the `run_model` method by removing the requirement to pass inputs as arguments, and instead leveraging an internal `inputs` state. Adjusted `prepare_inputs` methods across model runners to populate this internal state. This refactor improves code clarity and reduces redundancy in managing input data.
2025-01-06 18:46:30 +01:00
DevTekVE c59dead9fd Fix model_runner output to ensure tensor conversion to NumPy.
Updated the `run_model` method to explicitly convert tensor outputs to NumPy arrays using `.numpy()`. This ensures compatibility with downstream processes relying on NumPy array inputs.
2025-01-06 18:46:30 +01:00
DevTekVE 8512406fa9 Remove unused imports and redundant pass statements.
This change cleans up the code by removing unused imports and redundant `pass` statements in abstract methods. It improves code readability and adheres to cleaner coding practices.
2025-01-06 18:46:30 +01:00
DevTekVE 7087f3f400 Remove redundant tensor assignment in model runner.
The `assign` operation was unnecessary as new tensors are realized for updated inputs. This simplifies the code and avoids redundant updates, improving clarity and maintainability.
2025-01-06 18:46:30 +01:00
DevTekVE b7e601e92d Refactor model runner initialization logic.
Removed the `create_model_runner` factory function and replaced it with direct initialization of `TinyGradRunner` or `ONNXRunner`. Simplified the `__init__` methods by standardizing paths as constants within `model_runner.py` for cleaner and more maintainable code.
2025-01-06 18:46:30 +01:00
DevTekVE c3f92f2783 Refactor formatting in model_runner.py for readability.
Consolidated multiline function declarations and calls into single lines where appropriate to improve code readability and maintainability. No changes were made to the functionality.
2025-01-06 18:46:29 +01:00
DevTekVE 5a3e80fc92 Refactoring model handling in modeld.py with ModelRunner abstraction
A significant refactoring of `modeld.py` was performed to enhance the handling of model logic. A new abstraction called `ModelRunner` has been introduced which encapsulates the model-running logic. This refactor simplifies the `modeld.py` script and provides easier management across different hardware configurations. Using this segregation, varying processing methods for models can be handled distinctly ensuring cleaner and more maintainable code. An instance of the appropriate model runner is now created during initialization based on whether a TICI hardware or a different type is used.
2025-01-06 18:46:29 +01:00
DevTekVE 5fac11efbb ui: prevent driving model change with offroad transition (#524)
* Update model manager logic and handle offroad transitions

Added is_onroad state tracking in SoftwarePanelSP to handle offroad transitions. Updated model manager conditions for improved bundle validation. Removed unnecessary clear operation for ModelManager_DownloadIndex during offroad transitions to optimize behavior.

* Using is_onroad softwarePanel

* Enable model label button only when conditions are met

Previously, the button's state update was misplaced, leading to potential issues with its interactive availability. The logic has been adjusted to ensure it is properly enabled or disabled based on onroad status and download progress. This change improves UX consistency and prevents unintended actions.

* Remove redundant setEnabled call for currentModelLblBtn

The setEnabled call was unnecessary as its functionality was not required in this context. Cleaning up this code improves readability and removes redundant operations. No changes to functionality or behavior were introduced.

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-06 18:46:29 +01:00
DevTekVE c12c22eac7 ui: onroad skeleton (#521)
* onroad init

* init model renderer

* Add default virtual destructors to HudRenderer and AnnotatedCameraWidget

This ensures proper cleanup of derived classes if they override these destructors. Adding default destructors promotes better memory safety and adheres to modern C++ best practices.

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-05 05:14:18 -05:00
DevTekVE 5b1bfc26db ui: add sunnylink settings and sidebar status (#503)
* Add Sunnylink integration for improved device communication

This commit introduces Sunnylink support, including modules for API interactions, device registration, logging, and uploader processes. Key changes involve adding Sunnylink-related components, such as sunnylinkd, manage_sunnylinkd, and associated utilities, along with seamless integration into process management.

* Refactor Sunnylink modules and update import paths

Standardize parameter handling in Sunnylink functions by initializing Params within functions as needed. Update imports to use fully-qualified paths for better clarity and consistency. Also, refactor logging messages for improved readability and maintainability.

* Add Sunnylink support and improve log handling

Introduced Sunnylink-specific functionality, including compression for oversized logs and platform-specific socket handling for macOS. Improved logging mechanisms, refactored log queue management, and fixed exception handling in sunnylinkd.

* Refactor and fix minor coding style inconsistencies

Remove unnecessary string concatenation, adjust spacing for better readability, and ensure cleaner code in `athenad.py` and `sunnylink.py`. Added a macOS-specific comment for TCP_KEEPALIVE configuration to improve code clarity.

* Replace platform system check with sys platform in athenad.py

To check for macOS platform, the code in athenad.py has been altered. Originally, the platform.system() function was used. However, the function has been replaced with sys.platform for a more consistent and preferable syntax. Particularly, this has been modified in the context of setting socket options.

* Apply suggestions from code review

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Simplify imports and reformat API function.

Removed unused `platform` import for cleanup in `athenad.py`. Improved readability of `api_get` in `__init__.py` by reformatting the long return statement into multiple lines.

* Adjust backoff logic and refactor API call formatting.

Introduce randomness to backoff calculation in Sunnylink API to reduce synchronization issues. Minor code refactoring improves readability in the API call logic.

* Refactor Sunnylink network check logic.

Removed hardware-based network check due to performance concerns and replaced it with a real-time device state monitoring loop. This improves efficiency and ensures accurate online status before proceeding with Sunnylink registration.

* Apply suggestions from code review

* `Refactor saveParams error handling and simplify logic`

Removed redundant try-except block wrapping the entire method for clarity. Moved error logging directly inside the loop to handle individual parameter exceptions more effectively. Simplified dictionary construction and improved error logging format.

* Add BACKUP flag to select persistent parameters

This commit introduces a new BACKUP flag and applies it to specific persistent parameters in `params.cc` and `params.h`. The BACKUP flag enhances data retention by designating parameters for inclusion in backups, ensuring crucial information is preserved across sessions.

* Simplify Sunnypilot params formatting

Removed unnecessary blank lines and adjusted the Sunnypilot comment format for better readability and consistency. No functional changes were made.

* SP: Move Sunnypilot-related code to sunnypilot/sunnylink (#504)

* Refactor and relocate sunnylink-related modules

sunnylink components have been reorganized for better modularity and clarity, with files moved under `sunnypilot/sunnylink`. Unused code was removed, and reusable utilities were separated for easier maintenance. Adjusted references across the project to reflect these changes.

* Permissions

* adding init py

* Add sunnylink toggle to developer panel and translations

This commit introduces a new toggle for enabling or disabling sunnylink in the developer panel. Corresponding translation entries have been added for all supported languages to ensure compatibility across the UI.

* Add SunnyLink integration and multi-language support updates

Enhanced SidebarSP with SunnyLink connection status and temperature display. Extended translations for multiple languages, including new strings. Updated build scripts and added utility functions for SunnyPilot-specific features.

* Need it this way as it's intentionally shortened. Sorry

* format

* only block drawing

* format

* format

* fix path

* cleanup translations

* sunnylink panel

* offroad transition

* remove stretch

* add panel to ui preview

* Translating to spanish

* just reorder params

* Refactor sidebar drawing method names and remove unused code.

Renamed `paintSidebar` to `drawSidebar` for better clarity across both `Sidebar` and `SidebarSP` classes. Removed unused utility functions `drawRoundedRect` and `interpColor` to streamline the codebase and improve maintainability.

* Updating translations

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-05 10:47:28 +01:00
DevTekVE 0b1d97fc72 sunnylink support (#499)
* Add Sunnylink integration for improved device communication

This commit introduces Sunnylink support, including modules for API interactions, device registration, logging, and uploader processes. Key changes involve adding Sunnylink-related components, such as sunnylinkd, manage_sunnylinkd, and associated utilities, along with seamless integration into process management.

* Refactor Sunnylink modules and update import paths

Standardize parameter handling in Sunnylink functions by initializing Params within functions as needed. Update imports to use fully-qualified paths for better clarity and consistency. Also, refactor logging messages for improved readability and maintainability.

* Add Sunnylink support and improve log handling

Introduced Sunnylink-specific functionality, including compression for oversized logs and platform-specific socket handling for macOS. Improved logging mechanisms, refactored log queue management, and fixed exception handling in sunnylinkd.

* Refactor and fix minor coding style inconsistencies

Remove unnecessary string concatenation, adjust spacing for better readability, and ensure cleaner code in `athenad.py` and `sunnylink.py`. Added a macOS-specific comment for TCP_KEEPALIVE configuration to improve code clarity.

* Replace platform system check with sys platform in athenad.py

To check for macOS platform, the code in athenad.py has been altered. Originally, the platform.system() function was used. However, the function has been replaced with sys.platform for a more consistent and preferable syntax. Particularly, this has been modified in the context of setting socket options.

* Apply suggestions from code review

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Simplify imports and reformat API function.

Removed unused `platform` import for cleanup in `athenad.py`. Improved readability of `api_get` in `__init__.py` by reformatting the long return statement into multiple lines.

* Adjust backoff logic and refactor API call formatting.

Introduce randomness to backoff calculation in Sunnylink API to reduce synchronization issues. Minor code refactoring improves readability in the API call logic.

* Refactor Sunnylink network check logic.

Removed hardware-based network check due to performance concerns and replaced it with a real-time device state monitoring loop. This improves efficiency and ensures accurate online status before proceeding with Sunnylink registration.

* Apply suggestions from code review

* `Refactor saveParams error handling and simplify logic`

Removed redundant try-except block wrapping the entire method for clarity. Moved error logging directly inside the loop to handle individual parameter exceptions more effectively. Simplified dictionary construction and improved error logging format.

* Add BACKUP flag to select persistent parameters

This commit introduces a new BACKUP flag and applies it to specific persistent parameters in `params.cc` and `params.h`. The BACKUP flag enhances data retention by designating parameters for inclusion in backups, ensuring crucial information is preserved across sessions.

* Simplify Sunnypilot params formatting

Removed unnecessary blank lines and adjusted the Sunnypilot comment format for better readability and consistency. No functional changes were made.

* SP: Move Sunnypilot-related code to sunnypilot/sunnylink (#504)

* Refactor and relocate sunnylink-related modules

sunnylink components have been reorganized for better modularity and clarity, with files moved under `sunnypilot/sunnylink`. Unused code was removed, and reusable utilities were separated for easier maintenance. Adjusted references across the project to reflect these changes.

* Permissions

* adding init py

* more

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-05 02:27:44 -05:00
Jason Wen 9c58fa1020 ui: remove stretch in sunnypilot panel (#520)
* no need to stretch for listwidget

* button moved
2025-01-05 01:59:30 -05:00
DevTekVE ee5c1c4507 Modular Assistive Driving System (MADS) (#446)
* allow re-regage

* bump opendbc

* bump panda

* apply pause/resume fix for hyundai (should do this in a separate PR)

* bump opendbc

* fix

* rename

* Fix?

* make sure to disengage for allow always cars

* fix

* combine

* more fix

* not needed

* check if engagement is from openpilot's state machine

* Rename

* fix panda safety

* fix

* no fake lfa button for @devtekve ;)

* fix non drive gear re-engage

* fix settings

* combine

* add replace method

* use replace

* remoev already checks if it exists

* fix

* group

* add todo

* reserve events

* cleaner

* hyundai: only allow for cars with lfa button

* sunnyParams

* make sure it's car only

* Move car-specific changes to opendbc

* no need

* bump opendbc

* more fixes

* no more available

* more!

* final?

* always emit user disable

* no longer needed

* move unit test

* add sunnypilot to unit tests

* bump opendbc

* use new cereal

* bump opendbc

* static analysis

* no unittest

* no need available

* UI border update

* show MADS updates

* Add TODO

* no longer needed

* fix changed events

* fix cluster enabled

* don't add pre enable if not long

* should use enabled

* enabled <-> active

* better format

* bump opendbc

* static analysis

* static analysis

* Rename test as collector was dying

* Show our overriding

* Revert "show MADS updates"

This reverts commit daf0ad62

Revert "fix changed events"

This reverts commit 31d8c97f

* ignoring reserved events

* adjusting creation delays

* back to stock

removing allow_cancel

* should be enabled

* revert

* silent lkas disable

* no need

* user disable tests

* just warning

* MUST REMOVE test process replay

* fix no entry

* fixme

* bump opendbc

* need this check

* cleanup

* allow entering paused state if no entry from disabled

* brake hold should apply to all

* in lists

* update unit test

* simpler

* unused

* same thing

* fix

* only mads in enabled state and long in disabled state

* unify silent enable

* do this for dlob

* bump submodules

* fix

* bump submodules

* bump opendbc

* less frequent

* more events

* fix

* allow no entry to paused for non-drive gears

* fix

* use cereal

* Revert "allow no entry to paused for non-drive gears"

This reverts commit 6d64a4dd9c.

* allow in all

* Revert "allow in all"

This reverts commit 6375f14891.

* should not be all!

* rename for clarity

* silent park brake

* flipped

* bump submodules

* Bump to latest mads-new panda

* bump panda

* more nissan

* bump panda

* bump msgq

* bump panda

* bump submodules

* bump opendbc

* bump opendbc

* improving the state

* Revert "PlayStation® model (#34133)"

This reverts commit 5160bee543.

* should be none

* bump panda

* bump opendbc

* Apply suggestions from code review

* bump panda

* bump ref panda

* add todo-sp

* bump panda ref

* bump more panda

* changing refs

* nuke nuke nuke

* use sunny's newer states

* bump with new panda

* bump panda

* Parse more flags from alt exp, more tests, hyundai main cruise allowed

* Parse more flags from alt exp, more tests, hyundai main cruise allowed

* missed

* mutation for controls allowed rising edge

* ford mutation

* license

* remove

* unused

* bump submodules

* use always allowed mads button alt exp

* fix

* whitelist jason's lastname to codespell

* test_processes: update ref logs to 82c0278

* bump submodules

* bump submodules

* bump submodules

* bump panda

* add controls mismatch lateral event

* Simplify lateral disengagement logic for MADS configuration

Reversed the conditional to align the logic with the `disengage_lateral_on_brake` parameter. This ensures that lateral disengagement behavior is more intuitive and matches the expected configuration. Improves code readability and reduces potential misconfigurations.

* remove unified engagement mode in panda

* controls allow should be allowed at all times

* squash! treat MADS button as user entry

* heartbeat for mads

* heartbeat mismatch exit control

* remove always allow mads button from alt

* move to safety_mads

* remove main cruise allowed from alt

* bump panda

* heartbeat engaged mads mismatch mutation test

* bump panda

* use mads the third panda

* ignore pre enable with mads

* only force exit if actually actuating

* use brake signal instead of pedal events when dlob is active

* fix tests

* fix panda tests

* bump panda

* new events to retain long blocks

* format

* uem: do not engage mads if long is engaged

* bump submodules

* fix not allowed engaged bug

* block uem from engaging

* flipped

* use different heartbeat check if dlob

* hard code to skip heartbeat check

* remove toyota lta status for lkas, causes weird behaviors

* block tesla

* bump panda

* bump to merged panda

* bump opendbc

* bump opendbc

* bump opendbc

* bump opendbc

* Apply suggestions from code review

* code ignore spells

* needs to be in carstate

* Bump opendbc

* Update MADS toggle descriptions for clarity.

Added notes to clarify behavior of the "MadsMainCruiseAllowed" setting, particularly its impact on vehicles without LFA/LKAS buttons. This ensures users are informed about potential implications when disabling this feature.

* Updating translations + Adding spanish

* Disengage Lateral on Brake -> Pause Lateral on Brake

* test_processes: update ref logs to dd41005

* Apply suggestions from code review

* fix mads button not allowed

* bump submodules

* bump submodule

* test_processes: update ref logs to 0a0b998

* has multiple lists

* Revert "has multiple lists"

This reverts commit a37c1d26fe.

* base

* Reapply "has multiple lists"

This reverts commit d1cd8dcc81.

* migrate mads toggles to sp panel

* this is why it keeps crashing

* house keeping

* more housekeeping

* more housekeeping

* don't show description by default (yet)

* reset to main panel when clicked away

* more

* some more with interactions

* don't stretch cause it looks weird with descriptions

* simpler to handle offroad transition

* some are toggleable while onroad

* remove unused event

* slight cleanup

* default to true for HKG main cruise toggle

* append to list after

* add Customize MADS to UI preview

* simpler

* move to sp list

* how tf was this removed

* update mads settings button on show event

* test_processes: update ref logs to efa9c32

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-04 20:48:02 -05:00
DevTekVE 993c4a0d2a Driving Model Manager (#457)
* Introduce Model Manager to handle downloads and verification

This commit introduces a new Model Manager responsible for handling model downloads, including driving and navigation application models. The manager also verifies file hashes and communicates download progress for an improved user experience.

The Model Manager is asynchronous and utilizes asyncio and aiohttp for enhanced performance, including robust error handling.

Impacted files in the 'cereal', 'common', 'sunnypilot', and 'system' directories have been updated accordingly. The 'ModelsFetcher' process configuration has been modified to run only when off-road, ensuring optimum resource management.

This update aims to enhance code clarity, improve performance, and streamline the handling of model downloads.

* "Update model management and fetching for SunnyPilot"

This update refactors the model management, downloading and cache verification mechanisms of SunnyPilot. New functionalities, such as smart cache handling, have been implemented in ModelFetcher in sunnypilot/models/model_fetcher.py. Also, the model downloading process has been moved to a separate async function _download_bundle in ModelManagerSP in sunnypilot/models/model_manager.py. Hash verification of files is now performed in an async function verify_file in sunnypilot/models/model_helper.py. Changes in system parameters related to model management have been reflected in system/manager/manager.py.

* Integrate download ETA calculations in model manager

This commit introduces a new feature that calculates and tracks the Estimated Time of Arrival (ETA) for downloading models in the model manager component. The 'eta' property in the 'DownloadProgress' structure in 'custom.capnp' is changed from 'Float32' to 'UInt32'.

In the 'model_manager.py' file, a new method '_calculate_eta' has been added to perform ETA calculations. An additional dictionary '_download_start_times' has been created to keep track of the start time of each model download. The ETA is calculated every time a portion of the model file is downloaded, and it gets updated in the 'DownloadProgress' structure. Finally, the start time is cleared after the download completes.

In 'model_manager_audit.py', an additional check is added to only print downloadProgress for the downloads currently in progress.

* format

* no default model cache {} because it can be considered a valid json, we do not want that

* Refactor typing annotations to use PEP 604 syntax.

Updated type hints to adopt PEP 604 union syntax (`X | None`) and replaced `List` and `Dict` with modern built-in `list` and `dict`. This change improves consistency and readability while aligning with Python 3.10+ standards.

* Simplify logging messages and remove unused imports.

Removed an unused import from `model_manager.py` to improve clarity and maintainability. Also refined a log message in `model_fetcher.py` by removing unnecessary formatting for consistency.

* Refactor model handling and simplify cache fallback logic.

Updated type annotations for `selected_bundle` in `model_manager.py` for clarity. Streamlined cache fallback logic in `model_fetcher.py` by removing redundant conditionals while preserving functionality. These changes improve code readability and maintainability.

* "Fix formatting for ModelManager_DownloadIndex retrieval

Condensed parameter alignment in the get method for improved readability and adherence to style guidelines. This change does not affect functionality but ensures consistent code formatting."

* Need to have main defined for process_config to be able to run it

* Refactor model management to support active bundle tracking

Introduce the concept of an active model bundle with a new persistent parameter and API updates. Added fields for `generation` and `environment` in model metadata, improved caching, and updated methods to manage active model states efficiently.

* UI commit (#515)

* Refactor model management to support active bundle tracking

Introduce the concept of an active model bundle with a new persistent parameter and API updates. Added fields for `generation` and `environment` in model metadata, improved caching, and updated methods to manage active model states efficiently.

* Add new driving model selection feature to settings

This commit introduces a new feature to the settings that allows users to select different driving models. It fetches available models and displays their download progress. The created UI also suggests a calibration reset after model download. The changes include the creation of 'SoftwarePanelSP' within 'settings.' Additionally, 'sunnypilot/SConscript' has been updated to include 'settings.cc' and 'software_panel.cc'. Changes also include localization for this feature.

* Show model description during download status

This update ensures the model description is displayed when a model is in the downloading state. It improves the user interface by providing real-time feedback during the download process.

* Update translations for multiple languages

Added new and updated translation strings in several language files, including Spanish, Arabic, Chinese (Simplified and Traditional), Turkish, Korean, Thai, Japanese, and Brazilian Portuguese. These updates include placeholder translations for new UI elements and features.

* Refactor model name handling and add generation check.

Replaced `bundleName` with `model_name` for better clarity in status messages. Added a generation mismatch check before showing the reset parameters dialog to avoid unnecessary prompts.

* Update model handling in SoftwarePanelSP

Remove unused "common/model.h" and replace "CURRENT_MODEL" with "..." as the default return value in GetModelName. Adjust logic to check for active bundles instead of selected bundles for improved accuracy. Minor text change for clarity in UI label.

* Rename `GetModelName` to `GetActiveModelName` for clarity.

The new name better reflects the function's purpose of retrieving the active model name, improving code readability. All relevant calls and references have been updated to ensure consistency across the codebase.

* Update sunnypilot/models/model_helper.py

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Refactor download status handling and add 'cached' state

Introduce 'cached' as a new download status and adjust relevant logic across components to support it. Simplify and streamline model status handling in the software panel for better readability and maintainability. Ensure consistent status reporting for all model types.

* Update translations for multiple languages

Refined and expanded translations across various languages, replacing placeholders with meaningful text. This improves clarity and user experience in the multilingual interface.

* Update terminology from 'bundle' to 'model' in UI texts

Replaced occurrences of 'bundle' with 'model' in button labels, dialog titles, and messages in the SoftwarePanelSP code. This improves clarity and aligns terminology with current functionality.

* Update translation placeholders for model fetching texts

Replaced "Fetching bundles" with "Fetching models" across multiple languages to align text placeholders with the updated functionality. Adjusted related background download messages for clarity and consistency.

* cleanup

* not used, and likely not needed

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* cleaning up

* Update system/manager/process_config.py

* Simplify model parsing and index handling logic.

Refactored `ModelManager_DownloadIndex` retrieval to use the walrus operator, streamlining the conditional logic. Additionally, restructured model list initialization in `_parse_bundle` for improved readability and maintainability. These changes enhance code clarity and reduce redundancy.

* `Improve error handling in model cache retrieval`

Revised the `get` method to ensure it returns an empty dictionary on errors or missing data, avoiding potential `None`-related issues. Added logging for clearer diagnostics when cached model data is unavailable or retrieval fails. This improves reliability and debuggability of the model fetching process.

* Fix cached model data handling by parsing JSON response

Previously, cached model data was returned as a raw string, causing potential issues when using the data. The change ensures the cached data is properly parsed into JSON format before returning, improving reliability and consistency.

* Adjust modelManagerSP rate and Ratekeeper frequency

Reduced the rate for modelManagerSP in services and aligned the Ratekeeper frequency in model_manager.py to 0.1.

* Update model fetcher URL and adjust modelManagerSP rate

Updated the model fetcher URL to point to the correct resource for driving models. Adjusted the rate of modelManagerSP in both its service definition and the corresponding Ratekeeper initialization to 1 Hz for improved consistency.

* Refactor model download logic for clarity and efficiency

Simplify the logic for finding the model to download by combining redundant constructs into a single line. This improves code readability and reduces unnecessary variable assignments.

* Fix cache keys for manual prebuilt actions because they were missing the cache when manually built

* no need to log

* formatting

* revert ci changes

* Refactor and restructure `modeld` to `models` module.

Renamed `modeld` directory to `models` for clarity and consistency. Updated all references and imports to reflect the new structure. This improves maintainability and aligns with naming conventions.

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-04 15:05:21 -05:00
DevTekVE 63f0237e7a prebuilt: Squash and merge script (#497)
* Add script for squash and merge workflow automation

Introduced `squash_and_merge.py` to automate the process of squashing a source branch and merging it into a target branch. The script handles backups, commit message creation, stashing, cleanup, and includes optional push functionality. Provides user prompts and error handling to simplify complex merge workflows.

* temp1

* Switch to PEP 604 style for optional and generic type hints

Replaces `Optional` and `List` with the simpler `X | None` and `list[X]` syntax introduced in Python 3.10. Updated all function signatures accordingly for consistency and modernity. Also made minor string formatting consistency adjustments.

* Fix type annotation for temp_branch variable

Updated the temp_branch variable to include a proper type annotation (`str | None`). This change improves code clarity and aligns with Python typing standards.

* more hints
2025-01-04 09:43:03 +01:00
Jason Wen a3ab6db7eb ui: sunnypilot panel in settings (#513)
* ui: sunnypilot panel in settings

* add panel to comment

* test populate screenshot

* Revert "test populate screenshot"

This reverts commit 426b6c26c5.
2025-01-03 17:18:38 -05:00
Jason Wen 3a64efe52f Bump submodules 2025-01-03 15:07:10 -05:00
Jason Wen 3966599e9d ui: sunnypilot offroad UI (#512)
* Revert "Revert "ui: sunnypilot offroad UI" (#511)"

This reverts commit 0e264e1b05.

* Revert "move files to sp dir"

This reverts commit c72d732259.

* remove drive stats for now

* update translation

* update onboarding

* remove sp onboarding for now

* rearrange

* remove more

* shorter license
2025-01-03 15:04:09 -05:00
Jason Wen 0e264e1b05 Revert "ui: sunnypilot offroad UI" (#511)
Revert "ui: sunnypilot offroad UI (#500)"

This reverts commit 3717a111af.
2025-01-03 11:02:19 -05:00
Jason Wen 3717a111af ui: sunnypilot offroad UI (#500)
* add sp flag

* return if sp macro

* controls

* drive stats

* some more

* more

* skip the whole thing

* missed

* more

* more main

* not ready to push yet but sure

* get them icons

* later

* own icons

* Revert "own icons"

This reverts commit e07bd8e670.

* blank icon

* render differently on mac

* static

* set toggle and title space properly

* format

* remove prior objects

* good spacing for ButtonParamControlSP

* more space formatting

* on device fix

* update home wifi prompt

* handle AbstractControlSP_SELECTOR hide events properly

* use sp

* ignore name

* more

* keep spacing

* better flag

* settings button moved

* small cleanup

* move files to sp dir

* remove for now

* developer different icon

* add scrolling to ui test

* Revert "add scrolling to ui test"

This reverts commit c8d1c65d89.

* format

* make them virtual

* do we still need this?

* Apply suggestions from code review

Co-authored-by: DevTekVE <devtekve@gmail.com>

* revert for now

* shorter license

---------

Co-authored-by: DevTekVE <devtekve@gmail.com>
2025-01-03 09:36:36 -05:00
232 changed files with 8194 additions and 336 deletions
+2
View File
@@ -0,0 +1,2 @@
Wen
REGIST
+1 -1
View File
@@ -86,7 +86,7 @@ jobs:
run: >-
sudo apt-get install -y imagemagick
scenes="homescreen settings_device settings_software settings_toggles settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard"
scenes="homescreen settings_device settings_software settings_sunnylink settings_toggles settings_sunnypilot settings_sunnypilot_mads settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard"
A=($scenes)
DIFF=""
+10
View File
@@ -74,6 +74,12 @@ AddOption('--minimal',
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
help='the minimum build to run openpilot. no tests, tools, etc.')
AddOption('--stock-ui',
action='store_true',
dest='stock_ui',
default=False,
help='Build stock openpilot UI instead of sunnypilot UI')
## Architecture name breakdown (arch)
## - larch64: linux tici aarch64
## - aarch64: linux pc aarch64
@@ -172,6 +178,10 @@ else:
if arch != "Darwin":
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
if not GetOption('stock_ui'):
cflags += ["-DSUNNYPILOT"]
cxxflags += ["-DSUNNYPILOT"]
ccflags_option = GetOption('ccflags')
if ccflags_option:
ccflags += ccflags_option.split(' ')
+64 -2
View File
@@ -8,10 +8,72 @@ $Cxx.namespace("cereal");
# cereal, so use these if you want custom events in your fork.
# you can rename the struct, but don't change the identifier
struct CustomReserved0 @0x81c2f05a394cf4af {
struct SelfdriveStateSP @0x81c2f05a394cf4af {
mads @0 :ModularAssistiveDrivingSystem;
struct ModularAssistiveDrivingSystem {
state @0 :ModularAssistiveDrivingSystemState;
enabled @1 :Bool;
active @2 :Bool;
available @3 :Bool;
enum ModularAssistiveDrivingSystemState {
disabled @0;
paused @1;
enabled @2;
softDisabling @3;
overriding @4;
}
}
}
struct CustomReserved1 @0xaedffd8f31e7b55d {
struct ModelManagerSP @0xaedffd8f31e7b55d {
activeBundle @0 :ModelBundle;
selectedBundle @1 :ModelBundle;
availableBundles @2 :List(ModelBundle);
struct DownloadUri {
uri @0 :Text;
sha256 @1 :Text;
}
enum Type {
drive @0;
navigation @1;
metadata @2;
}
struct Model {
fullName @0 :Text;
fileName @1 :Text;
downloadUri @2 :DownloadUri;
downloadProgress @3 :DownloadProgress;
type @4 :Type;
}
enum DownloadStatus {
notDownloading @0;
downloading @1;
downloaded @2;
cached @3;
failed @4;
}
struct DownloadProgress {
status @0 :DownloadStatus;
progress @1 :Float32;
eta @2 :UInt32;
}
struct ModelBundle {
index @0 :UInt32;
internalName @1 :Text;
displayName @2 :Text;
models @3 :List(Model);
status @4 :DownloadStatus;
generation @5 :UInt32;
environment @6 :Text;
}
}
struct CustomReserved2 @0xf35cc4560bbf6ec2 {
+76 -3
View File
@@ -125,6 +125,79 @@ struct OnroadEvent @0xc4fa6047f024e718 {
espActive @90;
personalityChanged @91;
aeb @92;
eventReserved93 @93;
eventReserved94 @94;
eventReserved95 @95;
eventReserved96 @96;
eventReserved97 @97;
eventReserved98 @98;
eventReserved99 @99;
eventReserved100 @100;
eventReserved101 @101;
eventReserved102 @102;
eventReserved103 @103;
eventReserved104 @104;
eventReserved105 @105;
eventReserved106 @106;
eventReserved107 @107;
eventReserved108 @108;
eventReserved109 @109;
eventReserved110 @110;
eventReserved111 @111;
eventReserved112 @112;
eventReserved113 @113;
eventReserved114 @114;
eventReserved115 @115;
eventReserved116 @116;
eventReserved117 @117;
eventReserved118 @118;
eventReserved119 @119;
eventReserved120 @120;
eventReserved121 @121;
eventReserved122 @122;
eventReserved123 @123;
eventReserved124 @124;
eventReserved125 @125;
eventReserved126 @126;
eventReserved127 @127;
eventReserved128 @128;
eventReserved129 @129;
eventReserved130 @130;
eventReserved131 @131;
eventReserved132 @132;
eventReserved133 @133;
eventReserved134 @134;
eventReserved135 @135;
eventReserved136 @136;
eventReserved137 @137;
eventReserved138 @138;
eventReserved139 @139;
eventReserved140 @140;
eventReserved141 @141;
eventReserved142 @142;
eventReserved143 @143;
eventReserved144 @144;
eventReserved145 @145;
eventReserved146 @146;
eventReserved147 @147;
eventReserved148 @148;
eventReserved149 @149;
eventReserved150 @150;
# sunnypilot
lkasEnable @151;
lkasDisable @152;
manualSteeringRequired @153;
manualLongitudinalRequired @154;
silentLkasEnable @155;
silentLkasDisable @156;
silentBrakeHold @157;
silentWrongGear @158;
silentReverseGear @159;
silentDoorOpen @160;
silentSeatbeltNotLatched @161;
silentParkBrake @162;
controlsMismatchLateral @163;
soundsUnavailableDEPRECATED @47;
}
@@ -589,6 +662,7 @@ struct PandaState @0xa7649e2575e4591e {
# safety stuff
controlsAllowed @3 :Bool;
controlsAllowedLat @5 :Bool;
safetyRxInvalid @19 :UInt32;
safetyTxBlocked @24 :UInt32;
safetyModel @14 :Car.CarParams.SafetyModel;
@@ -696,7 +770,6 @@ struct PandaState @0xa7649e2575e4591e {
}
gasInterceptorDetectedDEPRECATED @4 :Bool;
startedSignalDetectedDEPRECATED @5 :Bool;
hasGpsDEPRECATED @6 :Bool;
gmlanSendErrsDEPRECATED @9 :UInt32;
fanSpeedRpmDEPRECATED @11 :UInt16;
@@ -2558,8 +2631,8 @@ struct Event {
customReservedRawData2 @126 :Data;
# *********** Custom: reserved for forks ***********
customReserved0 @107 :Custom.CustomReserved0;
customReserved1 @108 :Custom.CustomReserved1;
selfdriveStateSP @107 :Custom.SelfdriveStateSP;
modelManagerSP @108 :Custom.ModelManagerSP;
customReserved2 @109 :Custom.CustomReserved2;
customReserved3 @110 :Custom.CustomReserved3;
customReserved4 @111 :Custom.CustomReserved4;
+4
View File
@@ -74,6 +74,10 @@ _services: dict[str, tuple] = {
"userFlag": (True, 0., 1),
"microphone": (True, 10., 10),
# sunnypilot
"modelManagerSP": (False, 1., 1),
"selfdriveStateSP": (True, 100., 10),
# debug
"uiDebug": (True, 0., 1),
"testJoystick": (True, 0.),
+16 -35
View File
@@ -1,46 +1,27 @@
import jwt
import os
import requests
from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
from openpilot.common.api.comma_connect import CommaConnectApi
from sunnypilot.sunnylink.api import SunnylinkApi
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
class Api:
def __init__(self, dongle_id):
self.dongle_id = dongle_id
with open(Paths.persist_root()+'/comma/id_rsa') as f:
self.private_key = f.read()
def __init__(self, dongle_id, use_sunnylink=False):
if use_sunnylink:
self.service = SunnylinkApi(dongle_id)
else:
self.service = CommaConnectApi(dongle_id)
def request(self, method, endpoint, **params):
return self.service.request(method, endpoint, **params)
def get(self, *args, **kwargs):
return self.request('GET', *args, **kwargs)
return self.service.get(*args, **kwargs)
def post(self, *args, **kwargs):
return self.request('POST', *args, **kwargs)
def request(self, method, endpoint, timeout=None, access_token=None, **params):
return api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
return self.service.post(*args, **kwargs)
def get_token(self, expiry_hours=1):
now = datetime.now(UTC).replace(tzinfo=None)
payload = {
'identity': self.dongle_id,
'nbf': now,
'iat': now,
'exp': now + timedelta(hours=expiry_hours)
}
token = jwt.encode(payload, self.private_key, algorithm='RS256')
if isinstance(token, bytes):
token = token.decode('utf8')
return token
return self.service.get_token(expiry_hours)
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
headers = {}
if access_token is not None:
headers['Authorization'] = "JWT " + access_token
headers['User-Agent'] = "openpilot-" + get_version()
return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params)
def api_get(endpoint, method='GET', timeout=None, access_token=None, use_sunnylink=False, **params):
return SunnylinkApi(None).api_get(endpoint, method, timeout, access_token, **params) if use_sunnylink \
else CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
+56
View File
@@ -0,0 +1,56 @@
import jwt
import requests
import unicodedata
from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
class BaseApi:
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
self.dongle_id = dongle_id
self.api_host = api_host
self.user_agent = user_agent
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
self.private_key = f.read()
def get(self, *args, **kwargs):
return self.request('GET', *args, **kwargs)
def post(self, *args, **kwargs):
return self.request('POST', *args, **kwargs)
def request(self, method, endpoint, timeout=None, access_token=None, **params):
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
def _get_token(self, expiry_hours=1, **extra_payload):
now = datetime.now(UTC).replace(tzinfo=None)
payload = {
'identity': self.dongle_id,
'nbf': now,
'iat': now,
'exp': now + timedelta(hours=expiry_hours),
**extra_payload
}
token = jwt.encode(payload, self.private_key, algorithm='RS256')
if isinstance(token, bytes):
token = token.decode('utf8')
return token
def get_token(self, expiry_hours=1):
return self._get_token(expiry_hours)
def remove_non_ascii_chars(self, text):
normalized_text = unicodedata.normalize('NFD', text)
ascii_encoded_text = normalized_text.encode('ascii', 'ignore')
return ascii_encoded_text.decode()
def api_get(self, endpoint, method='GET', timeout=None, access_token=None, **params):
headers = {}
if access_token is not None:
headers['Authorization'] = "JWT " + access_token
version = self.remove_non_ascii_chars(get_version())
headers['User-Agent'] = self.user_agent + version
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, params=params)
+11
View File
@@ -0,0 +1,11 @@
import os
from openpilot.common.api.base import BaseApi
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
class CommaConnectApi(BaseApi):
def __init__(self, dongle_id):
super().__init__(dongle_id, API_HOST)
self.user_agent = "openpilot-"
+41 -20
View File
@@ -109,36 +109,36 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CurrentBootlog", PERSISTENT},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"DisablePowerDown", PERSISTENT},
{"DisableUpdates", PERSISTENT},
{"DisengageOnAccelerator", PERSISTENT},
{"DisablePowerDown", PERSISTENT | BACKUP},
{"DisableUpdates", PERSISTENT | BACKUP},
{"DisengageOnAccelerator", PERSISTENT | BACKUP},
{"DongleId", PERSISTENT},
{"DoReboot", CLEAR_ON_MANAGER_START},
{"DoShutdown", CLEAR_ON_MANAGER_START},
{"DoUninstall", CLEAR_ON_MANAGER_START},
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY},
{"ExperimentalMode", PERSISTENT},
{"ExperimentalModeConfirmed", PERSISTENT},
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY | BACKUP},
{"ExperimentalMode", PERSISTENT | BACKUP},
{"ExperimentalModeConfirmed", PERSISTENT | BACKUP},
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"ForcePowerDown", PERSISTENT},
{"GitBranch", PERSISTENT},
{"GitCommit", PERSISTENT},
{"GitCommitDate", PERSISTENT},
{"GitDiff", PERSISTENT},
{"GithubSshKeys", PERSISTENT},
{"GithubUsername", PERSISTENT},
{"GithubSshKeys", PERSISTENT | BACKUP},
{"GithubUsername", PERSISTENT | BACKUP},
{"GitRemote", PERSISTENT},
{"GsmApn", PERSISTENT},
{"GsmMetered", PERSISTENT},
{"GsmRoaming", PERSISTENT},
{"GsmApn", PERSISTENT | BACKUP},
{"GsmMetered", PERSISTENT | BACKUP},
{"GsmRoaming", PERSISTENT | BACKUP},
{"HardwareSerial", PERSISTENT},
{"HasAcceptedTerms", PERSISTENT},
{"IMEI", PERSISTENT},
{"InstallDate", PERSISTENT},
{"IsDriverViewEnabled", CLEAR_ON_MANAGER_START},
{"IsEngaged", PERSISTENT},
{"IsLdwEnabled", PERSISTENT},
{"IsMetric", PERSISTENT},
{"IsLdwEnabled", PERSISTENT | BACKUP},
{"IsMetric", PERSISTENT | BACKUP},
{"IsOffroad", CLEAR_ON_MANAGER_START},
{"IsOnroad", PERSISTENT},
{"IsRhdDetected", PERSISTENT},
@@ -146,7 +146,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"LanguageSetting", PERSISTENT},
{"LanguageSetting", PERSISTENT | BACKUP},
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
{"LastGPSPosition", PERSISTENT},
{"LastManagerExitReason", CLEAR_ON_MANAGER_START},
@@ -158,7 +158,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
{"LocationFilterInitialState", PERSISTENT},
{"LongitudinalManeuverMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"LongitudinalPersonality", PERSISTENT},
{"LongitudinalPersonality", PERSISTENT | BACKUP},
{"NetworkMetered", PERSISTENT},
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
@@ -174,17 +174,17 @@ std::unordered_map<std::string, uint32_t> keys = {
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
{"OpenpilotEnabledToggle", PERSISTENT},
{"OpenpilotEnabledToggle", PERSISTENT | BACKUP},
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"PandaSignatures", CLEAR_ON_MANAGER_START},
{"PrimeType", PERSISTENT},
{"RecordFront", PERSISTENT},
{"RecordFront", PERSISTENT | BACKUP},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"SecOCKey", PERSISTENT | DONT_LOG},
{"SecOCKey", PERSISTENT | DONT_LOG}, // Candidate for | BACKUP
{"RouteCount", PERSISTENT},
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"SshEnabled", PERSISTENT},
{"SshEnabled", PERSISTENT | BACKUP},
{"TermsVersion", PERSISTENT},
{"TrainingVersion", PERSISTENT},
{"UbloxAvailable", PERSISTENT},
@@ -200,7 +200,28 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
{"UpdaterLastFetchTime", PERSISTENT},
{"Version", PERSISTENT},
{"EnableGithubRunner", PERSISTENT},
// --- sunnypilot params --- //
{"EnableGithubRunner", PERSISTENT | BACKUP},
// MADS params
{"Mads", PERSISTENT | BACKUP},
{"MadsMainCruiseAllowed", PERSISTENT | BACKUP},
{"MadsPauseLateralOnBrake", PERSISTENT | BACKUP},
{"MadsUnifiedEngagementMode", PERSISTENT | BACKUP},
// Model Manager params
{"ModelManager_ActiveBundle", PERSISTENT},
{"ModelManager_DownloadIndex", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"ModelManager_LastSyncTime", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"ModelManager_ModelsCache", PERSISTENT | BACKUP},
// sunnylink params
{"EnableSunnylinkUploader", PERSISTENT | BACKUP},
{"LastSunnylinkPingTime", CLEAR_ON_MANAGER_START},
{"SunnylinkDongleId", PERSISTENT},
{"SunnylinkdPid", PERSISTENT},
{"SunnylinkEnabled", PERSISTENT},
};
} // namespace
+1
View File
@@ -16,6 +16,7 @@ enum ParamKeyType {
CLEAR_ON_OFFROAD_TRANSITION = 0x10,
DONT_LOG = 0x20,
DEVELOPMENT_ONLY = 0x40,
BACKUP = 0x80,
ALL = 0xFFFFFFFF
};
+1
View File
@@ -163,6 +163,7 @@ testpaths = [
"tools/replay",
"tools/cabana",
"cereal/messaging/tests",
"sunnypilot",
]
[tool.codespell]
+361
View File
@@ -0,0 +1,361 @@
#!/usr/bin/env python3
import argparse
import subprocess
import sys
import shutil
import signal
import contextlib
import tempfile
import os
def run_command(command: str) -> tuple[int, str, str]:
"""Run a shell command and return exit code, stdout, and stderr."""
process = subprocess.Popen(
command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate()
return process.returncode, stdout.strip(), stderr.strip()
def is_gh_available() -> bool:
"""Check if GitHub CLI is available."""
return shutil.which('gh') is not None
def get_current_branch() -> str | None:
"""Get the name of the current git branch."""
code, output, error = run_command("git rev-parse --abbrev-ref HEAD")
if code != 0:
print(f"Error getting current branch: {error}")
return None
return output
def backup_branch(branch_name: str) -> bool:
"""Create a backup of the current branch."""
backup_name = f"{branch_name}-backup-$(date +%Y%m%d_%H%M%S)"
code, _, error = run_command(f"git branch {backup_name}")
if code != 0:
print(f"Error creating backup branch: {error}")
return False
print(f"Created backup branch: {backup_name}")
return True
def get_commit_messages(source_branch: str, target_branch: str) -> list[str] | None:
"""Get all commit messages between source and target branches."""
code, output, error = run_command(f"git log {target_branch}..{source_branch} --format=%B")
if code != 0:
print(f"Error getting commit messages: {error}")
return None
return [msg.strip() for msg in output.splitlines() if msg and not msg.startswith('Merge')]
def get_pr_info(branch_name: str) -> str | None:
"""Get PR title using GitHub CLI."""
if not is_gh_available():
print("Warning: GitHub CLI not found. Install it to auto-fetch PR titles:")
print(" https://cli.github.com/")
return None
# Try to get PR info using gh cli
code, output, error = run_command(f"gh pr view --json title --jq .title {branch_name}")
if code != 0:
print(f"No open PR found for branch '{branch_name}'")
return None
return output
def create_squash_message(pr_title: str | None, commit_messages: list[str], source_branch: str) -> str:
"""Create a squash commit message from PR title and commit messages."""
parts = []
# Add PR title if provided
if pr_title:
parts.append(pr_title)
else:
parts.append(f"Squashed changes from {source_branch}")
parts.append("") # Empty line after title
# Add original commits section
if commit_messages:
parts.append("Original commits:")
parts.append("") # Empty line before list
parts.extend(f"* {msg}" for msg in commit_messages)
return '\n'.join(parts)
def prompt_for_title() -> str:
"""Prompt user for a commit title."""
return input("Enter commit title (or press Enter to use default): ").strip()
@contextlib.contextmanager
def workspace_manager(original_branch: str):
"""Context manager to handle workspace state and cleanup."""
stash_created = False
stash_restored = False
temp_branch: str | None = None
def cleanup_handler(signum=None, frame=None):
"""Clean up workspace state."""
nonlocal temp_branch, stash_created, stash_restored
try:
if signum and stash_restored:
# If we're handling Ctrl+C but stash was already restored,
# just clean up branches and exit
current = get_current_branch()
if current and current != original_branch:
run_command(f"git checkout {original_branch}")
if temp_branch:
run_command(f"git branch -D {temp_branch}")
print("\nOperation interrupted, but changes were already restored.")
sys.exit(1)
# First, switch back to original branch
current = get_current_branch()
if current and current != original_branch:
run_command(f"git checkout {original_branch}")
# Then clean up temp branch
if temp_branch:
run_command(f"git branch -D {temp_branch}")
# Finally, restore stash if needed - AFTER switching branches
if stash_created and not stash_restored:
print("Restoring your uncommitted changes...")
code, stash_list, _ = run_command("git stash list")
if code == 0 and "Automatic stash by squash script" in stash_list:
run_command("git stash pop")
stash_restored = True
stash_created = False
if signum:
print("\nOperation interrupted. Cleaned up and restored original state.")
sys.exit(1)
except Exception as e:
print(f"Error during cleanup: {e}")
if signum:
sys.exit(1)
try:
# Set up signal handlers
signal.signal(signal.SIGINT, cleanup_handler)
signal.signal(signal.SIGTERM, cleanup_handler)
# Check for changes (including untracked files)
code, output, _ = run_command("git status --porcelain")
if output:
print("Stashing uncommitted changes...")
run_command("git stash push -u -m 'Automatic stash by squash script'")
stash_created = True
yield lambda x: setattr(x, 'temp_branch', temp_branch)
except Exception as e:
print(f"\nError occurred: {str(e)}")
cleanup_handler()
raise
finally:
cleanup_handler()
def create_commit_with_message(message: str) -> bool:
"""Create a commit with the given message using a temporary file."""
try:
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
f.write(message)
temp_path = f.name
# Use the temporary file for the commit message
code, _, error = run_command(f"git commit -F {temp_path}")
os.unlink(temp_path) # Clean up the temp file
if code != 0:
print(f"Error creating commit: {error}")
return False
return True
except Exception as e:
print(f"Error handling commit message: {e}")
if os.path.exists(temp_path):
os.unlink(temp_path)
return False
def squash_and_merge(source_branch: str, target_branch: str, manual_title: str | None, backup: bool = False, push: bool = False) -> bool:
"""
Squash the source branch and merge into target branch.
"""
# Get original branch right away
original_branch = get_current_branch()
if not original_branch:
return False
class State:
temp_branch: str | None = None
state = State()
with workspace_manager(original_branch) as set_temp_branch:
# Validate source branch exists
code, _, error = run_command(f"git rev-parse --verify {source_branch}")
if code != 0:
print(f"Error: Source branch {source_branch} not found")
return False
if source_branch == target_branch:
print(f"Error: Source and target branches cannot be the same ({source_branch})")
return False
# Ensure target branch exists
code, _, error = run_command(f"git rev-parse --verify {target_branch}")
if code != 0:
print(f"Error: Target branch {target_branch} not found")
return False
# Find merge base
code, merge_base, error = run_command(f"git merge-base {target_branch} {source_branch}")
if code != 0:
print(f"Error finding merge base: {error}")
return False
# Create backup unless explicitly skipped
if backup and not backup_branch(source_branch):
return False
# Get commit messages
commit_messages = get_commit_messages(source_branch, target_branch)
if commit_messages is None:
return False
# Get title (priority: manual title > PR title > prompt user)
title = manual_title
if not title:
title = get_pr_info(source_branch)
if not title:
title = prompt_for_title()
try:
# Create and switch to temporary branch
temp_branch = f"temp-squash-{source_branch}"
state.temp_branch = temp_branch
set_temp_branch(state)
print(f"\nCreating temporary branch {temp_branch}...")
code, _, error = run_command(f"git checkout -b {temp_branch} {source_branch}")
if code != 0:
print(f"Error creating temp branch: {error}")
return False
print("Preparing squash by resetting temporary branch to merge base...")
code, _, error = run_command(f"git reset --soft {merge_base}")
if code != 0:
print(f"Error resetting for squash: {error}")
return False
# Create commit with message
print("Creating squash commit...")
squash_message = create_squash_message(title, commit_messages, source_branch)
if not create_commit_with_message(squash_message):
return False
# Switch to target and try merge
print(f"\nSwitching to target branch {target_branch}...")
code, _, error = run_command(f"git checkout {target_branch}")
if code != 0:
print(f"Error checking out target branch: {error}")
return False
print(f"Attempting to merge changes from {temp_branch}...")
code, _, error = run_command(f"git merge {temp_branch}")
if code != 0:
print(f"\nMerge failed with error: {error}")
print("\nThe squash was successful, and your changes are preserved in the temporary branch.")
print("To complete the merge manually, follow these steps:")
print(f"\n1. Your squashed changes are in branch: '{temp_branch}'")
print(f"2. The target branch is: '{target_branch}'")
print("\nTo resolve the conflicts:")
print(f" git checkout {target_branch}")
print(f" git merge {temp_branch}")
print(" # resolve conflicts in your editor")
print(" git add <resolved-files>")
print(" git commit")
print(f" git push origin {target_branch} # when ready to push")
print("\nTo clean up after successful merge:")
print(f" git branch -D {temp_branch}")
# Make sure to abort the merge
print("\nAborting current merge attempt...")
run_command("git merge --abort")
# Return to original branch, but keep temp branch
print(f"Returning to {original_branch}...")
run_command(f"git checkout {original_branch}")
return False
# Clean up temp branch on success
run_command(f"git branch -D {temp_branch}")
# Push if requested
if push:
code, _, error = run_command(f"git push origin {target_branch}")
if code != 0:
print(f"Error pushing to {target_branch}: {error}")
return False
print(f"Successfully pushed to {target_branch}")
else:
print(f"Changes squashed and merged into {target_branch} locally")
print(f"To push the changes: git push origin {target_branch}")
# Return to original branch
code, _, error = run_command(f"git checkout {original_branch}")
if code != 0:
print(f"Warning: Failed to return to original branch: {error}")
return False
return True
except Exception as e:
print(f"Error during squash process: {e}")
return False
def main():
parser = argparse.ArgumentParser(
description='Squash branch and merge into target branch'
)
parser.add_argument('--target', '-t', required=True,
help='Target branch to merge changes into')
parser.add_argument('--source', '-s',
help='Source branch to squash (default: current branch)')
parser.add_argument('--title', '-m',
help='Optional manual title (overrides PR title)')
parser.add_argument('--backup', action='store_true',
help='Creates a backup branch for the source branch')
parser.add_argument('--push', action='store_true',
help='Push changes to remote after squashing')
args = parser.parse_args()
# Determine source branch early
source_branch = args.source
if not source_branch:
source_branch = get_current_branch()
if not source_branch:
sys.exit(1)
if not squash_and_merge(source_branch, args.target, args.title, args.backup, args.push):
sys.exit(1)
if __name__ == "__main__":
main()
+1 -1
View File
@@ -57,7 +57,7 @@ function run_tests() {
if [[ -z "$FAST" ]]; then
run "mypy" mypy $PYTHON_FILES
run "codespell" codespell $ALL_FILES
run "codespell" codespell $ALL_FILES --ignore-words=$ROOT/.codespellignore
fi
return $FAILED
+6
View File
@@ -22,6 +22,8 @@ from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
from openpilot.selfdrive.car.cruise import VCruiseHelper
from openpilot.selfdrive.car.car_specific import MockCarState
from openpilot.sunnypilot.mads.mads import MadsParams
REPLAY = "REPLAY" in os.environ
EventName = log.OnroadEvent.EventName
@@ -113,6 +115,10 @@ class Car:
if not disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
# mads
MadsParams().set_alternative_experience(self.CP)
MadsParams().set_car_specific_params(self.CP)
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
+14 -1
View File
@@ -19,6 +19,7 @@ from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
from opendbc.sunnypilot import SunnypilotParamFlags
State = log.SelfdriveState.OpenpilotState
LaneChangeState = log.LaneChangeState
@@ -56,6 +57,9 @@ class Controls:
elif self.CP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.CI)
data_services = list(self.sm.data.keys()) + ['selfdriveStateSP']
self.sm = messaging.SubMaster(data_services, poll='selfdriveState')
def update(self):
self.sm.update(15)
if self.sm.updated["liveCalibration"]:
@@ -88,7 +92,16 @@ class Controls:
# Check which actuators can be enabled
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED) or CS.standstill
CC.latActive = self.sm['selfdriveState'].active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill
ss_sp = self.sm['selfdriveStateSP']
CC.madsEnabled = ss_sp.mads.enabled
if ss_sp.mads.available:
CC.sunnypilotParams |= SunnypilotParamFlags.ENABLE_MADS.value
_lat_active = ss_sp.mads.active
else:
_lat_active = self.sm['selfdriveState'].active
CC.latActive = _lat_active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill
CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and self.CP.openpilotLongitudinalControl
actuators = CC.actuators
+1 -1
View File
@@ -21,7 +21,7 @@ else:
libs += ['OpenCL']
# Set path definitions
for pathdef, fn in {'TRANSFORM': 'transforms/transform.cl', 'LOADYUV': 'transforms/loadyuv.cl'}.items():
for pathdef, fn in {'TRANSFORM': 'transforms/transform.cl', 'LOADYUV': 'transforms/loadyuv.cl', 'LOADYUV_FLOAT': 'transforms/loadyuv_float.cl'}.items():
for xenv in (lenv, lenvCython):
xenv['CXXFLAGS'].append(f'-D{pathdef}_PATH=\\"{File(fn).abspath}\\"')
+56 -58
View File
@@ -1,21 +1,13 @@
#!/usr/bin/env python3
import os
from openpilot.system.hardware import TICI
from openpilot.selfdrive.modeld.runners.model_runner import ONNXRunner, TinygradRunner
#
if TICI:
from tinygrad.tensor import Tensor
from tinygrad.dtype import dtypes
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
os.environ['QCOM'] = '1'
else:
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner
import time
import pickle
import numpy as np
import cereal.messaging as messaging
from cereal import car, log
from pathlib import Path
from setproctitle import setproctitle
from cereal.messaging import PubMaster, SubMaster
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
@@ -31,15 +23,12 @@ from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.modeld.parse_model_outputs import Parser
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
# from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrameLegacy as DrivingModelFrame, CLContext
PROCESS_NAME = "selfdrive.modeld.modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx'
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
class FrameMeta:
frame_id: int = 0
@@ -61,35 +50,26 @@ class ModelState:
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
self.full_features_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.desire_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
self.is_20hz = False
# Initialize model runner
self.model_runner = TinygradRunner(self.frames) if TICI else ONNXRunner(self.frames)
# img buffers are managed in openCL transform code
self.numpy_inputs = {
'desire': np.zeros((1, (ModelConstants.HISTORY_BUFFER_LEN+1), ModelConstants.DESIRE_LEN), dtype=np.float32),
'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32),
'features_buffer': np.zeros((1, ModelConstants.HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
}
self.numpy_inputs = {}
with open(METADATA_PATH, 'rb') as f:
model_metadata = pickle.load(f)
self.input_shapes = model_metadata['input_shapes']
for key, shape in self.model_runner.input_shapes.items():
if key not in self.frames: # Managed by opencl
self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32)
self.output_slices = model_metadata['output_slices']
net_output_size = model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
self.parser = Parser()
if TICI:
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
with open(MODEL_PKL_PATH, "rb") as f:
self.model_run = pickle.load(f)
else:
self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH)
net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]:
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_model_outputs['raw_pred'] = model_outputs.copy()
return parsed_model_outputs
num_elements = self.numpy_inputs['features_buffer'].shape[1]
step_size = int(-100 / num_elements)
self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2])
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
@@ -98,38 +78,54 @@ class ModelState:
new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0)
self.prev_desire[:] = inputs['desire']
self.desire_20Hz[:-1] = self.desire_20Hz[1:]
self.desire_20Hz[-1] = new_desire
self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape((1,25,4,-1)).max(axis=2)
if self.is_20hz:
self.desire_20Hz[:-1] = self.desire_20Hz[1:]
self.desire_20Hz[-1] = new_desire
self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape(self.desire_reshape_dims).max(axis=2)
else:
len = inputs['desire'].shape[0]
self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:]
self.numpy_inputs['desire'][0, -1, :len] = new_desire[:len]
for key in self.numpy_inputs:
if key in inputs and key not in ['desire']:
self.numpy_inputs[key][:] = inputs[key]
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()),
'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())}
if TICI:
# The imgs tensors are backed by opencl memory, only need init once
for key in imgs_cl:
if key not in self.tensor_inputs:
self.tensor_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
else:
for key in imgs_cl:
self.numpy_inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
# Prepare inputs using the model runner
self.model_runner.prepare_inputs(imgs_cl, self.numpy_inputs)
if prepare_only:
return None
if TICI:
self.output = self.model_run(**self.tensor_inputs).numpy().flatten()
# Run model inference
self.output = self.model_runner.run_model()
outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output), self.numpy_inputs.keys())
if self.is_20hz:
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[self.full_features_20Hz_idxs]
else:
self.output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten()
feature_len = outputs['hidden_state'].shape[1]
self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:]
self.numpy_inputs['features_buffer'][0, -1, :feature_len] = outputs['hidden_state'][0, :feature_len]
outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
if "desired_curvature" in outputs:
input_name_prev = None
idxs = np.arange(-4,-100,-4)[::-1]
self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[idxs]
if "prev_desired_curvs" in self.numpy_inputs.keys():
input_name_prev = 'prev_desired_curvs'
elif "prev_desired_curv" in self.numpy_inputs.keys():
input_name_prev = 'prev_desired_curv'
if input_name_prev is not None:
len = outputs['desired_curvature'][0].size
self.numpy_inputs[input_name_prev][0, :-len, 0] = self.numpy_inputs[input_name_prev][0, len:, 0]
self.numpy_inputs[input_name_prev][0, -len:, 0] = outputs['desired_curvature'][0]
return outputs
@@ -190,7 +186,6 @@ def main(demo=False):
meta_main = FrameMeta()
meta_extra = FrameMeta()
if demo:
CP = get_demo_car_params()
else:
@@ -272,6 +267,9 @@ def main(demo=False):
'traffic_convention': traffic_convention,
}
if "lateral_control_params" in model.numpy_inputs.keys():
inputs['lateral_control_params'] = np.array([sm["carState"].vEgo, steer_delay], dtype=np.float32)
mt1 = time.perf_counter()
model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
mt2 = time.perf_counter()
+48 -4
View File
@@ -8,25 +8,30 @@
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context) : ModelFrame(device_id, context) {
input_frames = std::make_unique<uint8_t[]>(buf_size);
input_frames_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size, NULL, &err));
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, 5*frame_size_bytes, NULL, &err));
region.origin = 4 * frame_size_bytes;
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buffer_length*frame_size_bytes, NULL, &err));
region.origin = (buffer_length - 1) * frame_size_bytes;
region.size = frame_size_bytes;
last_img_cl = CL_CHECK_ERR(clCreateSubBuffer(img_buffer_20hz_cl, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, &region, &err));
printf("Buffer length: %d, region origin: %lu, region size: %lu\n", buffer_length, region.origin, region.size);
loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT);
loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT, is_float);
init_transform(device_id, context, MODEL_WIDTH, MODEL_HEIGHT);
}
cl_mem* DrivingModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
for (int i = 0; i < 4; i++) {
for (int i = 0; i < (buffer_length - 1); i++) {
printf("Moving %d to %d from src_offset %lu to dst_offset %lu with size %lu\n", i+1, i, (i+1)*frame_size_bytes, i*frame_size_bytes, frame_size_bytes);
CL_CHECK(clEnqueueCopyBuffer(q, img_buffer_20hz_cl, img_buffer_20hz_cl, (i+1)*frame_size_bytes, i*frame_size_bytes, frame_size_bytes, 0, nullptr, nullptr));
}
printf("Loop done\n");
loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, last_img_cl);
copy_queue(&loadyuv, q, img_buffer_20hz_cl, input_frames_cl, 0, 0, frame_size_bytes);
printf("Copying from %p to %p with size %lu\n", img_buffer_20hz_cl, input_frames_cl, frame_size_bytes);
copy_queue(&loadyuv, q, last_img_cl, input_frames_cl, 0, frame_size_bytes, frame_size_bytes);
printf("Copying from %p to %p with size %lu\n", last_img_cl, input_frames_cl, frame_size_bytes);
// NOTE: Since thneed is using a different command queue, this clFinish is needed to ensure the image is ready.
clFinish(q);
@@ -41,6 +46,45 @@ DrivingModelFrame::~DrivingModelFrame() {
CL_CHECK(clReleaseCommandQueue(q));
}
DrivingModelFrameLegacy::DrivingModelFrameLegacy(cl_device_id device_id, cl_context context): ModelFrame(device_id, context) {
input_frames = std::make_unique<float[]>(buf_size);
input_frames_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size * sizeof(float), NULL, &err));
// Reduce buffer to 2 frames due to float size (4x bigger than uint8_t)
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, 2 * frame_size_bytes * sizeof(float), NULL, &err));
// Adjust region for 2-frame buffer
region.origin = frame_size_bytes * sizeof(float); // Point to second frame
region.size = frame_size_bytes * sizeof(float);
last_img_cl = CL_CHECK_ERR(clCreateSubBuffer(img_buffer_20hz_cl, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, &region, &err));
loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT, true);
init_transform(device_id, context, MODEL_WIDTH, MODEL_HEIGHT);
}
cl_mem* DrivingModelFrameLegacy::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset,
const mat3 &projection) {
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
// Rolling buffer with just 2 frames
CL_CHECK(clEnqueueCopyBuffer(q, img_buffer_20hz_cl, img_buffer_20hz_cl, frame_size_bytes * sizeof(float), 0, frame_size_bytes * sizeof(float), 0, nullptr, nullptr));
loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, last_img_cl);
// Copy both frames to input buffer
copy_queue(&loadyuv, q, img_buffer_20hz_cl, input_frames_cl, 0, 0, frame_size_bytes * sizeof(float));
copy_queue(&loadyuv, q, last_img_cl, input_frames_cl, 0, frame_size_bytes * sizeof(float), frame_size_bytes * sizeof(float));
clFinish(q);
return &input_frames_cl;
}
DrivingModelFrameLegacy::~DrivingModelFrameLegacy() {
deinit_transform();
loadyuv_destroy(&loadyuv);
CL_CHECK(clReleaseMemObject(img_buffer_20hz_cl));
CL_CHECK(clReleaseMemObject(input_frames_cl));
CL_CHECK(clReleaseCommandQueue(q));
}
MonitoringModelFrame::MonitoringModelFrame(cl_device_id device_id, cl_context context) : ModelFrame(device_id, context) {
input_frames = std::make_unique<uint8_t[]>(buf_size);
+31 -4
View File
@@ -17,6 +17,7 @@
#include "selfdrive/modeld/transforms/loadyuv.h"
#include "selfdrive/modeld/transforms/transform.h"
template <typename T = uint8_t>
class ModelFrame {
public:
ModelFrame(cl_device_id device_id, cl_context context) {
@@ -24,7 +25,7 @@ public:
}
virtual ~ModelFrame() {}
virtual cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) { return NULL; }
uint8_t* buffer_from_cl(cl_mem *in_frames, int buffer_size) {
T* buffer_from_cl(cl_mem *in_frames, int buffer_size) {
CL_CHECK(clEnqueueReadBuffer(q, *in_frames, CL_TRUE, 0, buffer_size, input_frames.get(), 0, nullptr, nullptr));
clFinish(q);
return &input_frames[0];
@@ -39,7 +40,7 @@ protected:
cl_mem y_cl, u_cl, v_cl;
Transform transform;
cl_command_queue q;
std::unique_ptr<uint8_t[]> input_frames;
std::unique_ptr<T[]> input_frames;
void init_transform(cl_device_id device_id, cl_context context, int model_width, int model_height) {
y_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, model_width * model_height, NULL, &err));
@@ -62,7 +63,11 @@ protected:
}
};
class DrivingModelFrame : public ModelFrame {
class DrivingModelFrame : public ModelFrame<> {
using ModelFrame::q, ModelFrame::y_cl, ModelFrame::u_cl, ModelFrame::v_cl;
using ModelFrame::init_transform, ModelFrame::deinit_transform, ModelFrame::run_transform;
using ModelFrame::input_frames;
public:
DrivingModelFrame(cl_device_id device_id, cl_context context);
~DrivingModelFrame();
@@ -73,6 +78,8 @@ public:
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
const int buf_size = MODEL_FRAME_SIZE * 2;
const size_t frame_size_bytes = MODEL_FRAME_SIZE * sizeof(uint8_t);
const bool is_float = false;
const uint8_t buffer_length = is_float ? 2 : 5;
private:
LoadYUVState loadyuv;
@@ -80,7 +87,7 @@ private:
cl_buffer_region region;
};
class MonitoringModelFrame : public ModelFrame {
class MonitoringModelFrame : public ModelFrame<> {
public:
MonitoringModelFrame(cl_device_id device_id, cl_context context);
~MonitoringModelFrame();
@@ -94,3 +101,23 @@ public:
private:
cl_mem input_frame_cl;
};
class DrivingModelFrameLegacy : public ModelFrame<float> {
public:
DrivingModelFrameLegacy(cl_device_id device_id, cl_context context);
~DrivingModelFrameLegacy();
cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection);
const int MODEL_WIDTH = 512;
const int MODEL_HEIGHT = 256;
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
const int buf_size = MODEL_FRAME_SIZE * 2;
private:
cl_mem input_frames_cl;
cl_mem img_buffer_20hz_cl;
cl_mem last_img_cl;
LoadYUVState loadyuv;
cl_buffer_region region;
const size_t frame_size_bytes = MODEL_FRAME_SIZE * sizeof(float); // This is the only change
};
+6 -2
View File
@@ -12,15 +12,19 @@ cdef extern from "common/clutil.h":
cl_context cl_create_context(cl_device_id)
cdef extern from "selfdrive/modeld/models/commonmodel.h":
cppclass ModelFrame:
cppclass ModelFrame[T]:
int buf_size
unsigned char * buffer_from_cl(cl_mem*, int);
T * buffer_from_cl(cl_mem*, int);
cl_mem * prepare(cl_mem, int, int, int, int, mat3)
cppclass DrivingModelFrame:
int buf_size
DrivingModelFrame(cl_device_id, cl_context)
cppclass DrivingModelFrameLegacy:
int buf_size
DrivingModelFrameLegacy(cl_device_id, cl_context)
cppclass MonitoringModelFrame:
int buf_size
MonitoringModelFrame(cl_device_id, cl_context)
+33 -5
View File
@@ -4,12 +4,12 @@
import numpy as np
cimport numpy as cnp
from libc.string cimport memcpy
from libc.stdint cimport uintptr_t
from libc.stdint cimport uintptr_t, uint8_t
from msgq.visionipc.visionipc cimport cl_mem
from msgq.visionipc.visionipc_pyx cimport VisionBuf, CLContext as BaseCLContext
from .commonmodel cimport CL_DEVICE_TYPE_DEFAULT, cl_get_device_id, cl_create_context
from .commonmodel cimport mat3, ModelFrame as cppModelFrame, DrivingModelFrame as cppDrivingModelFrame, MonitoringModelFrame as cppMonitoringModelFrame
from .commonmodel cimport mat3, ModelFrame as cppModelFrame, DrivingModelFrame as cppDrivingModelFrame, DrivingModelFrameLegacy as cppDrivingModelFrameLegacy, MonitoringModelFrame as cppMonitoringModelFrame
cdef class CLContext(BaseCLContext):
@@ -33,7 +33,7 @@ def cl_from_visionbuf(VisionBuf buf):
cdef class ModelFrame:
cdef cppModelFrame * frame
cdef cppModelFrame[uint8_t] * frame
cdef int buf_size
def __dealloc__(self):
@@ -52,12 +52,40 @@ cdef class ModelFrame:
return np.asarray(<cnp.uint8_t[:self.buf_size]> data2)
cdef class ModelFrame_float:
cdef cppModelFrame[float] * frame
cdef int buf_size
def __dealloc__(self):
del self.frame
def prepare(self, VisionBuf buf, float[:] projection):
cdef mat3 cprojection
memcpy(cprojection.v, &projection[0], 9*sizeof(float))
cdef cl_mem * data
data = self.frame.prepare(buf.buf.buf_cl, buf.width, buf.height, buf.stride, buf.uv_offset, cprojection)
return CLMem.create(data)
def buffer_from_cl(self, CLMem in_frames):
cdef float * data2
data2 = self.frame.buffer_from_cl(in_frames.mem, self.buf_size)
return np.asarray(<cnp.float32_t[:self.buf_size]> data2)
cdef class DrivingModelFrame(ModelFrame):
cdef cppDrivingModelFrame * _frame
def __cinit__(self, CLContext context):
self._frame = new cppDrivingModelFrame(context.device_id, context.context)
self.frame = <cppModelFrame*>(self._frame)
self.frame = <cppModelFrame[uint8_t]*>(self._frame)
self.buf_size = self._frame.buf_size
cdef class DrivingModelFrameLegacy(ModelFrame_float):
cdef cppDrivingModelFrameLegacy * _frame
def __cinit__(self, CLContext context):
self._frame = new cppDrivingModelFrameLegacy(context.device_id, context.context)
self.frame = <cppModelFrame[float]*>(self._frame)
self.buf_size = self._frame.buf_size
cdef class MonitoringModelFrame(ModelFrame):
@@ -65,6 +93,6 @@ cdef class MonitoringModelFrame(ModelFrame):
def __cinit__(self, CLContext context):
self._frame = new cppMonitoringModelFrame(context.device_id, context.context)
self.frame = <cppModelFrame*>(self._frame)
self.frame = <cppModelFrame[uint8_t]*>(self._frame)
self.buf_size = self._frame.buf_size
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72d3d6f8d3c98f5431ec86be77b6350d7d4f43c25075c0106f1d1e7ec7c77668
size 49096168
oid sha256:b31b504bc0b440d3bc72967507a00eb4f112285626fbfb3135011500325ee6d6
size 51452435
+4 -1
View File
@@ -84,7 +84,8 @@ class Parser:
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
def parse_outputs(self, outs: dict[str, np.ndarray], input_keys: [str]) -> dict[str, np.ndarray]:
""" Parse the model outputs into a dictionary of numpy arrays. The input_keys are used to determine how the output should be parsed. """
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
@@ -96,6 +97,8 @@ class Parser:
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs and "prev_desired_curv" in input_keys:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob', 'meta']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
+115
View File
@@ -0,0 +1,115 @@
import os
from openpilot.system.hardware import TICI
#
from tinygrad.tensor import Tensor, dtypes
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES
import pickle
import numpy as np
from pathlib import Path
from abc import ABC, abstractmethod
# from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLMem
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrameLegacy as DrivingModelFrame, CLMem
if TICI:
os.environ['QCOM'] = '1'
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
MODEL_PATH = Path(__file__).parent / '../models/supercombo.onnx'
MODEL_PKL_PATH = Path(__file__).parent / '../models/supercombo_tinygrad.pkl'
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
class ModelRunner(ABC):
"""Abstract base class for model runners that defines the interface for running ML models."""
def __init__(self):
"""Initialize the model runner with paths to model and metadata files."""
with open(METADATA_PATH, 'rb') as f:
self.model_metadata = pickle.load(f)
self.input_shapes = self.model_metadata['input_shapes']
self.output_slices = self.model_metadata['output_slices']
self.inputs: dict = {}
@abstractmethod
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray])-> dict:
"""Prepare inputs for model inference."""
@abstractmethod
def run_model(self):
"""Run model inference with prepared inputs."""
def slice_outputs(self, model_outputs: np.ndarray) -> dict:
"""Slice model outputs according to metadata configuration."""
parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_outputs['raw_pred'] = model_outputs.copy()
return parsed_outputs
class TinygradRunner(ModelRunner):
"""Tinygrad implementation of model runner for TICI hardware."""
def __init__(self, frames: dict[str, DrivingModelFrame] | None = None):
super().__init__()
# Load Tinygrad model
with open(MODEL_PKL_PATH, "rb") as f:
self.model_run = pickle.load(f)
self.input_to_dtype = {}
self.input_to_device = {}
for idx, name in enumerate(self.model_run.captured.expected_names):
self.input_to_dtype[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][2] # 2 is the dtype
self.input_to_device[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][3] # 3 is the device
assert TICI or frames is not None, "TinygradRunner requires frames for non-TICI hardware"
self.frames = frames
self.is_memory_model = None # Use None to indicate that it hasn't been determined yet
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray]) -> dict:
if self.is_memory_model is None:
self.is_memory_model = any(self.input_to_dtype[key] == dtypes.uint8 for key in imgs_cl)
print(f"Memory model: {self.is_memory_model}")
# Initialize image tensors if not already done
for key in imgs_cl:
if TICI and self.is_memory_model and key not in self.inputs:
self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
elif not TICI or not self.is_memory_model:
shape = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
self.inputs[key] = Tensor(shape, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
# Update numpy inputs
for key, value in numpy_inputs.items():
if key not in imgs_cl:
self.inputs[key] = Tensor(value, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
return self.inputs
def run_model(self):
return self.model_run(**self.inputs).numpy().flatten()
class ONNXRunner(ModelRunner):
"""ONNX implementation of model runner for non-TICI hardware."""
def __init__(self, frames: dict[str, DrivingModelFrame]):
super().__init__()
self.runner = make_onnx_cpu_runner(MODEL_PATH)
self.frames = frames
self.input_to_nptype = {
model_input.name: ORT_TYPES_TO_NP_TYPES[model_input.type]
for model_input in self.runner.get_inputs()
}
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray]) -> dict:
self.inputs = numpy_inputs.copy()
for key in imgs_cl:
self.inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
return self.inputs
def run_model(self):
return self.runner.run(None, self.inputs)[0].flatten()
+4 -2
View File
@@ -4,7 +4,7 @@
#include <cstdio>
#include <cstring>
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height) {
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height, bool use_float) {
memset(s, 0, sizeof(*s));
s->width = width;
@@ -15,7 +15,9 @@ void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int w
"-cl-fast-relaxed-math -cl-denorms-are-zero "
"-DTRANSFORMED_WIDTH=%d -DTRANSFORMED_HEIGHT=%d",
width, height);
cl_program prg = cl_program_from_file(ctx, device_id, LOADYUV_PATH, args);
const char * loadyuv_path = use_float ? LOADYUV_FLOAT_PATH : LOADYUV_PATH;
printf(" Use float: %d\n Using loadyuv_path: %s\n", use_float, loadyuv_path);
cl_program prg = cl_program_from_file(ctx, device_id, loadyuv_path, args);
s->loadys_krnl = CL_CHECK_ERR(clCreateKernel(prg, "loadys", &err));
s->loaduv_krnl = CL_CHECK_ERR(clCreateKernel(prg, "loaduv", &err));
+2 -1
View File
@@ -7,7 +7,8 @@ typedef struct {
cl_kernel loadys_krnl, loaduv_krnl, copy_krnl;
} LoadYUVState;
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height);
void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height, bool use_float);
inline void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height) { loadyuv_init(s, ctx, device_id, width, height, false); };
void loadyuv_destroy(LoadYUVState* s);
@@ -0,0 +1,49 @@
#define UV_SIZE ((TRANSFORMED_WIDTH/2)*(TRANSFORMED_HEIGHT/2))
__kernel void loadys(__global uchar8 const * const Y,
__global float * out,
int out_offset)
{
const int gid = get_global_id(0);
const int ois = gid * 8;
const int oy = ois / TRANSFORMED_WIDTH;
const int ox = ois % TRANSFORMED_WIDTH;
const uchar8 ys = Y[gid];
const float8 ysf = convert_float8(ys);
// 02
// 13
__global float* outy0;
__global float* outy1;
if ((oy & 1) == 0) {
outy0 = out + out_offset; //y0
outy1 = out + out_offset + UV_SIZE*2; //y2
} else {
outy0 = out + out_offset + UV_SIZE; //y1
outy1 = out + out_offset + UV_SIZE*3; //y3
}
vstore4(ysf.s0246, 0, outy0 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2);
vstore4(ysf.s1357, 0, outy1 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2);
}
__kernel void loaduv(__global uchar8 const * const in,
__global float8 * out,
int out_offset)
{
const int gid = get_global_id(0);
const uchar8 inv = in[gid];
const float8 outv = convert_float8(inv);
out[gid + out_offset / 8] = outv;
}
__kernel void copy(__global float8 * in,
__global float8 * out,
int in_offset,
int out_offset)
{
const int gid = get_global_id(0);
out[gid + out_offset / 8] = in[gid + in_offset / 8];
}
+2 -2
View File
@@ -137,8 +137,8 @@ void Panda::enable_deepsleep() {
handle->control_write(0xfb, 0, 0);
}
void Panda::send_heartbeat(bool engaged) {
handle->control_write(0xf3, engaged, 0);
void Panda::send_heartbeat(bool engaged, bool engaged_mads) {
handle->control_write(0xf3, engaged, engaged_mads);
}
void Panda::set_can_speed_kbps(uint16_t bus, uint16_t speed) {
+1 -1
View File
@@ -75,7 +75,7 @@ public:
std::optional<std::string> get_serial();
void set_power_saving(bool power_saving);
void enable_deepsleep();
void send_heartbeat(bool engaged);
void send_heartbeat(bool engaged, bool engaged_mads);
void set_can_speed_kbps(uint16_t bus, uint16_t speed);
void set_can_fd_auto(uint16_t bus, bool enabled);
void set_data_speed_kbps(uint16_t bus, uint16_t speed);
+18 -2
View File
@@ -41,6 +41,8 @@
#define CUTOFF_IL 400
#define SATURATE_IL 1000
#define ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE 2048
ExitHandler do_exit;
bool check_all_connected(const std::vector<Panda *> &pandas) {
@@ -53,6 +55,18 @@ bool check_all_connected(const std::vector<Panda *> &pandas) {
return true;
}
bool process_mads_heartbeat(SubMaster *sm) {
const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience();
const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_DISENGAGE_LATERAL_ON_BRAKE) != 0;
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled();
const bool engaged = sm->allAliveAndValid({"selfdriveStateSP"}) && heartbeat_type;
return engaged;
}
Panda *connect(std::string serial="", uint32_t index=0) {
std::unique_ptr<Panda> panda;
try {
@@ -144,6 +158,7 @@ void fill_panda_state(cereal::PandaState::Builder &ps, cereal::PandaState::Panda
ps.setIgnitionLine(health.ignition_line_pkt);
ps.setIgnitionCan(health.ignition_can_pkt);
ps.setControlsAllowed(health.controls_allowed_pkt);
ps.setControlsAllowedLat(health.controls_allowed_lat_pkt);
ps.setTxBufferOverflow(health.tx_buffer_overflow_pkt);
ps.setRxBufferOverflow(health.rx_buffer_overflow_pkt);
ps.setPandaType(hw_type);
@@ -327,7 +342,7 @@ void send_peripheral_state(Panda *panda, PubMaster *pm) {
}
void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoofing_started) {
static SubMaster sm({"selfdriveState"});
static SubMaster sm({"selfdriveState", "selfdriveStateSP", "carParams"});
std::vector<std::string> connected_serials;
for (Panda *p : pandas) {
@@ -366,8 +381,9 @@ void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool spoof
sm.update(0);
const bool engaged = sm.allAliveAndValid({"selfdriveState"}) && sm["selfdriveState"].getSelfdriveState().getEnabled();
const bool engaged_mads = process_mads_heartbeat(&sm);
for (const auto &panda : pandas) {
panda->send_heartbeat(engaged);
panda->send_heartbeat(engaged, engaged_mads);
}
}
}
+111
View File
@@ -105,6 +105,24 @@ class Events:
ret.append(event)
return ret
def has(self, event_name: int) -> bool:
return event_name in self.events
def contains_in_list(self, events_list: list[int]) -> bool:
return any(event_name in self.events for event_name in events_list)
def remove(self, event_name: int, static: bool = False) -> None:
if static and event_name in self.static_events:
self.static_events.remove(event_name)
if event_name in self.events:
self.event_counters[event_name] = self.event_counters[event_name] + 1
self.events.remove(event_name)
def replace(self, prev_event_name: int, cur_event_name: int, static: bool = False) -> None:
self.remove(prev_event_name, static)
self.add(cur_event_name, static)
class Alert:
def __init__(self,
@@ -951,6 +969,99 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
ET.WARNING: personality_changed_alert,
},
# sunnypilot
EventName.lkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.engage),
},
EventName.lkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
},
EventName.manualSteeringRequired: {
ET.USER_DISABLE: Alert(
"Automatic Lane Centering is OFF",
"Manual Steering Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.disengage, 1.),
},
EventName.manualLongitudinalRequired: {
ET.WARNING: Alert(
"Smart/Adaptive Cruise Control: OFF",
"Manual Speed Control Required",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 1.),
},
EventName.silentLkasEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.none),
},
EventName.silentLkasDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
},
EventName.silentBrakeHold: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.none),
ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"),
},
EventName.silentWrongGear: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: Alert(
"Gear not D",
"openpilot Unavailable",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 0.),
},
EventName.silentReverseGear: {
ET.PERMANENT: Alert(
"Reverse\nGear",
"",
AlertStatus.normal, AlertSize.full,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
},
EventName.silentDoorOpen: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Door Open"),
},
EventName.silentSeatbeltNotLatched: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Seatbelt Unlatched"),
},
EventName.silentParkBrake: {
ET.WARNING: Alert(
"",
"",
AlertStatus.normal, AlertSize.none,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, 0.),
ET.NO_ENTRY: NoEntryAlert("Parking Brake Engaged"),
},
EventName.controlsMismatchLateral: {
ET.IMMEDIATE_DISABLE: ImmediateDisableAlert("Controls Mismatch: Lateral"),
ET.NO_ENTRY: NoEntryAlert("Controls Mismatch: Lateral"),
},
}
+22
View File
@@ -23,6 +23,8 @@ from openpilot.selfdrive.controls.lib.latcontrol import MIN_LATERAL_CONTROL_SPEE
from openpilot.system.version import get_build_metadata
from openpilot.sunnypilot.mads.mads import ModularAssistiveDrivingSystem
REPLAY = "REPLAY" in os.environ
SIMULATION = "SIMULATION" in os.environ
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
@@ -131,6 +133,10 @@ class SelfdriveD:
elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True)
self.mads = ModularAssistiveDrivingSystem(self)
sock_services = list(self.pm.sock.keys()) + ['selfdriveStateSP']
self.pm = messaging.PubMaster(sock_services)
def update_events(self, CS):
"""Compute onroadEvents from carState"""
@@ -451,11 +457,25 @@ class SelfdriveD:
self.pm.send('onroadEvents', ce_send)
self.events_prev = self.events.names.copy()
# selfdriveStateSP
ss_sp_msg = messaging.new_message('selfdriveStateSP')
ss_sp_msg.valid = True
ss_sp = ss_sp_msg.selfdriveStateSP
mads = ss_sp.mads
mads.state = self.mads.state_machine.state
mads.enabled = self.mads.enabled
mads.active = self.mads.active
mads.available = self.mads.enabled_toggle
self.pm.send('selfdriveStateSP', ss_sp_msg)
def step(self):
CS = self.data_sample()
self.update_events(CS)
if not self.CP.passive and self.initialized:
self.enabled, self.active = self.state_machine.update(self.events)
if not self.CP.notCar:
self.mads.update(CS, self.sm)
self.update_alerts(CS)
self.publish_selfdriveState(CS)
@@ -473,6 +493,8 @@ class SelfdriveD:
self.is_metric = self.params.get_bool("IsMetric")
self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
self.personality = self.read_personality_param()
self.mads.read_params()
time.sleep(0.1)
def run(self):
+1 -1
View File
@@ -41,7 +41,7 @@ class TestAlerts:
events = log.OnroadEvent.EventName.schema.enumerants
for name, e in events.items():
if not name.endswith("DEPRECATED"):
if not name.endswith("DEPRECATED") and not name.startswith("eventReserved"):
fail_msg = f"{name} @{e} not in EVENTS"
assert e in EVENTS.keys(), fail_msg
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:737f95d34912db53a303ba6499e6f697b510fa5872b8c71f701a4fe924b5466e
size 356169
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:79ccc3bd2094ba8a55adedf0007b0152eb3a68edc5e2d35aeccbba122d5811c6
size 356151
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e359e8f6b5a22b6f3f89b54989dac2110ee3a4463de2d785be83e20cda4f1cb6
size 254248
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:45f39fe1d1dc8c271f577d1a12812e01da34979bf3fffaad01a19a7a61b6d456
size 256342
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a8e4d044812a714ebdf0b15e73d4466e9ddaafa374368f308803c6b68dcd79ab
size 332433
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ec8f8d879b38a4c8d4319a3bd67872d5e5d348bc5472443c4c8aa1cb1dd62cf5
size 332404
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:53b80c3c99a6897cafe7d408872c287ab0bbaac2751e344cf4623b312d2e4866
size 268928
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:68afd54dc68f6abff699d2740f90830b0f492db8818869e8936a63e7bed80458
size 268827
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4fa8a841c964e90b9c14b5aede8f30de949d319ae072cc291f0afb1c4c24baa
size 437808
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:32a458c0b364c1c793a4a843fe7a8fd21dd4ad45ff87dc8ad41a8e8759e52c80
size 437811
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ce370994de01d6240fa753846ddc7e1b852acac3f649a4c29ea16acae126f974
size 308578
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b3d412d4066f9ac90788e11a20d99ea3ff92eafdc8a317610b7d9c918aedd59
size 308644
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:82333cfc026735eac81defae9e01b82b81ac30fd01ad265dbdd311dd97322198
size 393106
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8ad0c3881d7d88f9038a1b3a5e43c779e84dca47e2f7f6d45a3449cd8a7dec99
size 393122
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e4ebae1742e3bb7d89f8cd452928c5c6a1b8679155562d23c6303ca4ec2e3b02
size 334350
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a0f9dc3743e4e09375f7a3289228b64486915ae4ef6dfcff66572a34820d5d2
size 334502
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03326fb4486c1ffdd3609a0e0200e65d1f30dca5d9b047501d98b1b2d5321e75
size 470495
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:944406ae64efb422b568437588fec6a8a8b5b7d9e577dc1d22c83a1d6d3813ff
size 470417
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bffa062a5eace4d499c2fe12f2b0eb8c71207f4e7bea0e88456614f00e2859b1
size 258989
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d80c61dc2a0b3685a522013c8aa8347adfdfc6d0611485da88b3a17771c68301
size 260913
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fe97d060fb6e60652b26b26406ca8b03890f6bff4b4bc5cb73fca267068a04c9
size 217460
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3271e31149f7053d134d3a0217851167e573647a8b415138e8c80d429d0a02c9
size 217488
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60e33d209f4f600acf344feca8ab6494a1bf3aafe3c7121e2110a82ea06aa61e
size 293084
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:35c95eaf222affae2e236f0b717c17e7f3b8a30e85d4ebb4ad1506e5025ce0b8
size 293078
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:754d601883db2377739f4112a8dff787b4b8181a8bb8bf4d7dbcf77c5cb6ac93
size 256838
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c8c8fe4943bba5bc86dd0116f75ac6b4fbae551c65ff530a5f62ee6e44b96314
size 259680
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:378d6fc5c674e62b51a12b994cfcd03c435a9731c6e2f4c0d14a39010540cc77
size 100067
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:396e46dfc38ea8f9b513ce81cc6019e76a51cca733360b662cc2c5d0933584d5
size 100090
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:037825cd9b443d038ab3bb370eab802612d1ce528c5de1350fd99965eaa0d131
size 262910
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa6712e3d4c03e1d68f6e1bb991930f6a98eac085aae60ea9d17a3b1914078e5
size 262935
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:58645abd939aa17cf291a254517865704c74124743a8a77da296c519a29567df
size 256653
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b14f71aa4e29f17a69b64802403a7358160667401505ef9a8f56db0c259480f5
size 259192

Some files were not shown because too many files have changed in this diff Show More