From f4530a4b1e067da6e257527d540163c4eea03d0b Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Tue, 6 Jan 2026 14:31:48 -0700 Subject: [PATCH 1/9] cleanup: fix some issues in dev guide, remove roscopter page --- docs/developer-guide/contribution-guidelines.md | 6 +++--- docs/developer-guide/firmware/debugging.md | 2 +- docs/developer-guide/firmware/unit-tests.md | 2 +- docs/developer-guide/roscopter/roscopter-dev-overview.md | 5 ----- 4 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 docs/developer-guide/roscopter/roscopter-dev-overview.md diff --git a/docs/developer-guide/contribution-guidelines.md b/docs/developer-guide/contribution-guidelines.md index 06543a65..217daa69 100644 --- a/docs/developer-guide/contribution-guidelines.md +++ b/docs/developer-guide/contribution-guidelines.md @@ -8,11 +8,11 @@ Although we strive for complete in-code documentation, in practice this sometime Here is a summary of the key goals and philosophies behind ROSflight. As you look to make contributions to the project, keep these in mind. -- Only include the things that most people will need. The goal of this would be to do most of the work so people can get a MAV in the air quickly and easily, but not overcomplicate the code with features that only a small portion of users would need. +- **Only include the things that most people will need.** The goal of this would be to do most of the work so people can get a MAV in the air quickly and easily, but not overcomplicate the code with features that only a small portion of users would need. -- Be modular and adaptable for many research-centric use cases. This will be accomplished by putting the majority of the autopilot in a well-designed ROS2 framework. That which needs to be on the microcontroller will need to be done so carefully with good coding practices. Additionally, microcontroller code that is the most likely to be expanded upon should include clear interfaces and instructions for doing so. +- **Be modular and adaptable for many research-centric use cases.** This will be accomplished by putting the majority of the autopilot in a well-designed ROS2 framework. That which needs to be on the microcontroller will need to be done so carefully with good coding practices. Additionally, microcontroller code that is the most likely to be expanded upon should include clear interfaces and instructions for doing so. -- Keep everything simple and well documented. The key goal here is to minimize the amount of time and effort it takes someone to go from knowing nothing about ROSflight to being able to implement their own features and making meaningful progress with their research. +- **Keep everything simple and well documented.** The key goal here is to minimize the amount of time and effort it takes someone to go from knowing nothing about ROSflight to being able to implement their own features and making meaningful progress with their research. ## Communication diff --git a/docs/developer-guide/firmware/debugging.md b/docs/developer-guide/firmware/debugging.md index 9abb205f..d5ad04c0 100644 --- a/docs/developer-guide/firmware/debugging.md +++ b/docs/developer-guide/firmware/debugging.md @@ -14,7 +14,7 @@ This guide assumes you are running Ubuntu 22.04 LTS, which is the currently supp Follow the guide in [Building and Flashing](building-and-flashing.md) to install the compiler toolchain. -Also make sure you have configured your computer as described in the [Serial Port Configuration](../../user-guide/concepts/flight-controller-setup.md#serial-port-configuration) section of the user guide. +Also make sure you have configured your computer as described in the [Serial Port Configuration](../../user-guide/hardware-and-rosflight/flight-controller-setup.md#serial-port-configuration) section of the user guide. ### Connect debugger to flight controller diff --git a/docs/developer-guide/firmware/unit-tests.md b/docs/developer-guide/firmware/unit-tests.md index 7c56ccff..ece81edb 100644 --- a/docs/developer-guide/firmware/unit-tests.md +++ b/docs/developer-guide/firmware/unit-tests.md @@ -41,7 +41,7 @@ If you want to manually build and run the unit tests, first build them with the cd mkdir build cd build -cmake .. -DBUILD_TEST=TRUE +cmake .. -DBOARD_TO_BUILD=test make ``` diff --git a/docs/developer-guide/roscopter/roscopter-dev-overview.md b/docs/developer-guide/roscopter/roscopter-dev-overview.md deleted file mode 100644 index 287af77e..00000000 --- a/docs/developer-guide/roscopter/roscopter-dev-overview.md +++ /dev/null @@ -1,5 +0,0 @@ -# ROScopter Developer Guide Overview - -!!! danger - - Documentation for ROScopter is still under development. Please check back soon! From de1c23c0f8c416626879b13f5027562e9a23cc1c Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Tue, 6 Jan 2026 14:32:43 -0700 Subject: [PATCH 2/9] cleanup: reorganize user-guide files to prep for dev guide changes --- docs/user-guide/customizing-rosflight.md | 4 +-- .../autonomous-flight.md | 2 +- .../flight-controller-setup.md | 0 .../getting-started.md | 2 +- .../hardware-setup.md | 2 +- .../improving-firmware-performance.md | 0 .../parameter-configuration.md | 0 .../preflight-checks.md | 0 .../rc-configuration.md | 0 .../installation/installation-hardware.md | 2 +- docs/user-guide/overview.md | 8 ++--- .../detailed-launching-guide.md | 4 +-- .../running-simulations-with-rosflight.md | 0 .../simulator-architecture.md | 2 +- .../rosplane-overview.md | 0 .../{concepts => rosplane}/rosplane-setup.md | 0 .../manually-flying-rosflight-sim.md | 10 +++--- .../tutorials/setting-up-roscopter-in-sim.md | 4 +-- .../tutorials/setting-up-rosflight-sim.md | 4 +-- .../tutorials/setting-up-rosplane-in-sim.md | 4 +-- .../tutorials/tuning-performance-in-sim.md | 10 +++--- mkdocs.yml | 31 ++++++++----------- 22 files changed, 42 insertions(+), 47 deletions(-) rename docs/user-guide/{concepts => hardware-and-rosflight}/autonomous-flight.md (99%) rename docs/user-guide/{concepts => hardware-and-rosflight}/flight-controller-setup.md (100%) rename docs/user-guide/{concepts => hardware-and-rosflight}/getting-started.md (97%) rename docs/user-guide/{concepts => hardware-and-rosflight}/hardware-setup.md (96%) rename docs/user-guide/{concepts => hardware-and-rosflight}/improving-firmware-performance.md (100%) rename docs/user-guide/{concepts => hardware-and-rosflight}/parameter-configuration.md (100%) rename docs/user-guide/{concepts => hardware-and-rosflight}/preflight-checks.md (100%) rename docs/user-guide/{concepts => hardware-and-rosflight}/rc-configuration.md (100%) rename docs/user-guide/{concepts => rosflight-sim}/detailed-launching-guide.md (97%) rename docs/user-guide/{concepts => rosflight-sim}/running-simulations-with-rosflight.md (100%) rename docs/user-guide/{concepts => rosflight-sim}/simulator-architecture.md (99%) rename docs/user-guide/{concepts => rosplane}/rosplane-overview.md (100%) rename docs/user-guide/{concepts => rosplane}/rosplane-setup.md (100%) diff --git a/docs/user-guide/customizing-rosflight.md b/docs/user-guide/customizing-rosflight.md index bd65b3a9..75cc7a10 100644 --- a/docs/user-guide/customizing-rosflight.md +++ b/docs/user-guide/customizing-rosflight.md @@ -19,7 +19,7 @@ We also give some examples of how elements of ROSflight could be modified. Modules on the companion computer and modules on the FCU are designed differently, so the methods for customizing the ROSflight autonomy stacks and the ROSflight firmware are different. !!! warning "ROSflight architecture" - This page will not go over the [ROScopter](./roscopter/roscopter-overview.md)/[ROSplane](./concepts/rosplane-overview.md)/[ROSflight firmware](../developer-guide/firmware/code-architecture.md) architecture, but we do reference it. + This page will not go over the [ROScopter](./roscopter/roscopter-overview.md)/[ROSplane](./rosplane/rosplane-overview.md)/[ROSflight firmware](../developer-guide/firmware/code-architecture.md) architecture, but we do reference it. Please see the respective documentation for a description of each module before reading through this page. ## Customizing ROSflight firmware @@ -29,7 +29,7 @@ Most users of ROSflight will not need to customize the ROSflight firmware, since If you need to customize the ROSflight firmware, first consider if you can accomplish what you need on the companion computer. If you still need to customize the ROSflight firmware, make your changes and then follow the [building and flashing guide](../developer-guide/firmware/building-and-flashing.md) to flash it on your board. -However, since ROSflight currently supports only [limited hardware](./concepts/flight-controller-setup.md), users may need to write a new board-specific support package for the ROSflight firmware. +However, since ROSflight currently supports only [limited hardware](./hardware-and-rosflight/flight-controller-setup.md), users may need to write a new board-specific support package for the ROSflight firmware. The ROSflight firmware depends on [an abstraction of a physical board](../developer-guide/firmware/code-architecture.md). This means that supporting ROSflight on a new board does not require users to change the core ROSflight firmware code. Instead, users only have to write drivers for sensors and implement the board-specific functions in the interface file. diff --git a/docs/user-guide/concepts/autonomous-flight.md b/docs/user-guide/hardware-and-rosflight/autonomous-flight.md similarity index 99% rename from docs/user-guide/concepts/autonomous-flight.md rename to docs/user-guide/hardware-and-rosflight/autonomous-flight.md index f62699eb..cdb89b32 100644 --- a/docs/user-guide/concepts/autonomous-flight.md +++ b/docs/user-guide/hardware-and-rosflight/autonomous-flight.md @@ -1,6 +1,6 @@ # Autonomous Flight -To perform autonomous flight with ROSflight, we need to send control commands from our companion computer to the firmware. This can be done with ROSplane or ROScopter, which already have completed autonomy stacks developed specifically for ROSflight. We recommend starting with one of these autonomy stacks and building on them to suit your needs. If using a multirotor, follow the [ROScopter setup guide](../roscopter/roscopter-overview.md) to get started. If using a fixed-wing, follow the [ROSplane setup guide](rosplane-setup.md). +To perform autonomous flight with ROSflight, we need to send control commands from our companion computer to the firmware. This can be done with ROSplane or ROScopter, which already have completed autonomy stacks developed specifically for ROSflight. We recommend starting with one of these autonomy stacks and building on them to suit your needs. If using a multirotor, follow the [ROScopter setup guide](../roscopter/roscopter-overview.md) to get started. If using a fixed-wing, follow the [ROSplane setup guide](../rosplane/rosplane-setup.md). However, ROSplane and ROScopter are optional and an entirely different autonomy stack can be used if desired. To use a different autonomy stack, follow this guide. diff --git a/docs/user-guide/concepts/flight-controller-setup.md b/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md similarity index 100% rename from docs/user-guide/concepts/flight-controller-setup.md rename to docs/user-guide/hardware-and-rosflight/flight-controller-setup.md diff --git a/docs/user-guide/concepts/getting-started.md b/docs/user-guide/hardware-and-rosflight/getting-started.md similarity index 97% rename from docs/user-guide/concepts/getting-started.md rename to docs/user-guide/hardware-and-rosflight/getting-started.md index 9cf21632..d061e266 100644 --- a/docs/user-guide/concepts/getting-started.md +++ b/docs/user-guide/hardware-and-rosflight/getting-started.md @@ -25,7 +25,7 @@ The following checklists should help you get a new vehicle set up for the first 6. Set up your RC switches * If you want to arm/disarm using a switch, set the `ARM_CHANNEL` parameter to the appropriate channel (0-indexed) * If you want to use a switch to enable RC override, set the `RC_ATT_OVRD_CHN` and `RC_THR_OVRD_CHN` parameters to the appropriate channel(s) (0-indexed). If you want complete control (attitude and throttle) when you flip the switch, set both these parameters to the same channel. - 7. Calibrate your IMU: start `rosflight_io`, then run `ros2 service call /calibrate_imu std_srvs/srv/Trigger` + 7. Calibrate your IMU: Start [`rosflight_io`](../rosflight-io.md), then run `ros2 service call /calibrate_imu std_srvs/srv/Trigger` 8. Complete the multirotor-specific or fixed-wing-specific checklist below 9. Save the parameters (`ros2 service call /param_write std_srvs/srv/Trigger`) 10. You'll probably want to save a backup of your parameters (call `ros2 service call /param_save_to_file rosflight_msgs/srv/ParamFile "{filename: "params.yml"}"`) diff --git a/docs/user-guide/concepts/hardware-setup.md b/docs/user-guide/hardware-and-rosflight/hardware-setup.md similarity index 96% rename from docs/user-guide/concepts/hardware-setup.md rename to docs/user-guide/hardware-and-rosflight/hardware-setup.md index e1214993..0a5be411 100644 --- a/docs/user-guide/concepts/hardware-setup.md +++ b/docs/user-guide/hardware-and-rosflight/hardware-setup.md @@ -88,7 +88,7 @@ You will need a laptop which can run ROS2 to communicate with the MAV over the g ### Joystick -A joystick is used for [software-in-the-loop (SIL) simulations](./running-simulations-with-rosflight.md). The joystick is not technically a required component because it is possible to control your MAV from the command line, but it makes things much easier. Our first recommendation is to use the same transmitter you use for hardware as a joystick by plugging it into the computer via USB. We support Taranis QX7 transmitters, Radiomaster TX16s transmitters, RealFlight controllers, and XBOX controllers. Other joysticks can be used, but you may need to create custom axis and button mappings within the ROSflight joystick utility. +A joystick is used for [software-in-the-loop (SIL) simulations](../rosflight-sim/running-simulations-with-rosflight.md). The joystick is not technically a required component because it is possible to control your MAV from the command line, but it makes things much easier. Our first recommendation is to use the same transmitter you use for hardware as a joystick by plugging it into the computer via USB. We support Taranis QX7 transmitters, Radiomaster TX16s transmitters, RealFlight controllers, and XBOX controllers. Other joysticks can be used, but you may need to create custom axis and button mappings within the ROSflight joystick utility. !!! note "Physical vs firmware channels" If you do write your own mapping, remember that the channel numbers need to be configured properly on both the firmware and the transmitter. diff --git a/docs/user-guide/concepts/improving-firmware-performance.md b/docs/user-guide/hardware-and-rosflight/improving-firmware-performance.md similarity index 100% rename from docs/user-guide/concepts/improving-firmware-performance.md rename to docs/user-guide/hardware-and-rosflight/improving-firmware-performance.md diff --git a/docs/user-guide/concepts/parameter-configuration.md b/docs/user-guide/hardware-and-rosflight/parameter-configuration.md similarity index 100% rename from docs/user-guide/concepts/parameter-configuration.md rename to docs/user-guide/hardware-and-rosflight/parameter-configuration.md diff --git a/docs/user-guide/concepts/preflight-checks.md b/docs/user-guide/hardware-and-rosflight/preflight-checks.md similarity index 100% rename from docs/user-guide/concepts/preflight-checks.md rename to docs/user-guide/hardware-and-rosflight/preflight-checks.md diff --git a/docs/user-guide/concepts/rc-configuration.md b/docs/user-guide/hardware-and-rosflight/rc-configuration.md similarity index 100% rename from docs/user-guide/concepts/rc-configuration.md rename to docs/user-guide/hardware-and-rosflight/rc-configuration.md diff --git a/docs/user-guide/installation/installation-hardware.md b/docs/user-guide/installation/installation-hardware.md index af49ada0..637f3cd8 100644 --- a/docs/user-guide/installation/installation-hardware.md +++ b/docs/user-guide/installation/installation-hardware.md @@ -155,7 +155,7 @@ Do the following on your companion computer. ROS2 will not be installed on the flight controller unit (FCU). We will only need to build and flash the FCU with the `rosflight_firmware`. -See the [flight controller guide](../concepts/flight-controller-setup.md) for instructions on how to do this. +See the [flight controller guide](../hardware-and-rosflight/flight-controller-setup.md) for instructions on how to do this. ## Next Steps diff --git a/docs/user-guide/overview.md b/docs/user-guide/overview.md index 9f74f8ed..0fbac284 100644 --- a/docs/user-guide/overview.md +++ b/docs/user-guide/overview.md @@ -12,7 +12,7 @@ Do this by following the [installation for sim](./installation/installation-sim.md) guides, and then the [ROSflight tutorials](./tutorials/tutorial-overview.md). After you do that, you should be ready to start using ROSflight in your own research! - We recommend you first read through the architecture documentation for [ROSplane](./concepts/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md). + We recommend you first read through the architecture documentation for [ROSplane](./rosplane/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md). Then visit the [Customizing ROSflight page](./customizing-rosflight.md) for more specifics on how to customize ROSflight for your research. @@ -48,16 +48,16 @@ Although higher level control is offloaded to the companion computer, enough con [ROSplane](https://github.com/rosflight/rosplane) and [ROScopter](https://github.com/rosflight/roscopter) are ROS2 based autonomy stacks that run on the companion computer and do most of the heavy computation of the autopilot. Each portion of their autonomy stacks are organized into highly modular ROS nodes that can be easily swapped out with custom nodes. ROSplane and ROScopter are not required for using ROSflight and you could choose to use an entirely different autonomy stack if you so desired. -See the [ROSplane](./concepts/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md) documentation for a detailed description of the respective architectures and functionality. +See the [ROSplane](./rosplane/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md) documentation for a detailed description of the respective architectures and functionality. ### RC Safety Pilot ROSflight is designed for use with offboard control from experimental and research code. As such, it provides several mechanisms for an RC safety pilot to intervene if something goes wrong with the control setpoints coming from the companion computer. -See the [RC Setup](./concepts/rc-configuration.md) page for more information. +See the [RC Setup](./hardware-and-rosflight/rc-configuration.md) page for more information. ## Where do I start? To get started with ROSflight, we recommend you first set up the simulation environment and walk through the tutorials to get familiar with the ROSflight ecosystem. -If you are ready to start with hardware experiments, first [install the required software](./installation/installation-hardware.md) and then follow the [hardware setup guide](./concepts/getting-started.md) to configure your vehicle for successful flight tests. +If you are ready to start with hardware experiments, first [install the required software](./installation/installation-hardware.md) and then follow the [hardware setup guide](./hardware-and-rosflight/getting-started.md) to configure your vehicle for successful flight tests. diff --git a/docs/user-guide/concepts/detailed-launching-guide.md b/docs/user-guide/rosflight-sim/detailed-launching-guide.md similarity index 97% rename from docs/user-guide/concepts/detailed-launching-guide.md rename to docs/user-guide/rosflight-sim/detailed-launching-guide.md index d2fd28a1..e91543d6 100644 --- a/docs/user-guide/concepts/detailed-launching-guide.md +++ b/docs/user-guide/rosflight-sim/detailed-launching-guide.md @@ -173,7 +173,7 @@ These command line arguments should be passed using the `:=` sy ## Joysticks ROSflight supports several types of transmitters or controllers that you can use to fly around in the sim as the RC safety pilot. If one of the supported transmitters is connected via USB at launch time, then the sim will default to using that controller instead of the default, **which is no RC connection**. -See the [Hardware Setup](./hardware-setup.md#joystick) guide for more information on joysticks. +See the [Hardware Setup](../hardware-and-rosflight/hardware-setup.md#joystick) guide for more information on joysticks. !!! note It is much easier to fly with a real transmitter than with an Xbox-type controller. @@ -196,7 +196,7 @@ Remember that the SIL tries its best to replicate hardware. That means you have to calibrate and set parameters in the same way you do in hardware. If you need a reminder, please follow the [configuration and manual flight tutorial](../tutorials/manually-flying-rosflight-sim.md). -See the [Parameter Configuration](./parameter-configuration.md) pages in this documentation for instructions on how to perform all preflight configuration before the aircraft will arm. +See the [Parameter Configuration](../hardware-and-rosflight/parameter-configuration.md) pages in this documentation for instructions on how to perform all preflight configuration before the aircraft will arm. You can also run ```bash diff --git a/docs/user-guide/concepts/running-simulations-with-rosflight.md b/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md similarity index 100% rename from docs/user-guide/concepts/running-simulations-with-rosflight.md rename to docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md diff --git a/docs/user-guide/concepts/simulator-architecture.md b/docs/user-guide/rosflight-sim/simulator-architecture.md similarity index 99% rename from docs/user-guide/concepts/simulator-architecture.md rename to docs/user-guide/rosflight-sim/simulator-architecture.md index 94e9a9c8..f1b68965 100644 --- a/docs/user-guide/concepts/simulator-architecture.md +++ b/docs/user-guide/rosflight-sim/simulator-architecture.md @@ -254,7 +254,7 @@ If you are adding a new sensor (e.g. camera), you could either change the `senso The `rc` module is responsible for publishing RC commands to the `sil_board`. It takes the place of the physical RC receiver in hardware that typically communicates with the flight controller over SBUS or PPM. -As described in the [ROSflight tutorials](../tutorials/manually-flying-rosflight-sim.md#rc-transmitter-control) and [hardware concept pages](./hardware-setup.md#joystick) pages, the `rc` node supports using a physical joystick or a simulated joystick like VimFly. +As described in the [ROSflight tutorials](../tutorials/manually-flying-rosflight-sim.md#rc-transmitter-control) and [hardware concept pages](../hardware-and-rosflight/hardware-setup.md#joystick) pages, the `rc` node supports using a physical joystick or a simulated joystick like VimFly. See the linked documents for more information. If VimFly is not specified and a physical transmitter is not connected when the simulation is launched, it will default to **no direct RC control**. diff --git a/docs/user-guide/concepts/rosplane-overview.md b/docs/user-guide/rosplane/rosplane-overview.md similarity index 100% rename from docs/user-guide/concepts/rosplane-overview.md rename to docs/user-guide/rosplane/rosplane-overview.md diff --git a/docs/user-guide/concepts/rosplane-setup.md b/docs/user-guide/rosplane/rosplane-setup.md similarity index 100% rename from docs/user-guide/concepts/rosplane-setup.md rename to docs/user-guide/rosplane/rosplane-setup.md diff --git a/docs/user-guide/tutorials/manually-flying-rosflight-sim.md b/docs/user-guide/tutorials/manually-flying-rosflight-sim.md index 5b4a8579..e15b6b54 100644 --- a/docs/user-guide/tutorials/manually-flying-rosflight-sim.md +++ b/docs/user-guide/tutorials/manually-flying-rosflight-sim.md @@ -32,7 +32,7 @@ ros2 node list and verifying that the `rosflight_io` and `sil_board` nodes are included in the list. ### Loading parameters manually -You can load parameters one-by-one or with a YAML file, as described in the [parameter configuration guide](../concepts/parameter-configuration.md). +You can load parameters one-by-one or with a YAML file, as described in the [parameter configuration guide](../hardware-and-rosflight/parameter-configuration.md). We will load parameters from a file. 1. Navigate to the params directory: @@ -53,7 +53,7 @@ Note that we first navigated to the directory so we could use the built-in `pwd` It just saves time instead of having to type the full path to the file. Here are some of the parameters you just loaded. -For a full list of the firmware's parameters, see [the parameter list](../concepts/parameter-configuration.md). +For a full list of the firmware's parameters, see [the parameter list](../hardware-and-rosflight/parameter-configuration.md). - **Aircraft configuration**: Sets `FIXED_WING: 0` for multirotor operation - **RC channels**: configures 8 RC channels with appropriate mappings @@ -231,7 +231,7 @@ ROSflight supports several types of transmitters or controllers that you can use - **RealFlight InterLink Controller** If one of the supported transmitters is connected via USB **at launch time**, then the sim will default to using that controller instead of the default, **which is no RC connection**. -See the [Hardware Setup](../concepts/hardware-setup.md#joystick) guide for more information on joysticks. +See the [Hardware Setup](../hardware-and-rosflight/hardware-setup.md#joystick) guide for more information on joysticks. !!! note "Have a joystick not on the list?" Joysticks not on the above list may have incorrect mappings. @@ -315,5 +315,5 @@ Once you have the simulator running, you can: ### Additional Resources -- [ROSflight Parameter Reference](../concepts/parameter-configuration.md): Detailed parameter descriptions -- [Hardware Setup Guide](../concepts/hardware-setup.md): Preparing real hardware for flight +- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed parameter descriptions +- [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight diff --git a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md index e2ddb3e4..efc904b3 100644 --- a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md +++ b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md @@ -371,7 +371,7 @@ Once you have ROScopter running autonomously, you can: ### Additional Resources -- [ROSflight Parameter Reference](../concepts/parameter-configuration.md): Detailed firmware parameter descriptions -- [Hardware Setup Guide](../concepts/hardware-setup.md): Preparing real hardware for flight +- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed firmware parameter descriptions +- [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight - [ROScopter Architecture Documentation](../roscopter/roscopter-overview.md): In-depth system design and implementation details diff --git a/docs/user-guide/tutorials/setting-up-rosflight-sim.md b/docs/user-guide/tutorials/setting-up-rosflight-sim.md index a8877f9d..67380f13 100644 --- a/docs/user-guide/tutorials/setting-up-rosflight-sim.md +++ b/docs/user-guide/tutorials/setting-up-rosflight-sim.md @@ -130,7 +130,7 @@ You should see the following output: Note that the `static_transform_publisher` nodes will have different hashes than what is shown above. Each of these nodes performs a different role in the sim. -Detailed information about these nodes and what they do can be found in the [simulation architecture description](../concepts/simulator-architecture.md). +Detailed information about these nodes and what they do can be found in the [simulation architecture description](../rosflight-sim/simulator-architecture.md). You can also see a representation of the data flow by running `rqt_graph` in a new terminal. If you don't see a similar view to what is below, click the refresh icon in the upper left. @@ -152,7 +152,7 @@ The `/dynamics` node handles gravity and other collision forces. This concludes a simulation "tick", and the simulation starts again with the `/sil_board`. -For more information, see the [detailed simulation architecture description](../concepts/simulator-architecture.md). +For more information, see the [detailed simulation architecture description](../rosflight-sim/simulator-architecture.md). ### Topics diff --git a/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md b/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md index eb77e07c..7aa2948c 100644 --- a/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md +++ b/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md @@ -324,7 +324,7 @@ Once you have ROSplane running autonomously, you can: ### Additional Resources -- [ROSflight Parameter Reference](../concepts/parameter-configuration.md): Detailed firmware parameter descriptions -- [Hardware Setup Guide](../concepts/hardware-setup.md): Preparing real hardware for flight +- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed firmware parameter descriptions +- [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight - [ROSplane Architecture Documentation](../../developer-guide/rosplane/rosplane-dev-overview.md): In-depth system design and implementation details diff --git a/docs/user-guide/tutorials/tuning-performance-in-sim.md b/docs/user-guide/tutorials/tuning-performance-in-sim.md index 6273cdca..134de8ab 100644 --- a/docs/user-guide/tutorials/tuning-performance-in-sim.md +++ b/docs/user-guide/tutorials/tuning-performance-in-sim.md @@ -21,7 +21,7 @@ This tutorial will walk you through: - **Introduce the tools** that we use when flying with ROSflight, and - Help you get a feel for an **autonomy stack architecture** - See the [improving firmware performance](../concepts/improving-firmware-performance.md) guide for more information on tuning the firmware controller and firmware estimator. + See the [improving firmware performance](../hardware-and-rosflight/improving-firmware-performance.md) guide for more information on tuning the firmware controller and firmware estimator. ## Prerequisites @@ -196,7 +196,7 @@ To do this, You should see the gains show up on the right as shown below. These are the firmware gains that we have exposed to `rosflight_io`. -This means that when we change the `rosflight_io` ROS2 parameters (which have the same name as the firmware parameters), the `rosflight_io` node will essentially call the `param_set` service as described in the [parameter configuration guide](../concepts/parameter-configuration.md). +This means that when we change the `rosflight_io` ROS2 parameters (which have the same name as the firmware parameters), the `rosflight_io` node will essentially call the `param_set` service as described in the [parameter configuration guide](../hardware-and-rosflight/parameter-configuration.md). ![ROSflightIO gains on RQT](../images/rosflight_io_rqt.png) @@ -272,7 +272,7 @@ Here's an example of a somewhat-tuned response: Note that I also added the estimated state to the plot as well. The orange line is the firmware's estimated roll angle, and it tracks the command decently. However, it has a noticeable difference between truth. -This means we should either [tune the firmware response](../concepts/improving-firmware-performance.md), or publish ROScopter's estimated state to the `external_attitude` topic. +This means we should either [tune the firmware response](../hardware-and-rosflight/improving-firmware-performance.md), or publish ROScopter's estimated state to the `external_attitude` topic. !!! tip "External Attitude" @@ -409,5 +409,5 @@ You should now be able to: ### Additional Resources -- [Parameter Reference](../concepts/parameter-configuration.md): Complete parameter documentation -- [Hardware Tuning Guide](../concepts/improving-firmware-performance.md): Considerations for real hardware +- [Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Complete parameter documentation +- [Hardware Tuning Guide](../hardware-and-rosflight/improving-firmware-performance.md): Considerations for real hardware diff --git a/mkdocs.yml b/mkdocs.yml index f4b90f6e..6ede54b7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -73,12 +73,12 @@ nav: - Appendix: user-guide/tutorials/user-manual-appendix.md - ROSflightIO: user-guide/rosflight-io.md - ROSflight Sim: - - ROSflight Sim Overview: user-guide/concepts/running-simulations-with-rosflight.md - - Detailed Launching Guide: user-guide/concepts/detailed-launching-guide.md - - Simulator Architecture: user-guide/concepts/simulator-architecture.md + - ROSflight Sim Overview: user-guide/rosflight-sim/running-simulations-with-rosflight.md + - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md + - Simulator Architecture: user-guide/rosflight-sim/simulator-architecture.md - ROSplane: - - ROSplane Overview: user-guide/concepts/rosplane-overview.md - - ROSplane Setup: user-guide/concepts/rosplane-setup.md + - ROSplane Overview: user-guide/rosplane/rosplane-overview.md + - ROSplane Setup: user-guide/rosplane/rosplane-setup.md - ROScopter: - ROScopter Overview: user-guide/roscopter/roscopter-overview.md - ROScopter Path Planner: user-guide/roscopter/roscopter-path-planner.md @@ -87,14 +87,14 @@ nav: - ROScopter Controller: user-guide/roscopter/roscopter-controller.md - ROScopter Estimator: user-guide/roscopter/roscopter-estimator.md - Hardware and ROSflight: - - Getting Started: user-guide/concepts/getting-started.md - - Hardware Setup: user-guide/concepts/hardware-setup.md - - Flight Controller Setup: user-guide/concepts/flight-controller-setup.md - - RC Configuration: user-guide/concepts/rc-configuration.md - - Parameter Configuration: user-guide/concepts/parameter-configuration.md - - Pre-Flight Checks: user-guide/concepts/preflight-checks.md - - Improving Firmware Performance: user-guide/concepts/improving-firmware-performance.md - - Autonomous Flight: user-guide/concepts/autonomous-flight.md + - Getting Started: user-guide/hardware-and-rosflight/getting-started.md + - Hardware Setup: user-guide/hardware-and-rosflight/hardware-setup.md + - Flight Controller Setup: user-guide/hardware-and-rosflight/flight-controller-setup.md + - RC Configuration: user-guide/hardware-and-rosflight/rc-configuration.md + - Parameter Configuration: user-guide/hardware-and-rosflight/parameter-configuration.md + - Pre-Flight Checks: user-guide/hardware-and-rosflight/preflight-checks.md + - Improving Firmware Performance: user-guide/hardware-and-rosflight/improving-firmware-performance.md + - Autonomous Flight: user-guide/hardware-and-rosflight/autonomous-flight.md - Customizing ROSflight: user-guide/customizing-rosflight.md - Developer Guide: - Contribution Guidelines: developer-guide/contribution-guidelines.md @@ -123,11 +123,6 @@ nav: - Path Planner: developer-guide/rosplane/navigation/path-planner.md - Path Manager: developer-guide/rosplane/navigation/path-manager.md - Path Follower: developer-guide/rosplane/navigation/path-follower.md - - ROScopter: - - Developer Guide Overview: developer-guide/roscopter/roscopter-dev-overview.md - Publications: - ROSplane: publications/rosplane.md - ROSflight: publications/rosflight.md -# - ROS Packages: -# - ROSplane: -# - ROScopter: From 3b47c31938a9eecc733abaf95c79ee003e346b3b Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Tue, 6 Jan 2026 14:50:51 -0700 Subject: [PATCH 3/9] cleanup: move rosflight_firmware architecture and flashing instructions to user guide --- docs/developer-guide/firmware/debugging.md | 2 +- docs/user-guide/customizing-rosflight.md | 6 +++--- .../flight-controller-setup.md | 4 ++-- .../firmware => user-guide}/images/HAL.svg | 0 .../firmware => user-guide}/images/arming-fsm.svg | 0 .../firmware => user-guide}/images/flight_stack.svg | 0 .../images/pixracer_pro_flash_instructions.jpeg | Bin .../images/stm32_programming_page.png | Bin .../images/stm32_select_hex.png | Bin .../images/stm32_select_options.png | Bin .../images/stm_programmer.png | Bin .../images/stm_programmer_connect.png | Bin .../images/varmint_flash_instructions.png | Bin .../rosflight-firmware}/building-and-flashing.md | 12 ++++++------ .../rosflight-firmware}/code-architecture.md | 6 +++--- mkdocs.yml | 5 +++-- 16 files changed, 18 insertions(+), 17 deletions(-) rename docs/{developer-guide/firmware => user-guide}/images/HAL.svg (100%) rename docs/{developer-guide/firmware => user-guide}/images/arming-fsm.svg (100%) rename docs/{developer-guide/firmware => user-guide}/images/flight_stack.svg (100%) rename docs/{developer-guide/firmware => user-guide}/images/pixracer_pro_flash_instructions.jpeg (100%) rename docs/{developer-guide/firmware => user-guide}/images/stm32_programming_page.png (100%) rename docs/{developer-guide/firmware => user-guide}/images/stm32_select_hex.png (100%) rename docs/{developer-guide/firmware => user-guide}/images/stm32_select_options.png (100%) rename docs/{developer-guide/firmware => user-guide}/images/stm_programmer.png (100%) rename docs/{developer-guide/firmware => user-guide}/images/stm_programmer_connect.png (100%) rename docs/{developer-guide/firmware => user-guide}/images/varmint_flash_instructions.png (100%) rename docs/{developer-guide/firmware => user-guide/rosflight-firmware}/building-and-flashing.md (88%) rename docs/{developer-guide/firmware => user-guide/rosflight-firmware}/code-architecture.md (98%) diff --git a/docs/developer-guide/firmware/debugging.md b/docs/developer-guide/firmware/debugging.md index d5ad04c0..2b8dbaa7 100644 --- a/docs/developer-guide/firmware/debugging.md +++ b/docs/developer-guide/firmware/debugging.md @@ -12,7 +12,7 @@ This guide assumes you are running Ubuntu 22.04 LTS, which is the currently supp ## General Setup -Follow the guide in [Building and Flashing](building-and-flashing.md) to install the compiler toolchain. +Follow the guide in [Building and Flashing](../../user-guide/rosflight-firmware/building-and-flashing.md) to install the compiler toolchain. Also make sure you have configured your computer as described in the [Serial Port Configuration](../../user-guide/hardware-and-rosflight/flight-controller-setup.md#serial-port-configuration) section of the user guide. diff --git a/docs/user-guide/customizing-rosflight.md b/docs/user-guide/customizing-rosflight.md index 75cc7a10..20b815b4 100644 --- a/docs/user-guide/customizing-rosflight.md +++ b/docs/user-guide/customizing-rosflight.md @@ -19,7 +19,7 @@ We also give some examples of how elements of ROSflight could be modified. Modules on the companion computer and modules on the FCU are designed differently, so the methods for customizing the ROSflight autonomy stacks and the ROSflight firmware are different. !!! warning "ROSflight architecture" - This page will not go over the [ROScopter](./roscopter/roscopter-overview.md)/[ROSplane](./rosplane/rosplane-overview.md)/[ROSflight firmware](../developer-guide/firmware/code-architecture.md) architecture, but we do reference it. + This page will not go over the [ROScopter](./roscopter/roscopter-overview.md)/[ROSplane](./rosplane/rosplane-overview.md)/[ROSflight firmware](rosflight-firmware/code-architecture.md) architecture, but we do reference it. Please see the respective documentation for a description of each module before reading through this page. ## Customizing ROSflight firmware @@ -27,10 +27,10 @@ The core [ROSflight firmware](./overview.md) resides on an embedded microcontrol Most users of ROSflight will not need to customize the ROSflight firmware, since the vast majority of the autonomy stack is on the companion computer. If you need to customize the ROSflight firmware, first consider if you can accomplish what you need on the companion computer. -If you still need to customize the ROSflight firmware, make your changes and then follow the [building and flashing guide](../developer-guide/firmware/building-and-flashing.md) to flash it on your board. +If you still need to customize the ROSflight firmware, make your changes and then follow the [building and flashing guide](rosflight-firmware/building-and-flashing.md) to flash it on your board. However, since ROSflight currently supports only [limited hardware](./hardware-and-rosflight/flight-controller-setup.md), users may need to write a new board-specific support package for the ROSflight firmware. -The ROSflight firmware depends on [an abstraction of a physical board](../developer-guide/firmware/code-architecture.md). +The ROSflight firmware depends on [an abstraction of a physical board](rosflight-firmware/code-architecture.md). This means that supporting ROSflight on a new board does not require users to change the core ROSflight firmware code. Instead, users only have to write drivers for sensors and implement the board-specific functions in the interface file. diff --git a/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md b/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md index 244a5db9..1fdad96c 100644 --- a/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md +++ b/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md @@ -2,7 +2,7 @@ !!! Note This page contains instructions for flashing pre-built firmware binaries. - For instructions on building and flashing from source, see [Building and Flashing](../../developer-guide/firmware/building-and-flashing.md) in the Developer Guide. + For instructions on building and flashing from source, see [Building and Flashing](../rosflight-firmware/building-and-flashing.md) in the Developer Guide. ## Compatible Hardware @@ -55,7 +55,7 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0664" ## Building and Flashing the Firmware -Follow the [building and flashing firmware guide](../../developer-guide/firmware/building-and-flashing.md) to build the ROSflight firmware and flash it to your flight controller. +Follow the [building and flashing firmware guide](../rosflight-firmware/building-and-flashing.md) to build the ROSflight firmware and flash it to your flight controller. ## LEDs diff --git a/docs/developer-guide/firmware/images/HAL.svg b/docs/user-guide/images/HAL.svg similarity index 100% rename from docs/developer-guide/firmware/images/HAL.svg rename to docs/user-guide/images/HAL.svg diff --git a/docs/developer-guide/firmware/images/arming-fsm.svg b/docs/user-guide/images/arming-fsm.svg similarity index 100% rename from docs/developer-guide/firmware/images/arming-fsm.svg rename to docs/user-guide/images/arming-fsm.svg diff --git a/docs/developer-guide/firmware/images/flight_stack.svg b/docs/user-guide/images/flight_stack.svg similarity index 100% rename from docs/developer-guide/firmware/images/flight_stack.svg rename to docs/user-guide/images/flight_stack.svg diff --git a/docs/developer-guide/firmware/images/pixracer_pro_flash_instructions.jpeg b/docs/user-guide/images/pixracer_pro_flash_instructions.jpeg similarity index 100% rename from docs/developer-guide/firmware/images/pixracer_pro_flash_instructions.jpeg rename to docs/user-guide/images/pixracer_pro_flash_instructions.jpeg diff --git a/docs/developer-guide/firmware/images/stm32_programming_page.png b/docs/user-guide/images/stm32_programming_page.png similarity index 100% rename from docs/developer-guide/firmware/images/stm32_programming_page.png rename to docs/user-guide/images/stm32_programming_page.png diff --git a/docs/developer-guide/firmware/images/stm32_select_hex.png b/docs/user-guide/images/stm32_select_hex.png similarity index 100% rename from docs/developer-guide/firmware/images/stm32_select_hex.png rename to docs/user-guide/images/stm32_select_hex.png diff --git a/docs/developer-guide/firmware/images/stm32_select_options.png b/docs/user-guide/images/stm32_select_options.png similarity index 100% rename from docs/developer-guide/firmware/images/stm32_select_options.png rename to docs/user-guide/images/stm32_select_options.png diff --git a/docs/developer-guide/firmware/images/stm_programmer.png b/docs/user-guide/images/stm_programmer.png similarity index 100% rename from docs/developer-guide/firmware/images/stm_programmer.png rename to docs/user-guide/images/stm_programmer.png diff --git a/docs/developer-guide/firmware/images/stm_programmer_connect.png b/docs/user-guide/images/stm_programmer_connect.png similarity index 100% rename from docs/developer-guide/firmware/images/stm_programmer_connect.png rename to docs/user-guide/images/stm_programmer_connect.png diff --git a/docs/developer-guide/firmware/images/varmint_flash_instructions.png b/docs/user-guide/images/varmint_flash_instructions.png similarity index 100% rename from docs/developer-guide/firmware/images/varmint_flash_instructions.png rename to docs/user-guide/images/varmint_flash_instructions.png diff --git a/docs/developer-guide/firmware/building-and-flashing.md b/docs/user-guide/rosflight-firmware/building-and-flashing.md similarity index 88% rename from docs/developer-guide/firmware/building-and-flashing.md rename to docs/user-guide/rosflight-firmware/building-and-flashing.md index 40ed72b6..488db0ae 100644 --- a/docs/developer-guide/firmware/building-and-flashing.md +++ b/docs/user-guide/rosflight-firmware/building-and-flashing.md @@ -51,26 +51,26 @@ We use the STM32CubeProgrammer to flash the flight controller. The Varmint has 2 6-pin connectors. **Do not** connect the ribbon cable to the port closest to the power wires. - ![Varmint 6-pin cable](images/varmint_flash_instructions.png) + ![Varmint 6-pin cable](../images/varmint_flash_instructions.png) 1. Power on the Varmint by connecting a battery to the board. 1. Open STM32CubeProgrammer. 1. Plug in the USB connector from the ST-Link to the computer. Select "Connect" in the STM32CubeProgrammer. This should detect the ST-Link and connect automatically. - ![Select "Connect"](images/stm_programmer_connect.png) + ![Select "Connect"](../images/stm_programmer_connect.png) 1. Navigate to the programming page. - ![Navigate to programming page](images/stm32_programming_page.png) + ![Navigate to programming page](../images/stm32_programming_page.png) 1. Select the hex file that was just built and click "Open". ``` /path/to/rosflight_firmware/build/boards/varmint_h7/varmint_10X/varmint10X.hex ``` - ![Select the previously built hex file](images/stm32_select_hex.png) + ![Select the previously built hex file](../images/stm32_select_hex.png) 1. Select the appropriate options and press "Start Programming" - ![Select options and start programming](images/stm32_select_options.png) + ![Select options and start programming](../images/stm32_select_options.png) ### Flashing the Pixracer Pro @@ -87,7 +87,7 @@ Flashing the Pixracer Pro is a very similar process to flashing the Varmint. 2. [Programming cable](https://www.tag-connect.com/product/tc2030-idc-nl) that connects to the adapter board 3. [TC2030 clip](https://www.tag-connect.com/product/tc2030-retaining-clip-board-3-pack) to attach the programming cable to the board - ![Connectors for the Pixracer Pro](./images/pixracer_pro_flash_instructions.jpeg) + ![Connectors for the Pixracer Pro](./../images/pixracer_pro_flash_instructions.jpeg) 1. Power on the Pixracer Pro using a USB-C port. diff --git a/docs/developer-guide/firmware/code-architecture.md b/docs/user-guide/rosflight-firmware/code-architecture.md similarity index 98% rename from docs/developer-guide/firmware/code-architecture.md rename to docs/user-guide/rosflight-firmware/code-architecture.md index 0d601bcb..312ce97e 100644 --- a/docs/developer-guide/firmware/code-architecture.md +++ b/docs/user-guide/rosflight-firmware/code-architecture.md @@ -4,7 +4,7 @@ The firmware is divided into two main components: the _core library_, and a coll This division is intended to allow the same core flight code to run on any processor or platform, either an embedded flight controller or a desktop environment for a software-in-the-loop (SIL) simulation. The interface between these two components is called the _hardware abstraction layer_ (HAL). This architecture is illustrated in the following diagram: -![hardware abstraction layer](images/HAL.svg) +![hardware abstraction layer](../images/HAL.svg) ## Firmware Core Library @@ -42,7 +42,7 @@ Each of these modules is implemented as a C++ class, and encapsulates a cohesive The following diagram illustrates these modules and the data flow between them. Rectangular blocks represent modules in the flight stack, and ellipses represent hardware functionality implemented in the board support layer: -![flight stack](images/flight_stack.svg) +![flight stack](../images/flight_stack.svg) We'll describe each of these modules in the following sections: @@ -52,7 +52,7 @@ While only the comm manager data flow is illustrated on the diagram, all other m The operation of the state manager is defined by the following finite state machine: -![state manager FSM](images/arming-fsm.svg) +![state manager FSM](../images/arming-fsm.svg) The state manager also includes functionality for recovering from hard faults. In the case of a hard fault, the firmware writes a small amount of data to backup memory then reboots. This backup memory location is checked and then cleared after every reboot. The backup memory includes the armed state of the flight controller. On reboot, the firmware will initialize then, if this armed-state flag is set, immediately transition back into the armed state. This functionality allows for continued RC control in the case of a hard fault. Hard faults are not expected with the stable firmware code base, but this feature adds an additional layer of safety if experimental changes are being made to the firmware itself. diff --git a/mkdocs.yml b/mkdocs.yml index 6ede54b7..791f8ca9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,9 @@ nav: - ROSflight Integration: user-guide/tutorials/user-manual-ROSflight-integration.md - Appendix: user-guide/tutorials/user-manual-appendix.md - ROSflightIO: user-guide/rosflight-io.md + - ROSflight Firmware: + - Code Architecture: user-guide/rosflight-firmware/code-architecture.md + - Building and Flashing: user-guide/rosflight-firmware/building-and-flashing.md - ROSflight Sim: - ROSflight Sim Overview: user-guide/rosflight-sim/running-simulations-with-rosflight.md - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md @@ -100,8 +103,6 @@ nav: - Contribution Guidelines: developer-guide/contribution-guidelines.md - Style Guide: developer-guide/style-guide.md - Firmware: - - Code Architecture: developer-guide/firmware/code-architecture.md - - Building and Flashing: developer-guide/firmware/building-and-flashing.md - Unit Tests: developer-guide/firmware/unit-tests.md - Debugging: developer-guide/firmware/debugging.md - ROSplane: From 5dc810f5078472cca37a7e02abb5a92fbb5e48a1 Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Thu, 18 Dec 2025 16:37:42 -0700 Subject: [PATCH 4/9] cleanup: move rosplane documents to rosplane folder --- docs/stylesheets/extra.css | 3 +++ docs/user-guide/customizing-rosflight.md | 2 +- .../hardware-and-rosflight/autonomous-flight.md | 6 +++++- .../installation/installation-hardware.md | 2 +- docs/user-guide/overview.md | 6 +++--- .../roscopter/{roscopter-overview.md => index.md} | 6 +++--- .../rosflight-sim/detailed-launching-guide.md | 2 +- .../running-simulations-with-rosflight.md | 2 +- .../rosplane/{rosplane-overview.md => index.md} | 10 +++++++++- docs/user-guide/rosplane/rosplane-setup.md | 2 +- .../tutorials/{tutorial-overview.md => index.md} | 0 .../tutorials/setting-up-roscopter-in-sim.md | 3 +-- mkdocs.yml | 14 +++++++++----- 13 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 docs/stylesheets/extra.css rename docs/user-guide/roscopter/{roscopter-overview.md => index.md} (91%) rename docs/user-guide/rosplane/{rosplane-overview.md => index.md} (84%) rename docs/user-guide/tutorials/{tutorial-overview.md => index.md} (100%) diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 00000000..ca42bcab --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,3 @@ +.md-grid { + max-width: 1440px; +} diff --git a/docs/user-guide/customizing-rosflight.md b/docs/user-guide/customizing-rosflight.md index 20b815b4..be74f7c1 100644 --- a/docs/user-guide/customizing-rosflight.md +++ b/docs/user-guide/customizing-rosflight.md @@ -19,7 +19,7 @@ We also give some examples of how elements of ROSflight could be modified. Modules on the companion computer and modules on the FCU are designed differently, so the methods for customizing the ROSflight autonomy stacks and the ROSflight firmware are different. !!! warning "ROSflight architecture" - This page will not go over the [ROScopter](./roscopter/roscopter-overview.md)/[ROSplane](./rosplane/rosplane-overview.md)/[ROSflight firmware](rosflight-firmware/code-architecture.md) architecture, but we do reference it. + This page will not go over the [ROScopter](./roscopter/index.md)/[ROSplane](./rosplane/index.md)/[ROSflight firmware](rosflight-firmware/code-architecture.md) architecture, but we do reference it. Please see the respective documentation for a description of each module before reading through this page. ## Customizing ROSflight firmware diff --git a/docs/user-guide/hardware-and-rosflight/autonomous-flight.md b/docs/user-guide/hardware-and-rosflight/autonomous-flight.md index cdb89b32..724f9432 100644 --- a/docs/user-guide/hardware-and-rosflight/autonomous-flight.md +++ b/docs/user-guide/hardware-and-rosflight/autonomous-flight.md @@ -1,6 +1,10 @@ # Autonomous Flight -To perform autonomous flight with ROSflight, we need to send control commands from our companion computer to the firmware. This can be done with ROSplane or ROScopter, which already have completed autonomy stacks developed specifically for ROSflight. We recommend starting with one of these autonomy stacks and building on them to suit your needs. If using a multirotor, follow the [ROScopter setup guide](../roscopter/roscopter-overview.md) to get started. If using a fixed-wing, follow the [ROSplane setup guide](../rosplane/rosplane-setup.md). +To perform autonomous flight with ROSflight, we need to send control commands from our companion computer to the firmware. +This can be done with ROSplane or ROScopter, which already have completed autonomy stacks developed specifically for ROSflight. +We recommend starting with one of these autonomy stacks and building on them to suit your needs. +If using a multirotor, follow the [ROScopter setup guide](../roscopter/index.md) to get started. +If using a fixed-wing, follow the [ROSplane setup guide](../rosplane/rosplane-setup.md). However, ROSplane and ROScopter are optional and an entirely different autonomy stack can be used if desired. To use a different autonomy stack, follow this guide. diff --git a/docs/user-guide/installation/installation-hardware.md b/docs/user-guide/installation/installation-hardware.md index 637f3cd8..4303f2bf 100644 --- a/docs/user-guide/installation/installation-hardware.md +++ b/docs/user-guide/installation/installation-hardware.md @@ -161,4 +161,4 @@ See the [flight controller guide](../hardware-and-rosflight/flight-controller-se At this point, you have successfully installed ROS2 and set up ROSflight on the companion computer, including all required dependencies. -For guidance on running simulations, see the [tutorials](../tutorials/tutorial-overview.md). +For guidance on running simulations, see the [tutorials](../tutorials/index.md). diff --git a/docs/user-guide/overview.md b/docs/user-guide/overview.md index 0fbac284..92c05475 100644 --- a/docs/user-guide/overview.md +++ b/docs/user-guide/overview.md @@ -9,10 +9,10 @@ !!! tip "New to ROSflight?" If you are new to ROSflight, we recommend that you first start by setting up the simulation environment and learning to use the ROSflight ecosystem. - Do this by following the [installation for sim](./installation/installation-sim.md) guides, and then the [ROSflight tutorials](./tutorials/tutorial-overview.md). + Do this by following the [installation for sim](./installation/installation-sim.md) guides, and then the [ROSflight tutorials](./tutorials/index.md). After you do that, you should be ready to start using ROSflight in your own research! - We recommend you first read through the architecture documentation for [ROSplane](./rosplane/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md). + We recommend you first read through the architecture documentation for [ROSplane](./rosplane/index.md) and [ROScopter](./roscopter/index.md). Then visit the [Customizing ROSflight page](./customizing-rosflight.md) for more specifics on how to customize ROSflight for your research. @@ -48,7 +48,7 @@ Although higher level control is offloaded to the companion computer, enough con [ROSplane](https://github.com/rosflight/rosplane) and [ROScopter](https://github.com/rosflight/roscopter) are ROS2 based autonomy stacks that run on the companion computer and do most of the heavy computation of the autopilot. Each portion of their autonomy stacks are organized into highly modular ROS nodes that can be easily swapped out with custom nodes. ROSplane and ROScopter are not required for using ROSflight and you could choose to use an entirely different autonomy stack if you so desired. -See the [ROSplane](./rosplane/rosplane-overview.md) and [ROScopter](./roscopter/roscopter-overview.md) documentation for a detailed description of the respective architectures and functionality. +See the [ROSplane](./rosplane/index.md) and [ROScopter](./roscopter/index.md) documentation for a detailed description of the respective architectures and functionality. ### RC Safety Pilot diff --git a/docs/user-guide/roscopter/roscopter-overview.md b/docs/user-guide/roscopter/index.md similarity index 91% rename from docs/user-guide/roscopter/roscopter-overview.md rename to docs/user-guide/roscopter/index.md index 4b8073d7..05cf199f 100644 --- a/docs/user-guide/roscopter/roscopter-overview.md +++ b/docs/user-guide/roscopter/index.md @@ -4,7 +4,7 @@ Ready to get fly your code with ROScopter? Start here: - 1. Follow [the ROSflight tutorials](../tutorials/tutorial-overview.md) to set up ROScopter fly waypoint missions in sim. + 1. Follow [the ROSflight tutorials](../tutorials/index.md) to set up ROScopter fly waypoint missions in sim. 2. Read through [the following ROScopter architecture pages](./roscopter-path-planner.md) to understand the responsibilities of each node. 3. Find examples of how to customize ROScopter in the [Customizing ROSflight](../customizing-rosflight.md) page. @@ -40,7 +40,7 @@ See [the ROScopter autonomy stack documentation pages](./roscopter-path-planner. ## Using ROScopter as-is ROScopter's default waypoint-following functionality may be useful to some users. -The [ROSflight tutorials](../tutorials/tutorial-overview.md) walk users through setting up ROSflight and ROScopter in sim, all the way through flying waypoint missions. +The [ROSflight tutorials](../tutorials/index.md) walk users through setting up ROSflight and ROScopter in sim, all the way through flying waypoint missions. Follow those tutorials first to get a feel for the default ROScopter behavior and workflow before you start making your own changes to the autonomy stack. A detailed description of the ROScopter autonomy stack and each module is found in [the ROScopter architecture pages](./roscopter-path-planner.md). @@ -50,7 +50,7 @@ A detailed description of the ROScopter autonomy stack and each module is found ROScopter's default functionality may not be sufficient for many users. Because of this, ROScopter has been designed to be understandable, modular, and customizable. -The [customizing ROflight page](../customizing-rosflight.md) describes how ROScopter is meant to be modified to assist in your research. +The [customizing ROSflight page](../customizing-rosflight.md) describes how ROScopter is meant to be modified to assist in your research. The page also includes examples and scenarios where each node can be modified, removed, or combined to accomplish different tasks. ## ROScopter Architecture Overview diff --git a/docs/user-guide/rosflight-sim/detailed-launching-guide.md b/docs/user-guide/rosflight-sim/detailed-launching-guide.md index e91543d6..7bbe0653 100644 --- a/docs/user-guide/rosflight-sim/detailed-launching-guide.md +++ b/docs/user-guide/rosflight-sim/detailed-launching-guide.md @@ -38,7 +38,7 @@ This following sections detail how to launch and debug these two simulators. !!! tip "New to ROSflight?" If you are new to ROSflight, we recommend that you first start by setting up the simulation environment and learning to use the ROSflight ecosystem. - Do this by following the [installation for sim](../installation/installation-sim.md) guides, and then the [ROSflight tutorials](../tutorials/tutorial-overview.md). + Do this by following the [installation for sim](../installation/installation-sim.md) guides, and then the [ROSflight tutorials](../tutorials/index.md). **This guide assumes you have already followed these tutorials.** diff --git a/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md b/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md index a99cbe07..8910b131 100644 --- a/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md +++ b/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md @@ -21,7 +21,7 @@ The [detailed launching guide](./detailed-launching-guide.md) contains informati !!! tip "New to ROSflight?" If you are new to ROSflight, we recommend that you first start by setting up the simulation environment and learning to use the ROSflight ecosystem. - Do this by following the [installation for sim](../installation/installation-sim.md) guides, and then the [ROSflight tutorials](../tutorials/tutorial-overview.md). + Do this by following the [installation for sim](../installation/installation-sim.md) guides, and then the [ROSflight tutorials](../tutorials/index.md). After you do that, you should be ready to start using ROSflight in your own research! Visit the [developer guide pages](../../developer-guide/contribution-guidelines.md) for more specifics on how to use ROSflight for your research. diff --git a/docs/user-guide/rosplane/rosplane-overview.md b/docs/user-guide/rosplane/index.md similarity index 84% rename from docs/user-guide/rosplane/rosplane-overview.md rename to docs/user-guide/rosplane/index.md index 3d889cdb..65f5e073 100644 --- a/docs/user-guide/rosplane/rosplane-overview.md +++ b/docs/user-guide/rosplane/index.md @@ -21,7 +21,15 @@ This can improve research productivity, decrease debugging time, and improve the ## Core Functionality The ROSplane autopilot allows users to fly waypoint missions with an RC safety pilot. -The simplicity of this framework allows users to add their own autonomy stacks or mission requirements on top of the ROSplane stack. +These waypoints are defined by a 3-D location and (optionally) a desired heading at that location. + +See [the ROSplane autonomy stack documentation pages](../../developer-guide/rosplane/rosplane-dev-overview.md) for a detailed description of each module and its default functionality. + +## Using ROSplane as-is +The simplicity of the ROSplane framework allows users to add their own autonomy stacks or mission requirements on top of the ROSplane stack. + + +## Customizing ROSplane For example, the `path_manager` node in the navigation stack in the core ROSplane package directs the `path_follower` node to follow either straight lines or circular arcs. However, if a project needed to follow B-splines instead, the `path_manager` and the `path_follower` nodes could easily be replaced to achieve that. diff --git a/docs/user-guide/rosplane/rosplane-setup.md b/docs/user-guide/rosplane/rosplane-setup.md index 52f4849b..20d3f825 100644 --- a/docs/user-guide/rosplane/rosplane-setup.md +++ b/docs/user-guide/rosplane/rosplane-setup.md @@ -6,7 +6,7 @@ ROSplane is a basic fixed-wing autopilot build around ROS2 for use with the ROSflight autopilot. It is built according to the methods published in *Small Unmanned Aircraft: Theory and Practice* by Dr. Randy Beard and Dr. Tim McLain. -See [ROSplane Overview](rosplane-overview.md) for more general information on ROSplane. +See [ROSplane Overview](index.md) for more general information on ROSplane. ## Requirements diff --git a/docs/user-guide/tutorials/tutorial-overview.md b/docs/user-guide/tutorials/index.md similarity index 100% rename from docs/user-guide/tutorials/tutorial-overview.md rename to docs/user-guide/tutorials/index.md diff --git a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md index efc904b3..5ba21343 100644 --- a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md +++ b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md @@ -373,5 +373,4 @@ Once you have ROScopter running autonomously, you can: - [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed firmware parameter descriptions - [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight -- [ROScopter Architecture Documentation](../roscopter/roscopter-overview.md): In-depth system design and implementation details - +- [ROScopter Architecture Documentation](../roscopter/index.md): In-depth system design and implementation details diff --git a/mkdocs.yml b/mkdocs.yml index 791f8ca9..87f05441 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,6 +13,7 @@ theme: features: - content.action.edit - navigation.tabs + - navigation.indexes palette: primary: blue grey accent: red @@ -26,7 +27,10 @@ extra: provider: mike extra_javascript: - - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML + - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML + +# extra_css: +# - stylesheets/extra.css markdown_extensions: - admonition @@ -58,8 +62,8 @@ nav: - Installation for Sim: user-guide/installation/installation-sim.md - Installation on Hardware: user-guide/installation/installation-hardware.md - Using Docker with ROSflight: user-guide/installation/using-docker-with-rosflight.md - - Tutorials: - - Tutorial overview: user-guide/tutorials/tutorial-overview.md + - Tutorials: + - user-guide/tutorials/index.md - Setting up ROSflight Sim: user-guide/tutorials/setting-up-rosflight-sim.md - Manually flying ROSflight Sim: user-guide/tutorials/manually-flying-rosflight-sim.md - Running ROScopter in Sim: user-guide/tutorials/setting-up-roscopter-in-sim.md @@ -80,10 +84,10 @@ nav: - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md - Simulator Architecture: user-guide/rosflight-sim/simulator-architecture.md - ROSplane: - - ROSplane Overview: user-guide/rosplane/rosplane-overview.md + - user-guide/rosplane/index.md - ROSplane Setup: user-guide/rosplane/rosplane-setup.md - ROScopter: - - ROScopter Overview: user-guide/roscopter/roscopter-overview.md + - user-guide/roscopter/index.md - ROScopter Path Planner: user-guide/roscopter/roscopter-path-planner.md - ROScopter Path Manager: user-guide/roscopter/roscopter-path-manager.md - ROScopter Trajectory Follower: user-guide/roscopter/roscopter-trajectory-follower.md From 3df7399d4622da46de46380e4c3d93ae8bdfecf6 Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Fri, 9 Jan 2026 15:51:19 -0700 Subject: [PATCH 5/9] cleanup: move mixer documentation to rosflight_firmware folder --- .../hardware-and-rosflight/getting-started.md | 2 +- .../hardware-and-rosflight/hardware-setup.md | 138 ----------- .../parameter-configuration.md | 10 +- .../images/primary_secondary_mixer_mixing.svg | 3 + .../images/rc_mixer_configuration.png | Bin 85206 -> 0 bytes docs/user-guide/rosflight-firmware/mixer.md | 214 ++++++++++++++++++ mkdocs.yml | 3 + 7 files changed, 226 insertions(+), 144 deletions(-) create mode 100644 docs/user-guide/images/primary_secondary_mixer_mixing.svg delete mode 100644 docs/user-guide/images/rc_mixer_configuration.png create mode 100644 docs/user-guide/rosflight-firmware/mixer.md diff --git a/docs/user-guide/hardware-and-rosflight/getting-started.md b/docs/user-guide/hardware-and-rosflight/getting-started.md index d061e266..2677d01a 100644 --- a/docs/user-guide/hardware-and-rosflight/getting-started.md +++ b/docs/user-guide/hardware-and-rosflight/getting-started.md @@ -47,7 +47,7 @@ The following checklists should help you get a new vehicle set up for the first 3. Connect power to the motors 4. Drop the throttle to minimum - 5. Set the `PRIMARY_MIXER` parameter back to the appropriate value for your vehicle (see the [Hardware Setup](hardware-setup.md#motor-layouts-and-mixer) page) + 5. Set the `PRIMARY_MIXER` parameter back to the appropriate value for your vehicle (see the [Hardware Setup](../rosflight-firmware/mixer.md) page) 6. Set `ARM_SPIN_MOTORS` back to `1` 2. The `ARM_SPIN_MOTORS` parameter should be set to `1` so the motors spin slowly when armed. The idle throttle setting can be adjusted with the `MOTOR_IDLE_THR` parameter. diff --git a/docs/user-guide/hardware-and-rosflight/hardware-setup.md b/docs/user-guide/hardware-and-rosflight/hardware-setup.md index 0a5be411..9f90858e 100644 --- a/docs/user-guide/hardware-and-rosflight/hardware-setup.md +++ b/docs/user-guide/hardware-and-rosflight/hardware-setup.md @@ -115,144 +115,6 @@ Below is an example wiring diagram for a multirotor using an MSI Cubi as a compa Your needs will likely be slightly different than what is shown. This is meant as an example only and can be adapted to fit your needs. -## Motor Layouts and Mixer - -The mixer takes in the desired forces and torques from the firmware controller and computes the motor and servo outputs accordingly. -If it is not set correctly, it will likely lead to a crash. -Make sure it is set properly for your airframe! - -!!! tip "Quick Start" - - For a quick start, use one of the "canned mixers". - For a more accurate mixer, use a custom mixer. - -ROSflight offers some pre-computed, "canned" mixers that can be used off the shelf for a variety of common multirotor and fixedwing airframes. -These mixers do not take into account all the parameters of your system (i.e. motor and propeller parameters), so they will be less accurate than they could be. -If you want a more accurate mixer, or have easy access to the motor and prop parameters of your system, then we recommend using a custom mixer. - -!!! warning "Important" - A mixer must be chosen for the firmware to allow arming. - -The desired mixer can be chosen by setting the `PRIMARY_MIXER` parameter to one of the following values: - -| # | Mixer | -|---|---------| -| 0 | ESC calibration | -| 1 | Quad + | -| 2 | Quad X | -| 3 | Hex + | -| 4 | Hex X | -| 5 | Octo + | -| 6 | Octo X | -| 7 | Y6 | -| 8 | X8 | -| 9 | Fixed-wing (traditional AETR) | -| 10 | Inverted V-tail fixedwing (like the RMRC Anaconda frame) | -| 11 | Custom mixer | - -The associated motor layouts are shown below for each mixer. -The **ESC calibration** mixer directly outputs the throttle command equally to each motor, and can be used for calibrating the ESCs. - -![Mixer_1](../images/mixers_1.png) - -The following parameters related to the mixer are optional: - -* `SECONDARY_MIXER` -* `USE_MOTOR_PARAM` -* All the custom mixer params of the form `PRI_MIXER_i_j` or `SEC_MIXER_i_j` - -The following subsections have more detail on these parameters. - -### Secondary Mixer - -ROSflight also has a secondary mixer that can be set using the options in the above table by setting the `SECONDARY_MIXER` param. - -Offboard control commands will use the secondary mixer, while commands from the RC safety pilot will use the primary mixer. -Thus, both RC throttle and attitude override will affect the mixer, as shown in the following image. - -![RC Mixer Configuration](../images/rc_mixer_configuration.png) - -The `mixer_to_use_` structure represents the mixer that will be used when computing the output. -The header, which includes the default PWM rate and the output type for each output channel, is always set to the same as the primary mixer. -See [Defining a Custom Mixer](#defining-a-custom-mixer) for more information. -Note that if the `SECONDARY_MIXER` param is not set, then it will default to the same value as the primary mixer. - -The secondary mixer might be useful when the airframe needs a different mixer for the offboard control (from the companion computer) than for RC control (from the safety pilot). -It allows flexibility for more advanced mixing schemes while still having a functional mixer available to a safety pilot. - -### Using Motor Parameters - -The parameter `USE_MOTOR_PARAM` causes the firmware to compute the actuator outputs differently than if `USE_MOTOR_PARAM` is set false. -As described in _Small Unmanned Aircraft: Theory and Practice_ by Beard and McLain, the mixing matrix is formed using equations from propeller theory, resulting in a set of equations that set the desired forces and torques equal to the square of the angular speeds of the propellers. -If the motor and propeller parameters are known, then the desired voltage (and thus throttle) setting can be computed from the squared angular speeds. - -If the motor and propeller parameters are not known, then some simplifying assumptions are made to compute the desired throttle settings for each motor from the desired forces and torques. -See [the report on the ROSflight mixer derivation](https://github.com/rosflight/rosflight_docs/blob/main/latex-reports/mixer.tex) for more information on the mixer derivation and assumptions. - -!!! tip "Quick Start" - - If using a canned mixer, set `USE_MOTOR_PARAM=0`. - - If using a custom mixer, set `USE_MOTOR_PARAM=1` **only** if the mixer was designed with motor parameters. - -The canned mixing matrices assume that `USE_MOTOR_PARAM` parameter is set to false. -Using a canned mixer matrix and setting `USE_MOTOR_PARAM=1` (i.e. specifying that you want to mix with motor and propeller parameters) will cause the outputs to be scaled incorrectly. -It is not required to use motor and propeller parameters when using a custom mixing matrix, but make sure your custom mixer makes sense. - -Also, if you selected a custom mixer and used the motor parameters to generate the mixer, make sure you set `USE_MOTOR_PARAM=1`. Otherwise, the outputs will likely be scaled incorrectly. - -!!! Important - - We recommend flying your firmware in simulation _before_ loading the firmware onto real hardware to make sure everything is working. - -!!! Warning - - It is not recommended to use a _canned mixer for the primary mixer_ and a _custom mixer for the secondary mixer_ **when the secondary mixer needs `USE_MOTOR_PARAM=1`.** - In other words, both `PRIMARY_MIXER` and `SECONDARY_MIXER` should use motor parameters, or neither should. - - This is important because the canned mixers make assumptions that affect the gains of the controller on the aircraft. - This means that a canned mixer will require slightly different tuning than a custom mixer might. - -### Defining a Custom Mixer - -A custom mixer can be defined by: - -1. Set `PRIMARY_MIXER` (required) and/or `SECONDARY_MIXER` (optional) to the desired value in the mixer table -2. Load the mixing matrix parameters for either/both the primary or the secondary mixer - -Note that computing the parameters of the mixing matrix can be done on the companion computer. - -The firmware loads a custom mixer by loading all mixing matrix values from parameters. -Since there are 6 inputs to the mixer (\(F_x,F_y,F_z,Q_x,Q_y,Q_z\)) and 10 possible outputs, the mixer is a 6x10 matrix and there are 60 parameters associated with each custom mixer. -For a standard quadrotor, however, most of these would be zero, since only the first 4 outputs (columns of the mixer matrix) would be used. - -In addition to the parameters associated with the 6x10 mixing matrix, there are two additional sets of parameters that need to be defined for each output used, the `PRI_MIXER_OUT_i` and the `PRI_MIXER_PWM_i` parameters, which define the output type and the default PWM rate, respectively, for the `i`th output. -See the [Parameter Configuration Page](parameter-configuration.md) for more information on these parameters. -The PWM rate is typically 490 or 50 Hz. - -!!! warning "Mixing Matrix PWM Header" - - Depending on the hardware used, the 10 `PRI_MIXER_PWM_*` parameters need to be grouped into sections. - This is because the PWM outputs are not all on separate interrupts, so it is not possible to set each PWM rate individually. - - The number of interrupts varies based on hardware. - If you don't want to worry about it, just set all the `PRI_MIXER_PWM_i` to the same value, if possible. - -The recommended way to load a custom mixer is to first compute all the required parameters and save them to a file on the companion computer. -The parameters are named `PRI_MIXER_i_j` or `SEC_MIXER_i_j`, where `(i,j)` is the index of the parameter in the 6x10 mixing matrix. -See the [Parameter Configuration Page](parameter-configuration.md) for more information on these parameters. -A convenience script is available in the `roscopter` ROS2 package that will compute the custom mixer and save the parameter values in a format ready to load. - -Once the parameters are saved to a file, load them with the ROS2 service call (make sure `rosflight_io` is running): -```ros2 service call /param_load_from_file rosflight_msgs/srv/ParamFile "{file: absolute/or/relative/path/to/saved/param/file.yaml}"``` - -Also make sure to save those parameters to memory with the ROS2 service call: -```ros2 service call /param_write std_srvs/srv/Trigger``` - -!!! tip - - Test your mixer in simulation first when making changes, to avoid accidents. - ## Connecting to the Flight Controller The flight controller communicates with the companion computer over a serial link. ROSflight only supports one serial connection at a time and by default should be the serial link connected to the USB connector on the board. diff --git a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md b/docs/user-guide/hardware-and-rosflight/parameter-configuration.md index fbde484b..71c90ba0 100644 --- a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md +++ b/docs/user-guide/hardware-and-rosflight/parameter-configuration.md @@ -102,11 +102,11 @@ Because ROSflight ships with default parameters for multirotors, you will probab | MOTOR_IDLE_THR | min throttle command sent to motors when armed (Set above 0.1 to spin when armed) | float | 0.1 | | ARM_CHANNEL | RC switch channel mapped to arming [0 indexed, -1 to disable] | int | 4 | | FIXED_WING | switches on passthrough commands for fixed-wing operation | int | true | -| MIXER | Which mixer to choose - See [Mixer documentation](hardware-setup.md#motor-layouts-and-mixer) | int | 10 | -| ELEVATOR_REV | reverses elevator servo output | int | 0/1 | -| AIL_REV | reverses aileron servo output | int | 0/1 | -| RUDDER_REV | reverses rudder servo output | int | 0/1 | -| CAL_GYRO_ARM | Calibrate gyros when arming - generally only for multirotors | int | false | 0 | 1 | +| PRIMARY_MIXER | Which primary mixer to choose - See [Mixer documentation](../rosflight-firmware/mixer.md) | int | 9 or 10 | +| ELEVATOR_REV | reverses elevator servo output | int | 0 or 1 | +| AIL_REV | reverses aileron servo output | int | 0 or 1 | +| RUDDER_REV | reverses rudder servo output | int | 0 or 1 | +| CAL_GYRO_ARM | Calibrate gyros when arming - generally only for multirotors | int | false | ## Description of all Parameters diff --git a/docs/user-guide/images/primary_secondary_mixer_mixing.svg b/docs/user-guide/images/primary_secondary_mixer_mixing.svg new file mode 100644 index 00000000..80149ce6 --- /dev/null +++ b/docs/user-guide/images/primary_secondary_mixer_mixing.svg @@ -0,0 +1,3 @@ + + +
Header
Primary Mixer
(RC pilot mixer)
Secondary Mixer
(offboard mixer)
RC throttle override active
RC attitude override active

Header

Header

\ No newline at end of file diff --git a/docs/user-guide/images/rc_mixer_configuration.png b/docs/user-guide/images/rc_mixer_configuration.png deleted file mode 100644 index e3361e5665c7c877568225bea4bc243df9de438a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85206 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%0A_vmAL1 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�&d!9{C``fz7tH`I6ZY@y*bh2)vmh(GMat6z7 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%0DT?^6BPiSx)mD1$j<|_?@1- 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 z zgDd`@&2tAVkXsrV%d1+(H6;5HOF0AA1h+d;rs%CZy@;Bgm4b8LMRaLAl$MQEPqm6XGRY(eo26}rd5-e zxP=voR__>HkH;9cV3_GGxPcWJCCraPY>h2zZ}I20-abTs#~g!eU4XAu^ByhJ4(AW! z3wxnO0@i$FC0#ea!t#`%3MfvE5+3N;svgP~fzyHfTwc_s`F%}_I}n^Hki`8!&`80R zk1OTr|9qU?MsTopICi|>=MLje6T!a^DFY)bE2GGH04HVu=~!O%64h=P6|24x(Cen> zNUOal)}_1P`IW+OhuBP{$i-9IW1Mx4WK)gbavzw|)Y*Rtzs_pRgPVGvK& zGF)nG;!j3pZCjesUkWf;q9n?+wnZnk5gJ8cyw$4cl=aJgL_eP*MmQ2Ls36RS2qCe+ z_v{(1YhxieuuC4{#7N*8nQLES+CbTfi zFaT0bxAHWG_Y_X}_6fYzqR8e}!ncUG<++&X|T@3Kw7pID-NikGRweElxZbMeIo=)Xp(uH}um;JtMw(!+;Hc)Nhh z#E1E>QRSFHyMMs2r4GtRb@vRcy^|M~Kvgxk4`8%&YMtk?S;|D9*@~{WMnX({-2a9+ zMX59E3?Dup61-W#+ZUFo@M{VH>PKkiwz$9_l>#;vv1w9>x9*D%9RH-rJ;5--dz8k7 zk$|=F%XHgQhuVb0OhQK-q)1BYYU zF|RC^!;$Px3^nu!+_LkkUA^BXz!*wpR+hc*--t~B1=BU^7PJ1n^C-SO0U|Msf?CgS z@ZT&wO6|jF2{LAzfBm?^j|<6%Bp4Ttxru{U*7AtfKXyVMAx3x^)}WH32K&tiaI7I9 NC@W~lSAZ-d{|oLrAx{7R diff --git a/docs/user-guide/rosflight-firmware/mixer.md b/docs/user-guide/rosflight-firmware/mixer.md new file mode 100644 index 00000000..6f5276bf --- /dev/null +++ b/docs/user-guide/rosflight-firmware/mixer.md @@ -0,0 +1,214 @@ +# Mixer + +A controller is responsible for computing commands that drive a system to a given reference. +However, in general the output of a controller is not raw motor commands. +For example, given a quadrotor vehicle, the [ROSflight firmware rate controller](TODO:insertlinkhere) takes in desired angular rates and outputs desired forces and torques that will achieve those rates. +These desired forces and torques are not individual actuator commands. +Thus, these desired forces and torques must be correctly *mapped* to each individual actuator. + +The *mixer* is responsible for mapping, or mixing, the output of a controller to the individual actuator commands of a MAV. +The mixer is highly frame-specific: a quadrotor mixer will be different than a hexarotor mixer since the hexarotor has 2 additional rotors and all six rotors are in physically different locations relative to the vehicle center of mass. + +!!! danger + If the mixer is not set correctly, it will lead to a crash. + Make sure it is set properly for your airframe! + +In ROSflight, the mixer is a matrix and operates on the inputs $u \in \mathbb{R}^m$ as +$$ +\tau = M^\dagger u +$$ +where $\tau \in \mathbb{R}^n$ is the vector of individual motor commands, $M \in \mathbb{R}^{m\times n}$ is the mixing matrix, and $(\cdot)^\dagger$ is the Moore-Penrose pseudoinverse. + +Note that $n$ denotes the number of possible output commands and $m$ denotes the size of the input commands, which for ROSflight is 6. +Note that $m=6$ was arbitrarily chosen, but conveniently corresponds to the number of forces and torques for a 6-DoF body. +The number of possible output commands is dependent on the number of PWM hardware outputs. + +!!! note + + The rest of this guide focuses on **using** the ROSflight mixer. + Please see the [ROSflight 2.0 paper](https://arxiv.org/abs/2510.00995) for more information on the derivation and details of the ROSflight mixer. + + Additionally, this guide tries not to repeat information found in the paper, so we recommend reading through that paper to get an overview of the ROSflight mixer. + +## ROSflight mixer implementation details +Currently supported hardware for the ROSflight firmware has a maximum of 10 output channels. +Thus, the ROSflight mixing matrix is a 6x10 matrix. + +In addition to storing these 60 values, each ROSflight mixer has a header with the following information: + +- PWM rate (in Hz) for each output channel +- Output type (motor, servo, GPIO, or auxiliary) + +Users usually won't have to adjust these header values, unless they [define a custom mixer](#defining-a-custom-mixer) as described below. + +## Selecting a primary mixer + +A primary mixer must be selected before the firmware will allow arming of the vehicle. +The primary mixer is used by the RC safety pilot and thus must be a mixer that allows the RC safety pilot to control the vehicle. +Specifically, the `PRIMARY_MIXER` parameter must be set to a valid value. + +As described in the [ROSflight 2.0 paper](https://arxiv.org/abs/2510.00995), ROSflight offers some pre-computed, "canned" mixers that can be used off the shelf for a variety of common multirotor and fixedwing airframes. +These mixers do not take into account all the parameters of your system (i.e. motor and propeller parameters), but instead are based solely on the geometry of the airframe. +If you want a more accurate mixer, or have easy access to the motor and prop parameters of your system, then we recommend using a [custom mixer](#defining-a-custom-mixer). + +The desired mixer must be chosen by [setting the `PRIMARY_MIXER` parameter](../hardware-and-rosflight/parameter-configuration.md) to one of the following valid values. + + +| # | Mixer | +|---|---------| +| 0 | ESC calibration | +| 1 | Quad + | +| 2 | Quad X | +| 3 | Hex + | +| 4 | Hex X | +| 5 | Octo + | +| 6 | Octo X | +| 7 | Y6 | +| 8 | X8 | +| 9 | Fixed-wing (traditional AETR) | +| 10 | Inverted V-tail fixedwing (like the RMRC Anaconda frame) | +| 11 | Custom mixer | + +The associated motor layouts are shown below for each mixer. +The **ESC calibration** mixer directly outputs the throttle command equally to each motor, and can be used for calibrating the ESCs. + +![Mixer_1](../images/mixers_1.png) + +## Selecting a secondary mixer +As described in the [ROSflight 2.0 paper](https://arxiv.org/abs/2510.00995), the secondary mixer is used by offboard control setpoints (sent by the companion computer). +The secondary mixer can be selected by setting the `SECONDARY_MIXER` parameter to one of the valid values in the previous table. + +The secondary mixer allows flexibility for more advanced mixing schemes while still having a functional mixer available to a safety pilot. +For example, a secondary mixer on a quadrotor could be configured as an identity matrix. +This would allow motor commands to be sent directly from the offboard computer to the motors. +In this case, the primary mixer could be set to mixer 2 so that the RC safety pilot can use the ROSflight angle/rate controllers, whose output is properly mixed to actuator commands by the mixer 2. + +!!! note + If the secondary mixer is not specified (or set to an invalid value), then it will default to the primary mixer. + If you don't need a distinction between the mixer used by the RC safety pilot and the onboard computer, then you do not need to set the secondary mixer parameter. + +## Mixing the primary and secondary mixers +In ROSflight, the RC safety pilot has the ability to independently override the attitude commands, the throttle commands, or both. +See the [RC configuration page](../hardware-and-rosflight/rc-configuration.md) for more information. + +Remember that the RC commands use the primary mixer and offboard control commands use the secondary mixer. +Thus, when only RC attitude override is active (or conversely only RC throttle override) the mixer actually used by ROSflight to mix controller commands is a combination of the primary and secondary mixers. + +When this happens, the primary and secondary mixers are "mixed" as shown in the following image. + +
+ ![RC Mixer Configuration](../images/primary_secondary_mixer_mixing.svg){ width="1200" loading=lazy } +
Diagram of how the actual mixer used by ROSflight, $M$, is "mixed" with the primary and secondary mixers, depending on the status of RC override.
+
+ +The `mixer_to_use_` structure represents the mixer that is actually used when computing the output. +When just RC throttle override is active (i.e. RC pilot controls the throttle while companion computer controls attitude), the $F$ rows of the `mixer_to_use_` are mapped to the $F$ rows of the primary mixer. +When just RC attitude override is active (i.e. RC pilot controls attitude while companion computer controls throttle), the $Q$ rows of the `mixer_to_use_` are mapped to the $Q$ rows of the primary mixer. + +The header, which includes the default PWM rate and the output type for each output channel, is always set to the header of the primary mixer. + + +## Using Motor Parameters +This section gives some background for the `USE_MOTOR_PARAM` firmware parameter and when it should be set. + +As described in _Small Unmanned Aircraft: Theory and Practice_ by Beard and McLain, the mixing matrix is formed using equations from propeller theory, resulting in a set of equations that set the desired forces and torques equal to the square of the angular speeds of the propellers. + +This results in equations of the form +$$ +M^\dagger u = \tau \propto +\begin{bmatrix} +\Omega_1^2 & \Omega_2^2 & \dots & \Omega_{10}^2 +\end{bmatrix}^T +$$ +where $\Omega_i^2$ is the squared angular speed of motor $i$. +Note that constant factors were omitted in the above equation for simplicity, resulting in the proportionality. + +If the motor and propeller parameters are known, then the desired voltage setting can be computed from these squared angular speeds. +The PWM motor output for each motor can be computed from these desired voltage settings. + +If the `USE_MOTOR_PARAM=1`, then the firmware will assume that the mixed output ($\tau$) is a vector of squared angular speeds ($\Omega^2$), and will use the stored motor and propeller parameters to compute motor commands ($\delta$). + +If the motor and propeller parameters are not known, then some simplifying assumptions are made to compute the desired throttle settings for each motor from the desired forces and torques. +In other words, these assumptions result in equations that look like +$$ +M^\dagger u = \tau = +\begin{bmatrix} +\delta_1 & \delta_2 & \dots & \delta_{10} +\end{bmatrix}^T +$$ +where $\delta_i$ is the $i$th PWM motor output. +**This is the form for all precomputed ("canned") mixers.** + +Therefore, if using a canned mixer, set `USE_MOTOR_PARAM=0` so that the firmware interprets the output of the mixer as $\delta$ commands, not $\Omega^2$ commands. + +See [the ROSflight 2.0 publication](https://arxiv.org/abs/2510.00995) for more information on the specific assumptions and more information on the above topics. + + +The canned mixing matrices assume that `USE_MOTOR_PARAM=0`. +Using a canned mixer matrix and setting `USE_MOTOR_PARAM=1` (i.e. specifying that you want to mix with motor and propeller parameters) will cause the outputs to be scaled incorrectly. +It is not required to use motor and propeller parameters when using a custom mixing matrix, but make sure your custom mixer makes sense. + +Also, if you selected a custom mixer and used the motor parameters to generate the mixer, make sure you set `USE_MOTOR_PARAM=1`. Otherwise, the outputs will likely be scaled incorrectly. + +!!! abstract "Summary" + + If using a canned mixer, set `USE_MOTOR_PARAM=0`. + + If using a custom mixer, set `USE_MOTOR_PARAM=1` **only** if the mixer was designed with motor parameters. + +!!! danger + + We recommend flying your firmware in simulation _before_ loading the firmware onto real hardware to make sure everything is working. + +!!! Warning + + It is not recommended to use a _canned mixer for the primary mixer_ and a _custom mixer for the secondary mixer_ **when the secondary mixer needs `USE_MOTOR_PARAM=1`.** + In other words, both `PRIMARY_MIXER` and `SECONDARY_MIXER` should use motor parameters, or neither should. + + This is important because the canned mixers make assumptions that affect the gains of the controller on the aircraft. + This means that a canned mixer will require slightly different tuning than a custom mixer might. + +### Defining a Custom Mixer + +A custom mixer can be defined by: + +1. Set `PRIMARY_MIXER` (required) and/or `SECONDARY_MIXER` (optional) to the custom mixer value in the mixer table +2. Load the mixing matrix parameters for either/both the primary or the secondary mixer + +The firmware loads a custom mixer by loading all mixing matrix values from parameters. +Since there are 6 inputs to the mixer (\(F_x,F_y,F_z,Q_x,Q_y,Q_z\)) and 10 possible outputs, the mixer is a 6x10 matrix and there are 60 parameters associated with each custom mixer. +For a standard quadrotor, however, most of these would be zero, since only the first 4 outputs (columns of the mixer matrix) would be used. + +In addition to the parameters associated with the 6x10 mixing matrix, the [mixer header values](#rosflight-mixer-implementation-details) need to be specified. +Specifically, make sure to define and load the `PRI_MIXER_OUT_i` and the `PRI_MIXER_PWM_i` parameters, which define the output type and the default PWM rate, respectively, for each `i`th output. +See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-configuration.md) for more information on these parameters. + +!!! note + + The PWM rate is typically 490 or 50 Hz. + +!!! warning "Mixing Matrix PWM Header" + + PWM outputs are run off of hardware timers, which are often tied to particular clocks. + Since there are typically more PWM channels than clocks (and therefore hardware timers), the PWM rate cannot be set individually for each channel. + Instead, there are groups of PWM channels that must all be configured to the same rate. + + This is usually only an issue when dealing with multiple types of actuators (e.g. servos and brushless motors) on a given airframe where the actuators require different PWM rates. + In many cases, servos and motors can be configured to have the same 50Hz rate. + This is how the ROSflight fixedwing canned mixers do it. + + The number of timers varies based on hardware, so double check your board documentation when creating the custom mixer PWM header. + If you don't want to worry about it, just set all the `PRI_MIXER_PWM_i` to the same value, if possible. + +The recommended way to load a custom mixer is to first compute all the required parameters and save them to a file on the companion computer. +The parameters are named `PRI_MIXER_i_j` or `SEC_MIXER_i_j`, where `(i,j)` is the index of the parameter in the 6x10 mixing matrix. +See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-configuration.md) for more information on these parameters. + +A convenience script is available in the `roscopter` ROS2 package that will compute the custom mixer and save the parameter values in a format ready to load. +The convenience script is found [here](https://github.com/rosflight/roscopter/blob/main/roscopter/scripts/compute_multirotor_mixing_matrix.py) with the configuration file found [here](https://github.com/rosflight/roscopter/blob/main/roscopter/config/multirotor_frame_config.yaml). + +Once the parameters are saved to a file, load them with the ROS2 service call (make sure `rosflight_io` is running): +```ros2 service call /param_load_from_file rosflight_msgs/srv/ParamFile "{file: absolute/or/relative/path/to/saved/param/file.yaml}"``` + +Also make sure to save those parameters to memory with the ROS2 service call: +```ros2 service call /param_write std_srvs/srv/Trigger``` diff --git a/mkdocs.yml b/mkdocs.yml index 87f05441..dbf9d510 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -46,6 +46,8 @@ markdown_extensions: - pymdownx.smartsymbols - pymdownx.superfences - pymdownx.tasklist + - md_in_html + - attr_list - toc: permalink: true @@ -78,6 +80,7 @@ nav: - ROSflightIO: user-guide/rosflight-io.md - ROSflight Firmware: - Code Architecture: user-guide/rosflight-firmware/code-architecture.md + - Mixer: user-guide/rosflight-firmware/mixer.md - Building and Flashing: user-guide/rosflight-firmware/building-and-flashing.md - ROSflight Sim: - ROSflight Sim Overview: user-guide/rosflight-sim/running-simulations-with-rosflight.md From d0132e9a980d2c95f014ccd21e1c65b79e08a66a Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Fri, 9 Jan 2026 16:21:31 -0700 Subject: [PATCH 6/9] feat: add auxiliary mixer input documentation --- .../parameter-configuration.md | 20 ++++++------ docs/user-guide/rosflight-firmware/mixer.md | 31 +++++++++++++++++-- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md b/docs/user-guide/hardware-and-rosflight/parameter-configuration.md index 71c90ba0..30624a6d 100644 --- a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md +++ b/docs/user-guide/hardware-and-rosflight/parameter-configuration.md @@ -127,16 +127,16 @@ This is a list of all ROSflight parameters, including their types, default value | PROP_CQ | Torque coefficient of the propeller | float | 0.0045f | 0 | 100.0 | | VOLT_MAX | Maximum voltage of the battery (V) | float | 25.0f | 0 | 100.0 | | USE_MOTOR_PARAM | Flag to use motor parameters in the mixer | int | false | 0 | 1 | -| PRI_MIXER_OUT_0 | Output type of mixer output 0. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_1 | Output type of mixer output 1. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_2 | Output type of mixer output 2. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_3 | Output type of mixer output 3. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_4 | Output type of mixer output 4. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_5 | Output type of mixer output 5. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_6 | Output type of mixer output 6. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_7 | Output type of mixer output 7. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_8 | Output type of mixer output 8. | int | 0 | 0 | 1 | -| PRI_MIXER_OUT_9 | Output type of mixer output 9. | int | 0 | 0 | 1 | +| PRI_MIXER_OUT_0 | Output type of mixer output 0. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_1 | Output type of mixer output 1. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_2 | Output type of mixer output 2. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_3 | Output type of mixer output 3. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_4 | Output type of mixer output 4. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_5 | Output type of mixer output 5. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_6 | Output type of mixer output 6. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_7 | Output type of mixer output 7. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_8 | Output type of mixer output 8. | int | 0 | 0 | 3 | +| PRI_MIXER_OUT_9 | Output type of mixer output 9. | int | 0 | 0 | 3 | | PRI_MIXER_PWM_0 | PWM frequenct output for mixer output 0 | float | 0 | 0 | 490 | | PRI_MIXER_PWM_1 | PWM frequenct output for mixer output 1 | float | 0 | 0 | 490 | | PRI_MIXER_PWM_2 | PWM frequenct output for mixer output 2 | float | 0 | 0 | 490 | diff --git a/docs/user-guide/rosflight-firmware/mixer.md b/docs/user-guide/rosflight-firmware/mixer.md index 6f5276bf..fc24754e 100644 --- a/docs/user-guide/rosflight-firmware/mixer.md +++ b/docs/user-guide/rosflight-firmware/mixer.md @@ -37,9 +37,9 @@ Thus, the ROSflight mixing matrix is a 6x10 matrix. In addition to storing these 60 values, each ROSflight mixer has a header with the following information: - PWM rate (in Hz) for each output channel -- Output type (motor, servo, GPIO, or auxiliary) +- [Output type](#defining-a-custom-mixer) (auxiliary, motor, servo, GPIO) -Users usually won't have to adjust these header values, unless they [define a custom mixer](#defining-a-custom-mixer) as described below. +Users usually won't have to adjust these header values, unless you [define a custom mixer](#defining-a-custom-mixer) as described below. ## Selecting a primary mixer @@ -181,6 +181,21 @@ For a standard quadrotor, however, most of these would be zero, since only the f In addition to the parameters associated with the 6x10 mixing matrix, the [mixer header values](#rosflight-mixer-implementation-details) need to be specified. Specifically, make sure to define and load the `PRI_MIXER_OUT_i` and the `PRI_MIXER_PWM_i` parameters, which define the output type and the default PWM rate, respectively, for each `i`th output. + +The `PRI_MIXER_OUT_i` values should be set to one of the following values: + +| `PRI_MIXER_OUT_i` value | How channel is interpreted by mixer | +| :--- | :--- | +| 0 | Auxiliary | +| 1 | Servo | +| 2 | Motor | +| 3 | GPIO | + +This designation is important since in ROSflight, motor PWM commands are one-sided (ranging from zero to 1) where servos are two-sided (ranging from -1 to 1). +The mixer uses the output channel type designation from the above table to know how to scale an output value to a standard PWM command (between 1000 and 2000). + +The auxiliary command types are [described below](#auxiliary-inputs). + See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-configuration.md) for more information on these parameters. !!! note @@ -212,3 +227,15 @@ Once the parameters are saved to a file, load them with the ROS2 service call (m Also make sure to save those parameters to memory with the ROS2 service call: ```ros2 service call /param_write std_srvs/srv/Trigger``` + +## Auxiliary inputs + +ROSflight supports auxiliary inputs, which are defined as inputs that bypass the mixer. +Thus, any output channel in the mixer labeled as `AUX` will not use the mixer. +This can be useful for sending servo commands to landing gears, grasping mechanisms, or other more complicated scenarios. + +!!! tip + + To set a channel in a custom mixer as an `AUX` channel (bypassing the mixer), set the `PRI_MIXER_OUT_i` parameter to 0, where `i` is the number of the channel (zero indexed). + + These values are hard-coded for the canned mixers. From 4c4f00ad870e64e95fee79fd7dbcb6dec000bfac Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Wed, 14 Jan 2026 20:23:33 -0700 Subject: [PATCH 7/9] cleanup: collapse rosflight-sim overview page into main header --- docs/user-guide/hardware-and-rosflight/hardware-setup.md | 2 +- .../{running-simulations-with-rosflight.md => index.md} | 0 mkdocs.yml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename docs/user-guide/rosflight-sim/{running-simulations-with-rosflight.md => index.md} (100%) diff --git a/docs/user-guide/hardware-and-rosflight/hardware-setup.md b/docs/user-guide/hardware-and-rosflight/hardware-setup.md index 9f90858e..c8cc2c64 100644 --- a/docs/user-guide/hardware-and-rosflight/hardware-setup.md +++ b/docs/user-guide/hardware-and-rosflight/hardware-setup.md @@ -88,7 +88,7 @@ You will need a laptop which can run ROS2 to communicate with the MAV over the g ### Joystick -A joystick is used for [software-in-the-loop (SIL) simulations](../rosflight-sim/running-simulations-with-rosflight.md). The joystick is not technically a required component because it is possible to control your MAV from the command line, but it makes things much easier. Our first recommendation is to use the same transmitter you use for hardware as a joystick by plugging it into the computer via USB. We support Taranis QX7 transmitters, Radiomaster TX16s transmitters, RealFlight controllers, and XBOX controllers. Other joysticks can be used, but you may need to create custom axis and button mappings within the ROSflight joystick utility. +A joystick is used for [software-in-the-loop (SIL) simulations](../rosflight-sim/index.md). The joystick is not technically a required component because it is possible to control your MAV from the command line, but it makes things much easier. Our first recommendation is to use the same transmitter you use for hardware as a joystick by plugging it into the computer via USB. We support Taranis QX7 transmitters, Radiomaster TX16s transmitters, RealFlight controllers, and XBOX controllers. Other joysticks can be used, but you may need to create custom axis and button mappings within the ROSflight joystick utility. !!! note "Physical vs firmware channels" If you do write your own mapping, remember that the channel numbers need to be configured properly on both the firmware and the transmitter. diff --git a/docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md b/docs/user-guide/rosflight-sim/index.md similarity index 100% rename from docs/user-guide/rosflight-sim/running-simulations-with-rosflight.md rename to docs/user-guide/rosflight-sim/index.md diff --git a/mkdocs.yml b/mkdocs.yml index dbf9d510..373ab419 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -83,7 +83,7 @@ nav: - Mixer: user-guide/rosflight-firmware/mixer.md - Building and Flashing: user-guide/rosflight-firmware/building-and-flashing.md - ROSflight Sim: - - ROSflight Sim Overview: user-guide/rosflight-sim/running-simulations-with-rosflight.md + - user-guide/rosflight-sim/index.md - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md - Simulator Architecture: user-guide/rosflight-sim/simulator-architecture.md - ROSplane: From b2a4f6604fec8c98dd6ad4a36f546a6f2775abb0 Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Wed, 14 Jan 2026 20:52:13 -0700 Subject: [PATCH 8/9] feat: add docs about mag and IMU sensor orientations to firmware code architecture --- .../rosflight-firmware/code-architecture.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/user-guide/rosflight-firmware/code-architecture.md b/docs/user-guide/rosflight-firmware/code-architecture.md index 312ce97e..dd19ec43 100644 --- a/docs/user-guide/rosflight-firmware/code-architecture.md +++ b/docs/user-guide/rosflight-firmware/code-architecture.md @@ -62,6 +62,8 @@ It supports the getting and setting of integer and floating-point parameters, an Setting and getting of parameters from the companion computer is done through the serial communication interface. While no other data flow lines are shown on the diagram, all of the other modules interact with the parameter server. +See the [parameter configuration](../hardware-and-rosflight/parameter-configuration.md) page for more information on each of the ROSflight parameters and how to get and set parameters. + ### Comm Manager This module handles all serial communication between the flight controller and companion computer. This includes streaming data and receiving offboard control setpoints and other commands from the computer. @@ -76,6 +78,17 @@ The implementation is found in [comms/mavlink/mavlink.h](https://github.com/rosf This module is in charge of managing the various sensors (IMU, magnetometer, barometer, differential pressure sensor, sonar altimeter, etc.). Its responsibilities include updating sensor data at appropriate rates, and computing and applying calibration parameters. +For some sensors, the orientation is critical. +For example, the IMU and magnetometer sensor data must be oriented in the correct frame to be interpreted correctly. +The ROSflight firmware expects that the [board implementation](#board-abstraction) handles rotating the sensor data into a board-aligned NED frame. + +In some cases, the magnetometer is mounted to a different board then the main flight controller (e.g. the mRo M10034D GPS/mag board). +In this case, the ROSflight sensor driver does not orient the magnetometer data into the main autopilot-board frame (since it has no idea how the mag board is oriented relative to the autopilot board). + +ROSflight also aligns IMU and magnetometer data with the vehicle NED frame before [streaming the data](#comm-manager) to the companion computer. +This is done using a rotation from the board frame to the vehicle frame. +This rotation from board to vehicle frame can be configured via [parameters](#parameter-server). + ### Estimator This module is responsible for estimating the attitude and attitude rates of the vehicle from the sensor data. @@ -83,6 +96,8 @@ This module is responsible for estimating the attitude and attitude rates of the The RC module is responsible for interpreting the RC signals coming from the transmitter via the receiver. This includes mapping channels to their appropriate functions and reversing directions if necessary. +See the [RC configuration](../hardware-and-rosflight/rc-configuration.md) page for more information on configuring the RC transmitter and parameters. + ### Command Manager The command manager combines inputs from the RC and comm manager modules to produce a control setpoint. Its main purpose is to handle the interaction between offboard commands and the RC safety pilot, as well as to enforce the failsafe command if the state manager reports failsafe mode. @@ -93,3 +108,5 @@ This control output is computed in a generic form (\(Q_x\), \(Q_y\), and \(Q_z\) ### Mixer The mixer takes the outputs computed by the controller and maps them to actual motor commands depending on the configuration of the vehicle. + +See the [Mixer](./mixer.md) documentation page for more information on the details of the mixer and how to configure it. From a29b8f9d6eb29dd97bc05cae0ab8feae4410ffc6 Mon Sep 17 00:00:00 2001 From: Jacob Moore Date: Sat, 17 Jan 2026 14:22:13 -0700 Subject: [PATCH 9/9] cleanup: moved parameter configuration and autonomous flight to the rosflight-firmware section --- docs/developer-guide/firmware/debugging.md | 2 +- docs/user-guide/customizing-rosflight.md | 2 +- .../flight-controller-setup.md | 98 ++++++++++++++++++- .../hardware-and-rosflight/getting-started.md | 4 +- .../hardware-and-rosflight/hardware-setup.md | 2 +- .../autonomous-flight.md | 59 +++++------ .../building-and-flashing.md | 94 ------------------ .../rosflight-firmware/code-architecture.md | 2 +- docs/user-guide/rosflight-firmware/mixer.md | 6 +- .../parameter-configuration.md | 5 +- .../rosflight-sim/detailed-launching-guide.md | 2 +- .../manually-flying-rosflight-sim.md | 6 +- .../tutorials/setting-up-roscopter-in-sim.md | 2 +- .../tutorials/setting-up-rosplane-in-sim.md | 2 +- .../tutorials/tuning-performance-in-sim.md | 4 +- mkdocs.yml | 17 ++-- 16 files changed, 154 insertions(+), 153 deletions(-) rename docs/user-guide/{hardware-and-rosflight => rosflight-firmware}/autonomous-flight.md (82%) delete mode 100644 docs/user-guide/rosflight-firmware/building-and-flashing.md rename docs/user-guide/{hardware-and-rosflight => rosflight-firmware}/parameter-configuration.md (98%) diff --git a/docs/developer-guide/firmware/debugging.md b/docs/developer-guide/firmware/debugging.md index 2b8dbaa7..57e79e33 100644 --- a/docs/developer-guide/firmware/debugging.md +++ b/docs/developer-guide/firmware/debugging.md @@ -12,7 +12,7 @@ This guide assumes you are running Ubuntu 22.04 LTS, which is the currently supp ## General Setup -Follow the guide in [Building and Flashing](../../user-guide/rosflight-firmware/building-and-flashing.md) to install the compiler toolchain. +Follow the guide in [Building and Flashing](../../user-guide/hardware-and-rosflight/flight-controller-setup.md#building-and-flashing-the-firmware) to install the compiler toolchain. Also make sure you have configured your computer as described in the [Serial Port Configuration](../../user-guide/hardware-and-rosflight/flight-controller-setup.md#serial-port-configuration) section of the user guide. diff --git a/docs/user-guide/customizing-rosflight.md b/docs/user-guide/customizing-rosflight.md index be74f7c1..e7d34d26 100644 --- a/docs/user-guide/customizing-rosflight.md +++ b/docs/user-guide/customizing-rosflight.md @@ -27,7 +27,7 @@ The core [ROSflight firmware](./overview.md) resides on an embedded microcontrol Most users of ROSflight will not need to customize the ROSflight firmware, since the vast majority of the autonomy stack is on the companion computer. If you need to customize the ROSflight firmware, first consider if you can accomplish what you need on the companion computer. -If you still need to customize the ROSflight firmware, make your changes and then follow the [building and flashing guide](rosflight-firmware/building-and-flashing.md) to flash it on your board. +If you still need to customize the ROSflight firmware, make your changes and then follow the [building and flashing guide](hardware-and-rosflight/flight-controller-setup.md#building-and-flashing-the-firmware) to flash it on your board. However, since ROSflight currently supports only [limited hardware](./hardware-and-rosflight/flight-controller-setup.md), users may need to write a new board-specific support package for the ROSflight firmware. The ROSflight firmware depends on [an abstraction of a physical board](rosflight-firmware/code-architecture.md). diff --git a/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md b/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md index 1fdad96c..103becc7 100644 --- a/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md +++ b/docs/user-guide/hardware-and-rosflight/flight-controller-setup.md @@ -1,8 +1,5 @@ # Flight Controller Setup -!!! Note - This page contains instructions for flashing pre-built firmware binaries. - For instructions on building and flashing from source, see [Building and Flashing](../rosflight-firmware/building-and-flashing.md) in the Developer Guide. ## Compatible Hardware @@ -55,10 +52,103 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0664" ## Building and Flashing the Firmware -Follow the [building and flashing firmware guide](../rosflight-firmware/building-and-flashing.md) to build the ROSflight firmware and flash it to your flight controller. +This guide assumes you are running Ubuntu 22.04 LTS, which is the currently supported development environment. + +### Installing the ARM Embedded Toolchain + +``` bash +sudo apt install gcc-arm-none-eabi +``` + +You can test the installation and check which version is installed by running `arm-none-eabi-gcc --version`. + +### Building the Firmware from Source + +1. To build the firmware, first clone the firmware repository: + ```bash + git clone --recursive https://github.com/rosflight/rosflight_firmware + ``` +1. Create build directory: + ```bash + cd rosflight_firmware && mkdir build && cd build + ``` +1. Build using: (`board_name` should be either `varmint` or `pixracer_pro`) + ```bash + cmake .. -DBOARD_TO_BUILD= && make -j + ``` + +### Flashing firmware + +#### Install STM32CubeProgrammer + +We use the STM32CubeProgrammer to flash the flight controller. + +1. Download the programmer [here](https://www.st.com/en/development-tools/stm32cubeprog.html#get-software). + You may have to enter your email to download the software. +1. Install the software by following the instructions in the downloaded package. + +#### Flashing the Varmint + +!!! warning "Needed tools" + + You will need an ST-Link STM programmer to flash the firmware. + We use ST-Link V2, and you can find it [on Mouser](https://www.mouser.com/ProductDetail/STMicroelectronics/ST-LINK-V2?qs=H4BOwPtf9MC1sDQ8j3cy4w%3D%3D&mgh=1&utm_id=22314976717&utm_source=google&utm_medium=cpc&utm_marketing_tactic=amercorp&gad_source=1&gad_campaignid=22304734959). + +1. Plug the end of the ribbon cable into the 6-pin slot on the Varmint. + You may have to make your own cable that connects the ST-Link to the 6-pin connector. + + !!! danger + + The Varmint has 2 6-pin connectors. + **Do not** connect the ribbon cable to the port closest to the power wires. + + ![Varmint 6-pin cable](../images/varmint_flash_instructions.png) + +1. Power on the Varmint by connecting a battery to the board. + +1. Open STM32CubeProgrammer. + +1. Plug in the USB connector from the ST-Link to the computer. Select "Connect" in the STM32CubeProgrammer. This should detect the ST-Link and connect automatically. + ![Select "Connect"](../images/stm_programmer_connect.png) + +1. Navigate to the programming page. + ![Navigate to programming page](../images/stm32_programming_page.png) + +1. Select the hex file that was just built and click "Open". + ``` + /path/to/rosflight_firmware/build/boards/varmint_h7/varmint_10X/varmint10X.hex + ``` + ![Select the previously built hex file](../images/stm32_select_hex.png) + +1. Select the appropriate options and press "Start Programming" + ![Select options and start programming](../images/stm32_select_options.png) + +#### Flashing the Pixracer Pro + +Flashing the Pixracer Pro is a very similar process to flashing the Varmint. + +1. The Pixracer Pro does not use the same 6-pin connector to connect to the ST-Link. + Instead, it uses a TC2030 connector with retaining clips. + + !!! tip "Connectors we use" + + In addition to the ST-Link V2 board linked above, we use these cables to flash the Pixracer Pro: + + 1. [20-pin ribbon adapter board](https://www.tag-connect.com/product/arm20-ctx-m) (connect the ST-Link to this adapter) + 2. [Programming cable](https://www.tag-connect.com/product/tc2030-idc-nl) that connects to the adapter board + 3. [TC2030 clip](https://www.tag-connect.com/product/tc2030-retaining-clip-board-3-pack) to attach the programming cable to the board + + ![Connectors for the Pixracer Pro](./../images/pixracer_pro_flash_instructions.jpeg) + +1. Power on the Pixracer Pro using a USB-C port. + +2. Follow the instructions from the [Varmint flashing guide](#flashing-the-varmint). ## LEDs +!!! danger "TODO" + Update this table with the updated LED flash patterns. + The meaning of the various LEDs is summarized in the following table. The colors of the LEDs may change depending on your specific board: | LED | On | Off | Slow Blink | Fast Blink | diff --git a/docs/user-guide/hardware-and-rosflight/getting-started.md b/docs/user-guide/hardware-and-rosflight/getting-started.md index 2677d01a..25e304d8 100644 --- a/docs/user-guide/hardware-and-rosflight/getting-started.md +++ b/docs/user-guide/hardware-and-rosflight/getting-started.md @@ -6,10 +6,10 @@ Reading through the pages in this user guide in order should provide you with th 2. [Flight controller setup](flight-controller-setup.md): Flash your flight controller with the latest ROSflight firmware. 3. [RC Configuration](rc-configuration.md): Set up your RC transmitter. 4. [ROS2 setup](../installation/installation-hardware.md): Set up ROS2 on your companion computer. - 5. [Parameter configuration](parameter-configuration.md): Configure the flight controller for your setup. The configuration checklists below should help guide you through this process. + 5. [Parameter configuration](../rosflight-firmware/parameter-configuration.md): Configure the flight controller for your setup. The configuration checklists below should help guide you through this process. 6. [Preflight checks](preflight-checks.md): Run through your preflight checks 7. [Improving firmware performance](improving-firmware-performance.md) (multirotors only): Tune the firmware attitude controller gains - 8. [Autonomous flight](autonomous-flight.md) (optional): Set up autonomous flight via offboard control + 8. [Autonomous flight](../rosflight-firmware/autonomous-flight.md) (optional): Set up autonomous flight via offboard control ## Configuration Checklist diff --git a/docs/user-guide/hardware-and-rosflight/hardware-setup.md b/docs/user-guide/hardware-and-rosflight/hardware-setup.md index c8cc2c64..7992a5b1 100644 --- a/docs/user-guide/hardware-and-rosflight/hardware-setup.md +++ b/docs/user-guide/hardware-and-rosflight/hardware-setup.md @@ -102,7 +102,7 @@ A battery monitor is an analog sensor that provides battery voltage and/or batte For ROSflight to use a battery monitor, an appropriate multiplier must be set. ROSflight multiplies the analog signal from the monitor by the multiplier to get the final reading. The monitor datasheet should contain the information needed to get the multiplier. For example, the datasheet for the AttoPilot 50V/90A sensor states that it outputs 63.69 mV / V. To get the original battery voltage, the multiplier must be 1/.06369, or 15.7. The multipliers for the voltage and current are set separately, with the `BATT_VOLT_MULT` and `BATT_CURR_MULT` parameters, respectively. -ROSflight applies a simple low-pass filter to remove noise from the voltage and current measurements. These filters are configurable via the `BATT_VOLT_ALPHA` and `BATT_CURR_ALPHA` [parameters](parameter-configuration.md). The alpha value for a given cutoff frequency \\(a\\), can be calulated with: \\( \alpha = e ^ {-.01a} \\). As battery voltages do not typically change quickly, the default of 0.995 usually suffices. +ROSflight applies a simple low-pass filter to remove noise from the voltage and current measurements. These filters are configurable via the `BATT_VOLT_ALPHA` and `BATT_CURR_ALPHA` [parameters](../rosflight-firmware/parameter-configuration.md). The alpha value for a given cutoff frequency \\(a\\), can be calulated with: \\( \alpha = e ^ {-.01a} \\). As battery voltages do not typically change quickly, the default of 0.995 usually suffices. More information on battery monitor hardware, including determining appropriate multipliers and creating a simple DIY monitor, can be found on the [OpenPilot Wiki](https://opwiki.readthedocs.io/en/latest/user_manual/revo/voltage_current.html). diff --git a/docs/user-guide/hardware-and-rosflight/autonomous-flight.md b/docs/user-guide/rosflight-firmware/autonomous-flight.md similarity index 82% rename from docs/user-guide/hardware-and-rosflight/autonomous-flight.md rename to docs/user-guide/rosflight-firmware/autonomous-flight.md index 724f9432..7731923e 100644 --- a/docs/user-guide/hardware-and-rosflight/autonomous-flight.md +++ b/docs/user-guide/rosflight-firmware/autonomous-flight.md @@ -68,35 +68,38 @@ The `ignore` field is a bitmask that can be populated by combining the values in ignore = IGNORE_QX | IGNORE_QY | IGNORE_FZ ``` -The best practice is to use enum names rather than the actual numeric values for the `mode` and `ignore` fields. For example, to specify a multirotor attitude angle command in C++ I might have: -```cpp -#include -#include - -rosflight_msgs::msg::Command msg; -msg.header.stamp = node->get_clock()->now(); -msg.mode = rosflight_msgs::msg::Command::MODE_ROLL_PITCH_YAWRATE_THROTTLE; -msg.ignore = rosflight_msgs::msg::Command::IGNORE_NONE; -msg.qx = 0.0; -msg.qy = 0.0; -msg.qz = 0.0; -msg.fz = 0.6; -``` +The best practice is to use enum names rather than the actual numeric values for the `mode` and `ignore` fields. For example, to specify a multirotor attitude angle command I might have: + +=== "C++" + ```cpp + #include + #include + + rosflight_msgs::msg::Command msg; + msg.header.stamp = node->get_clock()->now(); + msg.mode = rosflight_msgs::msg::Command::MODE_ROLL_PITCH_YAWRATE_THROTTLE; + msg.ignore = rosflight_msgs::msg::Command::IGNORE_NONE; + msg.qx = 0.0; + msg.qy = 0.0; + msg.qz = 0.0; + msg.fz = 0.6; + ``` + +=== "Python" + ```python + import rclpy + from rosflight_msgs.msg import Command + + msg = Command() + msg.header.stamp = node.get_clock().now().to_msg() + msg.mode = Command.MODE_ROLL_PITCH_YAWRATE_THROTTLE + msg.ignore = Command.IGNORE_NONE + msg.qx = 0.0 + msg.qy = 0.0 + msg.qz = 0.0 + msg.fz = 0.6 + ``` -In Python I might have: -```python -import rclpy -from rosflight_msgs.msg import Command - -msg = Command() -msg.header.stamp = node.get_clock().now().to_msg() -msg.mode = Command.MODE_ROLL_PITCH_YAWRATE_THROTTLE -msg.ignore = Command.IGNORE_NONE -msg.qx = 0.0 -msg.qy = 0.0 -msg.qz = 0.0 -msg.fz = 0.6 -``` I would then publish this message to the `/command` topic to forward it to the embedded flight controller. !!! note diff --git a/docs/user-guide/rosflight-firmware/building-and-flashing.md b/docs/user-guide/rosflight-firmware/building-and-flashing.md deleted file mode 100644 index 488db0ae..00000000 --- a/docs/user-guide/rosflight-firmware/building-and-flashing.md +++ /dev/null @@ -1,94 +0,0 @@ -# Building and Flashing the Firmware - - -This guide assumes you are running Ubuntu 22.04 LTS, which is the currently supported development environment. - -## Installing the ARM Embedded Toolchain - -``` bash -sudo apt install gcc-arm-none-eabi -``` - -You can test the installation and check which version is installed by running `arm-none-eabi-gcc --version`. - -## Building the Firmware from Source - -1. To build the firmware, first clone the firmware repository: - ```bash - git clone --recursive https://github.com/rosflight/rosflight_firmware - ``` -1. Create build directory: - ```bash - cd rosflight_firmware && mkdir build && cd build - ``` -1. Build using: (`board_name` should be either `varmint` or `pixracer_pro`) - ```bash - cmake .. -DBOARD_TO_BUILD= && make -j - ``` - -## Flashing firmware - -### Install STM32CubeProgrammer - -We use the STM32CubeProgrammer to flash the flight controller. - -1. Download the programmer [here](https://www.st.com/en/development-tools/stm32cubeprog.html#get-software). - You may have to enter your email to download the software. -1. Install the software by following the instructions in the downloaded package. - -### Flashing the Varmint - -!!! warning "Needed tools" - - You will need an ST-Link STM programmer to flash the firmware. - We use ST-Link V2, and you can find it [on Mouser](https://www.mouser.com/ProductDetail/STMicroelectronics/ST-LINK-V2?qs=H4BOwPtf9MC1sDQ8j3cy4w%3D%3D&mgh=1&utm_id=22314976717&utm_source=google&utm_medium=cpc&utm_marketing_tactic=amercorp&gad_source=1&gad_campaignid=22304734959). - -1. Plug the end of the ribbon cable into the 6-pin slot on the Varmint. - You may have to make your own cable that connects the ST-Link to the 6-pin connector. - - !!! danger - - The Varmint has 2 6-pin connectors. - **Do not** connect the ribbon cable to the port closest to the power wires. - - ![Varmint 6-pin cable](../images/varmint_flash_instructions.png) - -1. Power on the Varmint by connecting a battery to the board. - -1. Open STM32CubeProgrammer. - -1. Plug in the USB connector from the ST-Link to the computer. Select "Connect" in the STM32CubeProgrammer. This should detect the ST-Link and connect automatically. - ![Select "Connect"](../images/stm_programmer_connect.png) - -1. Navigate to the programming page. - ![Navigate to programming page](../images/stm32_programming_page.png) - -1. Select the hex file that was just built and click "Open". - ``` - /path/to/rosflight_firmware/build/boards/varmint_h7/varmint_10X/varmint10X.hex - ``` - ![Select the previously built hex file](../images/stm32_select_hex.png) - -1. Select the appropriate options and press "Start Programming" - ![Select options and start programming](../images/stm32_select_options.png) - -### Flashing the Pixracer Pro - -Flashing the Pixracer Pro is a very similar process to flashing the Varmint. - -1. The Pixracer Pro does not use the same 6-pin connector to connect to the ST-Link. - Instead, it uses a TC2030 connector with retaining clips. - - !!! tip "Connectors we use" - - In addition to the ST-Link V2 board linked above, we use these cables to flash the Pixracer Pro: - - 1. [20-pin ribbon adapter board](https://www.tag-connect.com/product/arm20-ctx-m) (connect the ST-Link to this adapter) - 2. [Programming cable](https://www.tag-connect.com/product/tc2030-idc-nl) that connects to the adapter board - 3. [TC2030 clip](https://www.tag-connect.com/product/tc2030-retaining-clip-board-3-pack) to attach the programming cable to the board - - ![Connectors for the Pixracer Pro](./../images/pixracer_pro_flash_instructions.jpeg) - -1. Power on the Pixracer Pro using a USB-C port. - -2. Follow the instructions from the [Varmint flashing guide](#flashing-the-varmint). diff --git a/docs/user-guide/rosflight-firmware/code-architecture.md b/docs/user-guide/rosflight-firmware/code-architecture.md index dd19ec43..985a17ac 100644 --- a/docs/user-guide/rosflight-firmware/code-architecture.md +++ b/docs/user-guide/rosflight-firmware/code-architecture.md @@ -62,7 +62,7 @@ It supports the getting and setting of integer and floating-point parameters, an Setting and getting of parameters from the companion computer is done through the serial communication interface. While no other data flow lines are shown on the diagram, all of the other modules interact with the parameter server. -See the [parameter configuration](../hardware-and-rosflight/parameter-configuration.md) page for more information on each of the ROSflight parameters and how to get and set parameters. +See the [parameter configuration](../rosflight-firmware/parameter-configuration.md) page for more information on each of the ROSflight parameters and how to get and set parameters. ### Comm Manager This module handles all serial communication between the flight controller and companion computer. diff --git a/docs/user-guide/rosflight-firmware/mixer.md b/docs/user-guide/rosflight-firmware/mixer.md index fc24754e..ff812d06 100644 --- a/docs/user-guide/rosflight-firmware/mixer.md +++ b/docs/user-guide/rosflight-firmware/mixer.md @@ -51,7 +51,7 @@ As described in the [ROSflight 2.0 paper](https://arxiv.org/abs/2510.00995), ROS These mixers do not take into account all the parameters of your system (i.e. motor and propeller parameters), but instead are based solely on the geometry of the airframe. If you want a more accurate mixer, or have easy access to the motor and prop parameters of your system, then we recommend using a [custom mixer](#defining-a-custom-mixer). -The desired mixer must be chosen by [setting the `PRIMARY_MIXER` parameter](../hardware-and-rosflight/parameter-configuration.md) to one of the following valid values. +The desired mixer must be chosen by [setting the `PRIMARY_MIXER` parameter](../rosflight-firmware/parameter-configuration.md) to one of the following valid values. | # | Mixer | @@ -196,7 +196,7 @@ The mixer uses the output channel type designation from the above table to know The auxiliary command types are [described below](#auxiliary-inputs). -See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-configuration.md) for more information on these parameters. +See the [Parameter Configuration Page](../rosflight-firmware/parameter-configuration.md) for more information on these parameters. !!! note @@ -217,7 +217,7 @@ See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-confi The recommended way to load a custom mixer is to first compute all the required parameters and save them to a file on the companion computer. The parameters are named `PRI_MIXER_i_j` or `SEC_MIXER_i_j`, where `(i,j)` is the index of the parameter in the 6x10 mixing matrix. -See the [Parameter Configuration Page](../hardware-and-rosflight/parameter-configuration.md) for more information on these parameters. +See the [Parameter Configuration Page](../rosflight-firmware/parameter-configuration.md) for more information on these parameters. A convenience script is available in the `roscopter` ROS2 package that will compute the custom mixer and save the parameter values in a format ready to load. The convenience script is found [here](https://github.com/rosflight/roscopter/blob/main/roscopter/scripts/compute_multirotor_mixing_matrix.py) with the configuration file found [here](https://github.com/rosflight/roscopter/blob/main/roscopter/config/multirotor_frame_config.yaml). diff --git a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md b/docs/user-guide/rosflight-firmware/parameter-configuration.md similarity index 98% rename from docs/user-guide/hardware-and-rosflight/parameter-configuration.md rename to docs/user-guide/rosflight-firmware/parameter-configuration.md index 30624a6d..8b0a9459 100644 --- a/docs/user-guide/hardware-and-rosflight/parameter-configuration.md +++ b/docs/user-guide/rosflight-firmware/parameter-configuration.md @@ -8,7 +8,8 @@ The ROSflight firmware has several dozen parameters which it uses to customize p * IMU low-pass filter constant * RC receiver type (PPM or SBUS) -and so on. Access to all parameters is enabled via ROS2 services advertised by `rosflight_io` while the flight controller is connected. +and so on. +Access to all parameters is enabled via ROS2 services advertised by `rosflight_io` while the flight controller is connected (either in sim or in hardware). ## Parameter Interface @@ -45,7 +46,7 @@ Notice that the parameters have been set, but not saved. Parameter changes take #### Changing Parameters via `rosflight_io` ROS2 params -The `rosflight_io` node has some firmware parameters exposed via the ROS2 parameter interface, enabling quick configuration of *some* of the firmware's parameters. +The [`rosflight_io` node](../rosflight-io.md#convenience-parameters) has some firmware parameters exposed via the ROS2 parameter interface, enabling quick configuration of *some* of the firmware's parameters. This means that changing these `rosflight_io` parameters via the standard ROS2 parameter configuration will automatically change them in the firmware. Currently, only the controller gains have been exposed to `rosflight_io`'s parameters. diff --git a/docs/user-guide/rosflight-sim/detailed-launching-guide.md b/docs/user-guide/rosflight-sim/detailed-launching-guide.md index 7bbe0653..1c5e2c43 100644 --- a/docs/user-guide/rosflight-sim/detailed-launching-guide.md +++ b/docs/user-guide/rosflight-sim/detailed-launching-guide.md @@ -196,7 +196,7 @@ Remember that the SIL tries its best to replicate hardware. That means you have to calibrate and set parameters in the same way you do in hardware. If you need a reminder, please follow the [configuration and manual flight tutorial](../tutorials/manually-flying-rosflight-sim.md). -See the [Parameter Configuration](../hardware-and-rosflight/parameter-configuration.md) pages in this documentation for instructions on how to perform all preflight configuration before the aircraft will arm. +See the [Parameter Configuration](../rosflight-firmware/parameter-configuration.md) pages in this documentation for instructions on how to perform all preflight configuration before the aircraft will arm. You can also run ```bash diff --git a/docs/user-guide/tutorials/manually-flying-rosflight-sim.md b/docs/user-guide/tutorials/manually-flying-rosflight-sim.md index e15b6b54..2b8a39ef 100644 --- a/docs/user-guide/tutorials/manually-flying-rosflight-sim.md +++ b/docs/user-guide/tutorials/manually-flying-rosflight-sim.md @@ -32,7 +32,7 @@ ros2 node list and verifying that the `rosflight_io` and `sil_board` nodes are included in the list. ### Loading parameters manually -You can load parameters one-by-one or with a YAML file, as described in the [parameter configuration guide](../hardware-and-rosflight/parameter-configuration.md). +You can load parameters one-by-one or with a YAML file, as described in the [parameter configuration guide](../rosflight-firmware/parameter-configuration.md). We will load parameters from a file. 1. Navigate to the params directory: @@ -53,7 +53,7 @@ Note that we first navigated to the directory so we could use the built-in `pwd` It just saves time instead of having to type the full path to the file. Here are some of the parameters you just loaded. -For a full list of the firmware's parameters, see [the parameter list](../hardware-and-rosflight/parameter-configuration.md). +For a full list of the firmware's parameters, see [the parameter list](../rosflight-firmware/parameter-configuration.md). - **Aircraft configuration**: Sets `FIXED_WING: 0` for multirotor operation - **RC channels**: configures 8 RC channels with appropriate mappings @@ -315,5 +315,5 @@ Once you have the simulator running, you can: ### Additional Resources -- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed parameter descriptions +- [ROSflight Parameter Reference](../rosflight-firmware/parameter-configuration.md): Detailed parameter descriptions - [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight diff --git a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md index 5ba21343..8189572c 100644 --- a/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md +++ b/docs/user-guide/tutorials/setting-up-roscopter-in-sim.md @@ -371,6 +371,6 @@ Once you have ROScopter running autonomously, you can: ### Additional Resources -- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed firmware parameter descriptions +- [ROSflight Parameter Reference](../rosflight-firmware/parameter-configuration.md): Detailed firmware parameter descriptions - [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight - [ROScopter Architecture Documentation](../roscopter/index.md): In-depth system design and implementation details diff --git a/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md b/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md index 7aa2948c..bfdd8537 100644 --- a/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md +++ b/docs/user-guide/tutorials/setting-up-rosplane-in-sim.md @@ -324,7 +324,7 @@ Once you have ROSplane running autonomously, you can: ### Additional Resources -- [ROSflight Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Detailed firmware parameter descriptions +- [ROSflight Parameter Reference](../rosflight-firmware/parameter-configuration.md): Detailed firmware parameter descriptions - [Hardware Setup Guide](../hardware-and-rosflight/hardware-setup.md): Preparing real hardware for flight - [ROSplane Architecture Documentation](../../developer-guide/rosplane/rosplane-dev-overview.md): In-depth system design and implementation details diff --git a/docs/user-guide/tutorials/tuning-performance-in-sim.md b/docs/user-guide/tutorials/tuning-performance-in-sim.md index 134de8ab..34e9b1cd 100644 --- a/docs/user-guide/tutorials/tuning-performance-in-sim.md +++ b/docs/user-guide/tutorials/tuning-performance-in-sim.md @@ -196,7 +196,7 @@ To do this, You should see the gains show up on the right as shown below. These are the firmware gains that we have exposed to `rosflight_io`. -This means that when we change the `rosflight_io` ROS2 parameters (which have the same name as the firmware parameters), the `rosflight_io` node will essentially call the `param_set` service as described in the [parameter configuration guide](../hardware-and-rosflight/parameter-configuration.md). +This means that when we change the `rosflight_io` ROS2 parameters (which have the same name as the firmware parameters), the `rosflight_io` node will essentially call the `param_set` service as described in the [parameter configuration guide](../rosflight-firmware/parameter-configuration.md). ![ROSflightIO gains on RQT](../images/rosflight_io_rqt.png) @@ -409,5 +409,5 @@ You should now be able to: ### Additional Resources -- [Parameter Reference](../hardware-and-rosflight/parameter-configuration.md): Complete parameter documentation +- [Parameter Reference](../rosflight-firmware/parameter-configuration.md): Complete parameter documentation - [Hardware Tuning Guide](../hardware-and-rosflight/improving-firmware-performance.md): Considerations for real hardware diff --git a/mkdocs.yml b/mkdocs.yml index 373ab419..c78f596f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -45,6 +45,8 @@ markdown_extensions: - pymdownx.inlinehilite - pymdownx.smartsymbols - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true - pymdownx.tasklist - md_in_html - attr_list @@ -80,12 +82,9 @@ nav: - ROSflightIO: user-guide/rosflight-io.md - ROSflight Firmware: - Code Architecture: user-guide/rosflight-firmware/code-architecture.md + - Parameter Configuration: user-guide/rosflight-firmware/parameter-configuration.md - Mixer: user-guide/rosflight-firmware/mixer.md - - Building and Flashing: user-guide/rosflight-firmware/building-and-flashing.md - - ROSflight Sim: - - user-guide/rosflight-sim/index.md - - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md - - Simulator Architecture: user-guide/rosflight-sim/simulator-architecture.md + - Autonomous Flight: user-guide/rosflight-firmware/autonomous-flight.md - ROSplane: - user-guide/rosplane/index.md - ROSplane Setup: user-guide/rosplane/rosplane-setup.md @@ -96,15 +95,17 @@ nav: - ROScopter Trajectory Follower: user-guide/roscopter/roscopter-trajectory-follower.md - ROScopter Controller: user-guide/roscopter/roscopter-controller.md - ROScopter Estimator: user-guide/roscopter/roscopter-estimator.md - - Hardware and ROSflight: + - ROSflight Sim: + - user-guide/rosflight-sim/index.md + - Detailed Launching Guide: user-guide/rosflight-sim/detailed-launching-guide.md + - Simulator Architecture: user-guide/rosflight-sim/simulator-architecture.md + - ROSflight in Hardware: - Getting Started: user-guide/hardware-and-rosflight/getting-started.md - Hardware Setup: user-guide/hardware-and-rosflight/hardware-setup.md - Flight Controller Setup: user-guide/hardware-and-rosflight/flight-controller-setup.md - RC Configuration: user-guide/hardware-and-rosflight/rc-configuration.md - - Parameter Configuration: user-guide/hardware-and-rosflight/parameter-configuration.md - Pre-Flight Checks: user-guide/hardware-and-rosflight/preflight-checks.md - Improving Firmware Performance: user-guide/hardware-and-rosflight/improving-firmware-performance.md - - Autonomous Flight: user-guide/hardware-and-rosflight/autonomous-flight.md - Customizing ROSflight: user-guide/customizing-rosflight.md - Developer Guide: - Contribution Guidelines: developer-guide/contribution-guidelines.md