Merge "iwyu: //aos/mutex/..."
diff --git a/aos/aos_cli_utils.cc b/aos/aos_cli_utils.cc
index bd54e38..bc1fb80 100644
--- a/aos/aos_cli_utils.cc
+++ b/aos/aos_cli_utils.cc
@@ -5,6 +5,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <iomanip>
 #include <iostream>
 
 #include "aos/configuration.h"
diff --git a/aos/aos_jitter.cc b/aos/aos_jitter.cc
index 3ac35e8..5f23935 100644
--- a/aos/aos_jitter.cc
+++ b/aos/aos_jitter.cc
@@ -1,6 +1,7 @@
 #include <unistd.h>
 
-#include <iostream>
+#include <iomanip>
+#include <iostream>  // IWYU pragma: keep
 
 #include "gflags/gflags.h"
 
diff --git a/aos/events/epoll.cc b/aos/events/epoll.cc
index 8c5c3e1..61c346e 100644
--- a/aos/events/epoll.cc
+++ b/aos/events/epoll.cc
@@ -1,14 +1,20 @@
 #include "aos/events/epoll.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <sys/epoll.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/timerfd.h>
+#include <time.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <atomic>
 #include <cstring>
+#include <ostream>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "glog/logging.h"
diff --git a/aos/events/epoll.h b/aos/events/epoll.h
index 526c6a7..80ae721 100644
--- a/aos/events/epoll.h
+++ b/aos/events/epoll.h
@@ -1,13 +1,12 @@
 #ifndef AOS_EVENTS_EPOLL_H_
 #define AOS_EVENTS_EPOLL_H_
 
-#include <fcntl.h>
+#include <stdint.h>
 #include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <unistd.h>
 
 #include <atomic>
 #include <functional>
+#include <memory>
 #include <vector>
 
 #include "aos/time/time.h"
diff --git a/aos/events/event_loop_event.h b/aos/events/event_loop_event.h
index 588b851..4d1ff07 100644
--- a/aos/events/event_loop_event.h
+++ b/aos/events/event_loop_event.h
@@ -1,6 +1,10 @@
 #ifndef AOS_EVENTS_EVENT_LOOP_EVENT_H
 #define AOS_EVENTS_EVENT_LOOP_EVENT_H
 
+#include <stddef.h>
+
+#include <chrono>
+
 #include "glog/logging.h"
 
 #include "aos/time/time.h"
diff --git a/aos/events/logging/boot_timestamp.h b/aos/events/logging/boot_timestamp.h
index e9c7877..dc5967c 100644
--- a/aos/events/logging/boot_timestamp.h
+++ b/aos/events/logging/boot_timestamp.h
@@ -1,7 +1,13 @@
 #ifndef AOS_EVENTS_LOGGING_BOOT_TIMESTAMP_H_
 #define AOS_EVENTS_LOGGING_BOOT_TIMESTAMP_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <compare>
 #include <iostream>
+#include <limits>
 
 #include "glog/logging.h"
 
diff --git a/aos/events/logging/file_operations.cc b/aos/events/logging/file_operations.cc
index 04e695e..d54c8b6 100644
--- a/aos/events/logging/file_operations.cc
+++ b/aos/events/logging/file_operations.cc
@@ -1,5 +1,8 @@
 #include "aos/events/logging/file_operations.h"
 
+#include <algorithm>
+#include <ostream>
+
 #include "absl/strings/match.h"
 #include "glog/logging.h"
 
diff --git a/aos/events/logging/file_operations.h b/aos/events/logging/file_operations.h
index 0c2133d..5f87ca5 100644
--- a/aos/events/logging/file_operations.h
+++ b/aos/events/logging/file_operations.h
@@ -1,8 +1,11 @@
 #ifndef AOS_EVENTS_LOGGING_FILE_OPERATIONS_H_
 #define AOS_EVENTS_LOGGING_FILE_OPERATIONS_H_
 
+#include <stddef.h>
+
 #include <filesystem>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace aos::logger::internal {
diff --git a/aos/flatbuffers/base.cc b/aos/flatbuffers/base.cc
index 8ad3b98..97b3b36 100644
--- a/aos/flatbuffers/base.cc
+++ b/aos/flatbuffers/base.cc
@@ -1,4 +1,9 @@
 #include "aos/flatbuffers/base.h"
+
+#include <string.h>
+
+#include <iomanip>
+
 namespace aos::fbs {
 
 namespace {
diff --git a/aos/flatbuffers/base.h b/aos/flatbuffers/base.h
index 387dbc3..ff81c9a 100644
--- a/aos/flatbuffers/base.h
+++ b/aos/flatbuffers/base.h
@@ -1,12 +1,19 @@
 #ifndef AOS_FLATBUFFERS_BASE_H_
 #define AOS_FLATBUFFERS_BASE_H_
-#include <iomanip>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cstring>
 #include <memory>
 #include <optional>
+#include <ostream>
 #include <span>
+#include <utility>
+#include <vector>
 
 #include "flatbuffers/base.h"
 #include "glog/logging.h"
+
 namespace aos::fbs {
 using ::flatbuffers::soffset_t;
 using ::flatbuffers::uoffset_t;
diff --git a/aos/flatbuffers/base_test.cc b/aos/flatbuffers/base_test.cc
index 522882d..f0eaf04 100644
--- a/aos/flatbuffers/base_test.cc
+++ b/aos/flatbuffers/base_test.cc
@@ -1,5 +1,9 @@
 #include "aos/flatbuffers/base.h"
 
+#include <stddef.h>
+
+#include <algorithm>
+
 #include "gtest/gtest.h"
 
 namespace aos::fbs::testing {
diff --git a/aos/flatbuffers/generate.cc b/aos/flatbuffers/generate.cc
index 030de35..9a2e5fd 100644
--- a/aos/flatbuffers/generate.cc
+++ b/aos/flatbuffers/generate.cc
@@ -1,4 +1,7 @@
+#include <stdlib.h>
+
 #include "flatbuffers/reflection_generated.h"
+#include "gflags/gflags.h"
 
 #include "aos/flatbuffers.h"
 #include "aos/flatbuffers/static_flatbuffers.h"
diff --git a/aos/flatbuffers/static_flatbuffers.cc b/aos/flatbuffers/static_flatbuffers.cc
index c1b617f..c2e0454 100644
--- a/aos/flatbuffers/static_flatbuffers.cc
+++ b/aos/flatbuffers/static_flatbuffers.cc
@@ -1,13 +1,28 @@
 #include "aos/flatbuffers/static_flatbuffers.h"
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <map>
+#include <optional>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include "absl/strings/numbers.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/str_join.h"
 #include "absl/strings/str_replace.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/string.h"
+#include "flatbuffers/vector.h"
 #include "glog/logging.h"
 
-#include "aos/flatbuffers/static_table.h"
+#include "aos/flatbuffers/base.h"
 #include "aos/json_to_flatbuffer.h"
+
 namespace aos::fbs {
 namespace {
 // Represents a given field within a type with all of the data that we actually
diff --git a/aos/flatbuffers/static_flatbuffers.h b/aos/flatbuffers/static_flatbuffers.h
index 6a5f800..11e21ae 100644
--- a/aos/flatbuffers/static_flatbuffers.h
+++ b/aos/flatbuffers/static_flatbuffers.h
@@ -1,12 +1,11 @@
 #ifndef AOS_FLATBUFFERS_STATIC_FLATBUFFERS_H_
 #define AOS_FLATBUFFERS_STATIC_FLATBUFFERS_H_
-#include <map>
-#include <set>
+#include <set>  // IWYU pragma: keep
 #include <string>
 #include <string_view>
-#include <vector>
 
-#include "flatbuffers/reflection_generated.h"
+#include "flatbuffers/reflection_generated.h"  // IWYU pragma: keep
+
 namespace aos::fbs {
 
 // Raw C++ code needed to represent a single flatbuffer table.
diff --git a/aos/flatbuffers/static_flatbuffers_fuzz_test.cc b/aos/flatbuffers/static_flatbuffers_fuzz_test.cc
index 755dd57..ae6fc71 100644
--- a/aos/flatbuffers/static_flatbuffers_fuzz_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_fuzz_test.cc
@@ -1,17 +1,15 @@
-#include "absl/strings/str_format.h"
+#include <functional>
+#include <span>
+#include <string>
+#include <string_view>
+#include <vector>
+
 #include "absl/strings/str_join.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
-#include "aos/flatbuffers.h"
 #include "aos/flatbuffers/builder.h"
-#include "aos/flatbuffers/static_flatbuffers.h"
-#include "aos/flatbuffers/test_dir/type_coverage_static.h"
 #include "aos/flatbuffers/test_static.h"
 #include "aos/json_to_flatbuffer.h"
-#include "aos/testing/path.h"
-#include "aos/testing/tmpdir.h"
-#include "aos/util/file.h"
 
 namespace aos::fbs::testing {
 
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index 26dfc50..a971d82 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -1,17 +1,42 @@
 #include "aos/flatbuffers/static_flatbuffers.h"
 
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <span>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
 #include "absl/strings/str_format.h"
 #include "absl/strings/str_join.h"
+#include "absl/types/span.h"
 #include "external/com_github_google_flatbuffers/src/annotated_binary_text_gen.h"
 #include "external/com_github_google_flatbuffers/src/binary_annotator.h"
-#include "gmock/gmock.h"
+#include "flatbuffers/base.h"
+#include "flatbuffers/buffer.h"
+#include "flatbuffers/flatbuffer_builder.h"
+#include "flatbuffers/stl_emulation.h"
+#include "flatbuffers/string.h"
+#include "flatbuffers/vector.h"
+#include "glog/logging.h"
 #include "gtest/gtest.h"
 
 #include "aos/flatbuffers.h"
+#include "aos/flatbuffers/base.h"
 #include "aos/flatbuffers/builder.h"
 #include "aos/flatbuffers/interesting_schemas.h"
+#include "aos/flatbuffers/static_vector.h"
+#include "aos/flatbuffers/test_dir/include_generated.h"
 #include "aos/flatbuffers/test_dir/include_reflection_static.h"
+#include "aos/flatbuffers/test_dir/include_static.h"
+#include "aos/flatbuffers/test_dir/type_coverage_generated.h"
 #include "aos/flatbuffers/test_dir/type_coverage_static.h"
+#include "aos/flatbuffers/test_generated.h"
 #include "aos/flatbuffers/test_schema.h"
 #include "aos/flatbuffers/test_static.h"
 #include "aos/json_to_flatbuffer.h"
diff --git a/aos/ipc_lib/aos_sync.cc b/aos/ipc_lib/aos_sync.cc
index b1a9fb0..78c6b2c 100644
--- a/aos/ipc_lib/aos_sync.cc
+++ b/aos/ipc_lib/aos_sync.cc
@@ -7,18 +7,16 @@
 
 #include <linux/futex.h>
 #include <pthread.h>
-#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
 #include <sys/syscall.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <cassert>
 #include <cerrno>
 #include <cinttypes>
 #include <climits>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
+#include <ostream>
 
 #include "aos/ipc_lib/shm_observers.h"
 
@@ -26,9 +24,6 @@
 #include <sanitizer/tsan_interface_atomic.h>
 #endif
 
-#include <algorithm>
-#include <type_traits>
-
 #include "absl/base/call_once.h"
 #include "glog/logging.h"
 
diff --git a/aos/ipc_lib/aos_sync.h b/aos/ipc_lib/aos_sync.h
index 157c1b2..0cdab30 100644
--- a/aos/ipc_lib/aos_sync.h
+++ b/aos/ipc_lib/aos_sync.h
@@ -1,11 +1,8 @@
 #ifndef AOS_IPC_LIB_SYNC_H_
 #define AOS_IPC_LIB_SYNC_H_
 
-#include <signal.h>
-#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <stdlib.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/aos/ipc_lib/core_lib.c b/aos/ipc_lib/core_lib.c
index bfe80c1..6f9aad2 100644
--- a/aos/ipc_lib/core_lib.c
+++ b/aos/ipc_lib/core_lib.c
@@ -4,6 +4,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "aos/ipc_lib/aos_sync.h"
 #include "aos/ipc_lib/shared_mem_types.h"
 
 static uint8_t aos_8max(uint8_t l, uint8_t r) { return (l > r) ? l : r; }
diff --git a/aos/ipc_lib/core_lib.h b/aos/ipc_lib/core_lib.h
index d62f602..a3a5ba8 100644
--- a/aos/ipc_lib/core_lib.h
+++ b/aos/ipc_lib/core_lib.h
@@ -1,10 +1,9 @@
 #ifndef _AOS_CORE_LIB_H_
 #define _AOS_CORE_LIB_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
-#include "aos/ipc_lib/aos_sync.h"
-
 #ifdef __cplusplus
 extern "C" {
 #endif  // __cplusplus
diff --git a/aos/ipc_lib/event.cc b/aos/ipc_lib/event.cc
index 6b58e33..fd5bcaf 100644
--- a/aos/ipc_lib/event.cc
+++ b/aos/ipc_lib/event.cc
@@ -1,6 +1,10 @@
 #include "aos/ipc_lib/event.h"
 
+#include <time.h>
+
 #include <chrono>
+#include <ostream>
+#include <ratio>
 
 #include "glog/logging.h"
 
diff --git a/aos/ipc_lib/event_test.cc b/aos/ipc_lib/event_test.cc
index eda9156..5a8e644 100644
--- a/aos/ipc_lib/event_test.cc
+++ b/aos/ipc_lib/event_test.cc
@@ -1,6 +1,7 @@
 #include "aos/ipc_lib/event.h"
 
 #include <chrono>
+#include <memory>
 #include <thread>
 
 #include "gtest/gtest.h"
diff --git a/aos/ipc_lib/eventfd_latency.cc b/aos/ipc_lib/eventfd_latency.cc
index efd326e..4a5616f 100644
--- a/aos/ipc_lib/eventfd_latency.cc
+++ b/aos/ipc_lib/eventfd_latency.cc
@@ -1,18 +1,21 @@
+#include <inttypes.h>
+#include <string.h>
 #include <sys/eventfd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <unistd.h>
 
+#include <algorithm>
 #include <chrono>
+#include <compare>
 #include <random>
+#include <ratio>
 #include <thread>
 
 #include "gflags/gflags.h"
+#include "glog/logging.h"
 
 #include "aos/events/epoll.h"
-#include "aos/init.h"
 #include "aos/ipc_lib/latency_lib.h"
 #include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
 #include "aos/realtime.h"
 #include "aos/time/time.h"
 
diff --git a/aos/ipc_lib/futex_latency.cc b/aos/ipc_lib/futex_latency.cc
index 80ba711..eee015e 100644
--- a/aos/ipc_lib/futex_latency.cc
+++ b/aos/ipc_lib/futex_latency.cc
@@ -1,18 +1,19 @@
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <inttypes.h>
+#include <signal.h>
 
+#include <algorithm>
 #include <chrono>
+#include <compare>
 #include <random>
+#include <ratio>
 #include <thread>
 
 #include "gflags/gflags.h"
+#include "glog/logging.h"
 
 #include "aos/condition.h"
-#include "aos/init.h"
 #include "aos/ipc_lib/latency_lib.h"
 #include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
 #include "aos/mutex/mutex.h"
 #include "aos/realtime.h"
 #include "aos/time/time.h"
diff --git a/aos/ipc_lib/index.h b/aos/ipc_lib/index.h
index 75f1e9b..c4cc64a 100644
--- a/aos/ipc_lib/index.h
+++ b/aos/ipc_lib/index.h
@@ -2,10 +2,8 @@
 #define AOS_IPC_LIB_INDEX_H_
 
 #include <stdint.h>
-#include <sys/types.h>
 
-#include <atomic>
-#include <string>
+#include <limits>
 
 #include "glog/logging.h"
 
@@ -25,10 +23,6 @@
 
 namespace aos::ipc_lib {
 
-struct AtomicQueueIndex;
-class AtomicIndex;
-class Index;
-
 namespace testing {
 class QueueIndexTest;
 }  // namespace testing
diff --git a/aos/ipc_lib/index_test.cc b/aos/ipc_lib/index_test.cc
index 8e0da6d..334696e 100644
--- a/aos/ipc_lib/index_test.cc
+++ b/aos/ipc_lib/index_test.cc
@@ -1,5 +1,7 @@
 #include "aos/ipc_lib/index.h"
 
+#include <ostream>
+
 #include "glog/logging.h"
 #include "gtest/gtest.h"
 
diff --git a/aos/ipc_lib/ipc_comparison.cc b/aos/ipc_lib/ipc_comparison.cc
index f15db53..8bb78d7 100644
--- a/aos/ipc_lib/ipc_comparison.cc
+++ b/aos/ipc_lib/ipc_comparison.cc
@@ -1,23 +1,30 @@
+#include <arpa/inet.h>
+#include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <mqueue.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <pthread.h>
 #include <semaphore.h>
+#include <stdio.h>
+#include <string.h>
 #include <sys/eventfd.h>
+#include <sys/ipc.h>
 #include <sys/msg.h>
 #include <sys/sem.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
+#include <unistd.h>
 
 #include <atomic>
 #include <chrono>
+#include <compare>
 #include <cstdint>
 #include <memory>
 #include <string>
 #include <thread>
+#include <utility>
 
 #include "gflags/gflags.h"
 
@@ -25,7 +32,6 @@
 #include "aos/init.h"
 #include "aos/ipc_lib/event.h"
 #include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
 #include "aos/mutex/mutex.h"
 #include "aos/realtime.h"
 #include "aos/time/time.h"
diff --git a/aos/ipc_lib/latency_lib.cc b/aos/ipc_lib/latency_lib.cc
index 0fcb8de..a31df1d 100644
--- a/aos/ipc_lib/latency_lib.cc
+++ b/aos/ipc_lib/latency_lib.cc
@@ -1,8 +1,9 @@
 #include "aos/ipc_lib/latency_lib.h"
 
+#include <algorithm>
 #include <chrono>
+#include <compare>
 #include <random>
-#include <thread>
 
 #include "aos/logging/logging.h"
 #include "aos/realtime.h"
diff --git a/aos/ipc_lib/latency_lib.h b/aos/ipc_lib/latency_lib.h
index 97bc434..8206f3e 100644
--- a/aos/ipc_lib/latency_lib.h
+++ b/aos/ipc_lib/latency_lib.h
@@ -2,10 +2,8 @@
 #define AOS_IPC_LIB_LATENCY_LIB_H_
 
 #include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <chrono>
+#include <string.h>
+#include <unistd.h>
 
 #include "glog/logging.h"
 
diff --git a/aos/ipc_lib/lockless_queue.cc b/aos/ipc_lib/lockless_queue.cc
index 57a2e9e..ae3a493 100644
--- a/aos/ipc_lib/lockless_queue.cc
+++ b/aos/ipc_lib/lockless_queue.cc
@@ -2,21 +2,25 @@
 
 #include <linux/futex.h>
 #include <pwd.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
-#include <syscall.h>
 #include <unistd.h>
 
 #include <algorithm>
+#include <chrono>
+#include <compare>
 #include <iomanip>
 #include <iostream>
-#include <sstream>
+#include <string>
+#include <string_view>
 
 #include "absl/strings/escaping.h"
 #include "gflags/gflags.h"
 #include "glog/logging.h"
 
 #include "aos/ipc_lib/lockless_queue_memory.h"
-#include "aos/realtime.h"
 #include "aos/util/compiler_memory_barrier.h"
 
 DEFINE_bool(dump_lockless_queue_data, false,
diff --git a/aos/ipc_lib/lockless_queue.h b/aos/ipc_lib/lockless_queue.h
index 462b2b6..ce067e6 100644
--- a/aos/ipc_lib/lockless_queue.h
+++ b/aos/ipc_lib/lockless_queue.h
@@ -1,14 +1,18 @@
 #ifndef AOS_IPC_LIB_LOCKLESS_QUEUE_H_
 #define AOS_IPC_LIB_LOCKLESS_QUEUE_H_
 
-#include <sys/signalfd.h>
-#include <sys/types.h>
+#include <signal.h>
+#include <stdint.h>
 
-#include <csignal>
+#include <atomic>
+#include <functional>
+#include <iosfwd>
 #include <optional>
+#include <utility>
 #include <vector>
 
 #include "absl/types/span.h"
+#include "glog/logging.h"
 
 #include "aos/events/context.h"
 #include "aos/ipc_lib/aos_sync.h"
diff --git a/aos/ipc_lib/lockless_queue_death_test.cc b/aos/ipc_lib/lockless_queue_death_test.cc
index bc5ebb7..c61c62c 100644
--- a/aos/ipc_lib/lockless_queue_death_test.cc
+++ b/aos/ipc_lib/lockless_queue_death_test.cc
@@ -1,31 +1,25 @@
-#include <dlfcn.h>
-#include <elf.h>
-#include <linux/futex.h>
+#include <stdio.h>
 #include <sys/mman.h>
-#include <sys/procfs.h>
-#include <sys/ptrace.h>
-#include <sys/syscall.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <wait.h>
 
 #include <chrono>
 #include <cinttypes>
 #include <functional>
 #include <memory>
-#include <thread>
+#include <optional>
+#include <ostream>
 
-#include "gflags/gflags.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
 
-#include "aos/ipc_lib/aos_sync.h"
+#include "aos/events/context.h"
+#include "aos/ipc_lib/index.h"
 #include "aos/ipc_lib/lockless_queue.h"
 #include "aos/ipc_lib/lockless_queue_memory.h"
 #include "aos/ipc_lib/lockless_queue_stepping.h"
-#include "aos/ipc_lib/shm_observers.h"
-#include "aos/realtime.h"
+#include "aos/ipc_lib/robust_ownership_tracker.h"
 #include "aos/testing/test_logging.h"
+#include "aos/time/time.h"
+#include "aos/uuid.h"
 
 namespace aos::ipc_lib::testing {
 
diff --git a/aos/ipc_lib/lockless_queue_memory.h b/aos/ipc_lib/lockless_queue_memory.h
index 70fc8a8..12aec89 100644
--- a/aos/ipc_lib/lockless_queue_memory.h
+++ b/aos/ipc_lib/lockless_queue_memory.h
@@ -1,6 +1,7 @@
 #ifndef AOS_IPC_LIB_LOCKLESS_QUEUE_MEMORY_H_
 #define AOS_IPC_LIB_LOCKLESS_QUEUE_MEMORY_H_
 
+#include <stdint.h>
 #include <sys/types.h>
 
 #include <cstddef>
@@ -8,7 +9,6 @@
 #include "aos/ipc_lib/aos_sync.h"
 #include "aos/ipc_lib/index.h"
 #include "aos/ipc_lib/lockless_queue.h"
-#include "aos/time/time.h"
 
 namespace aos::ipc_lib {
 
diff --git a/aos/ipc_lib/lockless_queue_stepping.cc b/aos/ipc_lib/lockless_queue_stepping.cc
index 40d1631..a7bd233 100644
--- a/aos/ipc_lib/lockless_queue_stepping.cc
+++ b/aos/ipc_lib/lockless_queue_stepping.cc
@@ -1,24 +1,30 @@
 #include "aos/ipc_lib/lockless_queue_stepping.h"
 
-#include <dlfcn.h>
+#include <assert.h>
 #include <elf.h>
-#include <linux/futex.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/procfs.h>
 #include <sys/ptrace.h>
 #include <sys/syscall.h>
 #include <sys/uio.h>
+#include <sys/wait.h>
 #include <unistd.h>
-#include <wait.h>
 
-#include <memory>
+#include <atomic>
+#include <new>
+#include <optional>
+#include <ostream>
+#include <string>
 #include <thread>
+#include <utility>
 
 #include "glog/logging.h"
 #include "gtest/gtest.h"
 
 #include "aos/ipc_lib/aos_sync.h"
-#include "aos/ipc_lib/lockless_queue_memory.h"
 #include "aos/ipc_lib/shm_observers.h"
 #include "aos/libc/aos_strsignal.h"
 #include "aos/testing/prevent_exit.h"
diff --git a/aos/ipc_lib/lockless_queue_stepping.h b/aos/ipc_lib/lockless_queue_stepping.h
index 590534f..8be7b44 100644
--- a/aos/ipc_lib/lockless_queue_stepping.h
+++ b/aos/ipc_lib/lockless_queue_stepping.h
@@ -1,11 +1,14 @@
 #ifndef AOS_IPC_LIB_LOCKLESS_QUEUE_STEPPING_H_
 #define AOS_IPC_LIB_LOCKLESS_QUEUE_STEPPING_H_
 
+#include <stdlib.h>
+#include <unistd.h>
+
 #include <cinttypes>
 #include <functional>
+#include <tuple>
 
 #include "aos/ipc_lib/lockless_queue.h"
-#include "aos/ipc_lib/lockless_queue_memory.h"
 
 namespace aos::ipc_lib::testing {
 
diff --git a/aos/ipc_lib/lockless_queue_test.cc b/aos/ipc_lib/lockless_queue_test.cc
index a2c0992..4169c93 100644
--- a/aos/ipc_lib/lockless_queue_test.cc
+++ b/aos/ipc_lib/lockless_queue_test.cc
@@ -1,21 +1,26 @@
 #include "aos/ipc_lib/lockless_queue.h"
 
-#include <sys/mman.h>
-#include <unistd.h>
-#include <wait.h>
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/signalfd.h>
 
+#include <algorithm>
 #include <chrono>
 #include <cinttypes>
+#include <compare>
 #include <csignal>
-#include <memory>
+#include <new>
+#include <ostream>
 #include <random>
+#include <ratio>
 #include <thread>
+#include <type_traits>
 
 #include "gflags/gflags.h"
 #include "gtest/gtest.h"
 
 #include "aos/events/epoll.h"
-#include "aos/ipc_lib/aos_sync.h"
 #include "aos/ipc_lib/event.h"
 #include "aos/ipc_lib/lockless_queue_memory.h"
 #include "aos/ipc_lib/lockless_queue_stepping.h"
diff --git a/aos/ipc_lib/memory_mapped_queue.cc b/aos/ipc_lib/memory_mapped_queue.cc
index dc457fe..8daf5a1 100644
--- a/aos/ipc_lib/memory_mapped_queue.cc
+++ b/aos/ipc_lib/memory_mapped_queue.cc
@@ -1,10 +1,22 @@
 #include "aos/ipc_lib/memory_mapped_queue.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <limits>
+#include <ostream>
+#include <thread>
+
 #include "absl/strings/str_cat.h"
+#include "flatbuffers/string.h"
+#include "glog/logging.h"
+
+#include "aos/ipc_lib/index.h"
+#include "aos/util/file.h"
 
 namespace aos::ipc_lib {
 
diff --git a/aos/ipc_lib/memory_mapped_queue.h b/aos/ipc_lib/memory_mapped_queue.h
index bd766c1..f16f88e 100644
--- a/aos/ipc_lib/memory_mapped_queue.h
+++ b/aos/ipc_lib/memory_mapped_queue.h
@@ -1,6 +1,12 @@
 #ifndef AOS_IPC_LIB_MEMORY_MAPPED_QUEUE_H_
 #define AOS_IPC_LIB_MEMORY_MAPPED_QUEUE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <string_view>
+
 #include "absl/types/span.h"
 
 #include "aos/configuration.h"
diff --git a/aos/ipc_lib/named_pipe_latency.cc b/aos/ipc_lib/named_pipe_latency.cc
index 032b72e..023853d 100644
--- a/aos/ipc_lib/named_pipe_latency.cc
+++ b/aos/ipc_lib/named_pipe_latency.cc
@@ -1,17 +1,22 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
 #include <sys/stat.h>
-#include <sys/types.h>
+#include <unistd.h>
 
+#include <algorithm>
 #include <chrono>
+#include <compare>
 #include <random>
+#include <ratio>
 #include <thread>
 
 #include "gflags/gflags.h"
+#include "glog/logging.h"
 
 #include "aos/events/epoll.h"
-#include "aos/init.h"
 #include "aos/ipc_lib/latency_lib.h"
 #include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
 #include "aos/realtime.h"
 #include "aos/time/time.h"
 
diff --git a/aos/ipc_lib/print_lockless_queue_memory.cc b/aos/ipc_lib/print_lockless_queue_memory.cc
index 2cbcf12..4bad81c 100644
--- a/aos/ipc_lib/print_lockless_queue_memory.cc
+++ b/aos/ipc_lib/print_lockless_queue_memory.cc
@@ -1,7 +1,11 @@
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/types.h>
+#include <unistd.h>
+
+#include <ostream>
+
+#include "glog/logging.h"
 
 #include "aos/ipc_lib/lockless_queue.h"
 
diff --git a/aos/ipc_lib/queue_racer.cc b/aos/ipc_lib/queue_racer.cc
index 2797c24..67ed6c5 100644
--- a/aos/ipc_lib/queue_racer.cc
+++ b/aos/ipc_lib/queue_racer.cc
@@ -1,11 +1,20 @@
 #include "aos/ipc_lib/queue_racer.h"
 
+#include <stdio.h>
+
+#include <algorithm>
 #include <cinttypes>
 #include <cstring>
 #include <limits>
+#include <optional>
+#include <ostream>
+#include <thread>
 
+#include "absl/types/span.h"
+#include "glog/logging.h"
 #include "gtest/gtest.h"
 
+#include "aos/events/context.h"
 #include "aos/ipc_lib/event.h"
 
 namespace aos::ipc_lib {
diff --git a/aos/ipc_lib/queue_racer.h b/aos/ipc_lib/queue_racer.h
index 2cbe3ed..1c07c49 100644
--- a/aos/ipc_lib/queue_racer.h
+++ b/aos/ipc_lib/queue_racer.h
@@ -1,9 +1,18 @@
 #ifndef AOS_IPC_LIB_QUEUE_RACER_H_
 #define AOS_IPC_LIB_QUEUE_RACER_H_
 
-#include <cstring>
+#include <stdint.h>
 
+#include <atomic>
+#include <chrono>
+#include <cstring>
+#include <functional>
+#include <vector>
+
+#include "aos/ipc_lib/index.h"
 #include "aos/ipc_lib/lockless_queue.h"
+#include "aos/time/time.h"
+#include "aos/uuid.h"
 
 namespace aos::ipc_lib {
 
diff --git a/aos/ipc_lib/robust_ownership_tracker.cc b/aos/ipc_lib/robust_ownership_tracker.cc
index 13edebc..449e151 100644
--- a/aos/ipc_lib/robust_ownership_tracker.cc
+++ b/aos/ipc_lib/robust_ownership_tracker.cc
@@ -1,7 +1,5 @@
 #include "aos/ipc_lib/robust_ownership_tracker.h"
 
-#include "aos/ipc_lib/lockless_queue.h"
-
 namespace aos::ipc_lib {
 
 ::std::string RobustOwnershipTracker::DebugString() const {
diff --git a/aos/ipc_lib/robust_ownership_tracker.h b/aos/ipc_lib/robust_ownership_tracker.h
index a8bbca3..34c8b7a 100644
--- a/aos/ipc_lib/robust_ownership_tracker.h
+++ b/aos/ipc_lib/robust_ownership_tracker.h
@@ -1,11 +1,20 @@
 #ifndef AOS_IPC_LIB_ROBUST_OWNERSHIP_TRACKER_H_
 #define AOS_IPC_LIB_ROBUST_OWNERSHIP_TRACKER_H_
 
+#include <assert.h>
 #include <linux/futex.h>
+#include <stdint.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 
+#include <atomic>
+#include <limits>
+#include <optional>
+#include <ostream>
 #include <string>
 
+#include "glog/logging.h"
+
 #include "aos/ipc_lib/aos_sync.h"
 #include "aos/util/top.h"
 
diff --git a/aos/ipc_lib/robust_ownership_tracker_test.cc b/aos/ipc_lib/robust_ownership_tracker_test.cc
index b204886..8df5da0 100644
--- a/aos/ipc_lib/robust_ownership_tracker_test.cc
+++ b/aos/ipc_lib/robust_ownership_tracker_test.cc
@@ -1,5 +1,7 @@
 #include "aos/ipc_lib/robust_ownership_tracker.h"
 
+#include <errno.h>
+#include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/wait.h>
 
diff --git a/aos/ipc_lib/shared_mem.cc b/aos/ipc_lib/shared_mem.cc
index e5de366..30dd173 100644
--- a/aos/ipc_lib/shared_mem.cc
+++ b/aos/ipc_lib/shared_mem.cc
@@ -1,8 +1,8 @@
 #include "aos/ipc_lib/shared_mem.h"
 
 #include <fcntl.h>
+#include <stdint.h>
 #include <sys/mman.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <cassert>
@@ -10,11 +10,11 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <ostream>
 
 #include "glog/logging.h"
 
 #include "aos/ipc_lib/aos_sync.h"
-#include "aos/ipc_lib/core_lib.h"
 
 // the path for the shared memory segment. see shm_open(3) for restrictions
 #define AOS_SHM_NAME "/aos_shared_mem"
diff --git a/aos/ipc_lib/shared_mem.h b/aos/ipc_lib/shared_mem.h
index 89a700e..4c2d8d7 100644
--- a/aos/ipc_lib/shared_mem.h
+++ b/aos/ipc_lib/shared_mem.h
@@ -2,8 +2,6 @@
 #define _SHARED_MEM_H_
 
 #include <stddef.h>
-#include <time.h>
-#include <unistd.h>
 
 #include "aos/ipc_lib/shared_mem_types.h"
 
diff --git a/aos/ipc_lib/shm_base.cc b/aos/ipc_lib/shm_base.cc
index 22bf915..5db25d3 100644
--- a/aos/ipc_lib/shm_base.cc
+++ b/aos/ipc_lib/shm_base.cc
@@ -1,5 +1,7 @@
 #include "aos/ipc_lib/shm_base.h"
 
+#include <string>
+
 DEFINE_string(shm_base, "/dev/shm/aos",
               "Directory to place queue backing mmaped files in.");
 namespace aos::testing {
diff --git a/aos/ipc_lib/shm_base.h b/aos/ipc_lib/shm_base.h
index b70df06..1d06a69 100644
--- a/aos/ipc_lib/shm_base.h
+++ b/aos/ipc_lib/shm_base.h
@@ -1,6 +1,8 @@
 #ifndef AOS_IPC_LIB_SHM_BASE_H_
 #define AOS_IPC_LIB_SHM_BASE_H_
 
+#include <string_view>
+
 #include "gflags/gflags.h"
 
 DECLARE_string(shm_base);
diff --git a/aos/ipc_lib/signal_stress.cc b/aos/ipc_lib/signal_stress.cc
index 0559bce..b2749af 100644
--- a/aos/ipc_lib/signal_stress.cc
+++ b/aos/ipc_lib/signal_stress.cc
@@ -1,17 +1,21 @@
+#include <inttypes.h>
 #include <sys/signalfd.h>
+#include <unistd.h>
 
+#include <algorithm>
 #include <chrono>
+#include <compare>
 #include <csignal>
 #include <random>
+#include <ratio>
 #include <thread>
 
 #include "gflags/gflags.h"
+#include "glog/logging.h"
 
 #include "aos/events/epoll.h"
-#include "aos/init.h"
 #include "aos/ipc_lib/latency_lib.h"
 #include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
 #include "aos/realtime.h"
 #include "aos/time/time.h"
 
diff --git a/aos/ipc_lib/signalfd.cc b/aos/ipc_lib/signalfd.cc
index d25151d..384baaa 100644
--- a/aos/ipc_lib/signalfd.cc
+++ b/aos/ipc_lib/signalfd.cc
@@ -1,8 +1,10 @@
 #include "aos/ipc_lib/signalfd.h"
 
+#include <string.h>
 #include <sys/types.h>
 
 #include <csignal>
+#include <ostream>
 #if __has_feature(memory_sanitizer)
 #include <sanitizer/msan_interface.h>
 #endif
diff --git a/aos/ipc_lib/signalfd.h b/aos/ipc_lib/signalfd.h
index 014ad5c..a2c2549 100644
--- a/aos/ipc_lib/signalfd.h
+++ b/aos/ipc_lib/signalfd.h
@@ -1,8 +1,8 @@
 #ifndef AOS_IPC_LIB_SIGNALFD_H_
 #define AOS_IPC_LIB_SIGNALFD_H_
 
+#include <signal.h>
 #include <sys/signalfd.h>
-#include <sys/types.h>
 
 #include <initializer_list>
 
diff --git a/aos/ipc_lib/signalfd_test.cc b/aos/ipc_lib/signalfd_test.cc
index 2f349c1..025d66b 100644
--- a/aos/ipc_lib/signalfd_test.cc
+++ b/aos/ipc_lib/signalfd_test.cc
@@ -1,5 +1,8 @@
 #include "aos/ipc_lib/signalfd.h"
 
+#include <memory>
+#include <thread>
+
 #include "glog/logging.h"
 #include "gtest/gtest.h"
 
diff --git a/aos/libc/aos_strerror.cc b/aos/libc/aos_strerror.cc
index 9d270c0..3c37a19 100644
--- a/aos/libc/aos_strerror.cc
+++ b/aos/libc/aos_strerror.cc
@@ -1,7 +1,5 @@
 #include "aos/libc/aos_strerror.h"
 
-#include <sys/types.h>
-
 #include <cassert>
 #include <cstdio>
 #include <cstring>
diff --git a/aos/libc/aos_strerror_test.cc b/aos/libc/aos_strerror_test.cc
index 06a012a..5b328d7 100644
--- a/aos/libc/aos_strerror_test.cc
+++ b/aos/libc/aos_strerror_test.cc
@@ -1,6 +1,9 @@
 #include "aos/libc/aos_strerror.h"
 
+#include <string.h>
+
 #include <cerrno>
+#include <string>
 
 #include "gtest/gtest.h"
 
diff --git a/aos/libc/aos_strsignal.cc b/aos/libc/aos_strsignal.cc
index 12838b4..6a91973 100644
--- a/aos/libc/aos_strsignal.cc
+++ b/aos/libc/aos_strsignal.cc
@@ -1,5 +1,9 @@
 #include "aos/libc/aos_strsignal.h"
 
+#include <features.h>
+#include <stdio.h>
+#include <string.h>
+
 #include <csignal>
 
 #include "glog/logging.h"
diff --git a/aos/libc/aos_strsignal_test.cc b/aos/libc/aos_strsignal_test.cc
index 7e91d46..7a607bd 100644
--- a/aos/libc/aos_strsignal_test.cc
+++ b/aos/libc/aos_strsignal_test.cc
@@ -1,6 +1,10 @@
 #include "aos/libc/aos_strsignal.h"
 
+#include <string.h>
+
 #include <csignal>
+#include <functional>
+#include <memory>
 #include <thread>
 
 #include "gtest/gtest.h"
diff --git a/aos/libc/dirname.cc b/aos/libc/dirname.cc
index e73030c..324d71c 100644
--- a/aos/libc/dirname.cc
+++ b/aos/libc/dirname.cc
@@ -1,5 +1,7 @@
 #include "aos/libc/dirname.h"
 
+#include <stddef.h>
+
 namespace aos::libc {
 namespace {
 
diff --git a/aos/libc/dirname_test.cc b/aos/libc/dirname_test.cc
index 501b752..26ae23c 100644
--- a/aos/libc/dirname_test.cc
+++ b/aos/libc/dirname_test.cc
@@ -1,6 +1,7 @@
 #include "aos/libc/dirname.h"
 
 #include <libgen.h>
+#include <string.h>
 
 #include "gtest/gtest.h"
 
diff --git a/aos/logging/context.cc b/aos/logging/context.cc
index 5e5d53c..00b7f3c 100644
--- a/aos/logging/context.cc
+++ b/aos/logging/context.cc
@@ -8,11 +8,14 @@
 #include <sanitizer/msan_interface.h>
 #endif
 #include <sys/prctl.h>
-#include <sys/types.h>
 #include <unistd.h>
 
-#include <cerrno>
+#include <algorithm>
+#include <cstddef>
 #include <cstring>
+#include <limits>
+#include <optional>
+#include <ostream>
 #include <string>
 
 extern char *program_invocation_name;
@@ -20,8 +23,6 @@
 
 #include "glog/logging.h"
 
-#include "aos/logging/implementations.h"
-
 namespace aos::logging::internal {
 namespace {
 
diff --git a/aos/logging/context.h b/aos/logging/context.h
index bb53b99..10993e8 100644
--- a/aos/logging/context.h
+++ b/aos/logging/context.h
@@ -3,10 +3,7 @@
 
 #include <sys/types.h>
 
-#include <atomic>
 #include <cinttypes>
-#include <climits>
-#include <cstddef>
 #include <memory>
 #include <string_view>
 
diff --git a/aos/logging/dynamic_logging.cc b/aos/logging/dynamic_logging.cc
index a3461b5..48c9dcb 100644
--- a/aos/logging/dynamic_logging.cc
+++ b/aos/logging/dynamic_logging.cc
@@ -1,5 +1,9 @@
 #include "aos/logging/dynamic_logging.h"
 
+#include <ostream>
+#include <string_view>
+
+#include "flatbuffers/string.h"
 #include "glog/logging.h"
 
 namespace aos::logging {
diff --git a/aos/logging/dynamic_logging.h b/aos/logging/dynamic_logging.h
index e3d3a6f..352d4b7 100644
--- a/aos/logging/dynamic_logging.h
+++ b/aos/logging/dynamic_logging.h
@@ -1,9 +1,8 @@
 #include <string>
 
-#include "glog/logging.h"
-
 #include "aos/events/event_loop.h"
 #include "aos/logging/dynamic_log_command_generated.h"
+#include "aos/macros.h"
 
 // The purpose of this class is to listen for /aos aos.logging.DynamicLogCommand
 // and make changes to the log level of the current application based on that
diff --git a/aos/logging/dynamic_logging_test.cc b/aos/logging/dynamic_logging_test.cc
index 70e491d..21e0c36 100644
--- a/aos/logging/dynamic_logging_test.cc
+++ b/aos/logging/dynamic_logging_test.cc
@@ -1,12 +1,18 @@
 #include "aos/logging/dynamic_logging.h"
 
-#include <sys/stat.h>
+#include <chrono>
+#include <memory>
+#include <ostream>
 
+#include "flatbuffers/buffer.h"
+#include "flatbuffers/flatbuffer_builder.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
 
+#include "aos/configuration.h"
 #include "aos/events/event_loop.h"
 #include "aos/events/simulated_event_loop.h"
+#include "aos/flatbuffers.h"
 #include "aos/testing/path.h"
 
 using aos::testing::ArtifactPath;
diff --git a/aos/logging/implementations.cc b/aos/logging/implementations.cc
index da9e72b..042a09b 100644
--- a/aos/logging/implementations.cc
+++ b/aos/logging/implementations.cc
@@ -1,8 +1,6 @@
 #include "aos/logging/implementations.h"
 
-#include <algorithm>
 #include <chrono>
-#include <cinttypes>
 #include <cstdarg>
 
 #include "aos/logging/printf_formats.h"
diff --git a/aos/logging/implementations.h b/aos/logging/implementations.h
index 4e740f5..294750b 100644
--- a/aos/logging/implementations.h
+++ b/aos/logging/implementations.h
@@ -2,18 +2,13 @@
 #define AOS_LOGGING_IMPLEMENTATIONS_H_
 
 #include <sys/types.h>
-#include <unistd.h>
 
-#include <atomic>
-#include <climits>
-#include <cstdarg>
 #include <cstdint>
-#include <cstdio>
-#include <cstring>
 #include <functional>
 #include <memory>
 #include <string>
 #include <string_view>
+#include <utility>
 
 #include "aos/logging/context.h"
 #include "aos/logging/interface.h"
diff --git a/aos/logging/implementations_test.cc b/aos/logging/implementations_test.cc
index 693a91e..08fdc7b 100644
--- a/aos/logging/implementations_test.cc
+++ b/aos/logging/implementations_test.cc
@@ -1,5 +1,8 @@
 #include "aos/logging/implementations.h"
 
+#include <signal.h>
+#include <stdlib.h>
+
 #include <chrono>
 #include <cinttypes>
 #include <string>
diff --git a/aos/logging/interface.cc b/aos/logging/interface.cc
index e0ce6a8..326f28e 100644
--- a/aos/logging/interface.cc
+++ b/aos/logging/interface.cc
@@ -1,9 +1,12 @@
 #include "aos/logging/interface.h"
 
+#include <algorithm>
 #include <cstdarg>
 #include <cstdio>
 #include <cstring>
-#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
 #include <type_traits>
 
 #include "glog/logging.h"
@@ -11,6 +14,7 @@
 #include "aos/die.h"
 #include "aos/logging/context.h"
 #include "aos/logging/implementations.h"
+#include "aos/time/time.h"
 
 namespace aos::logging {
 namespace internal {
diff --git a/aos/logging/interface.h b/aos/logging/interface.h
index d2663e7..9889078 100644
--- a/aos/logging/interface.h
+++ b/aos/logging/interface.h
@@ -1,9 +1,9 @@
 #ifndef AOS_LOGGING_INTERFACE_H_
 #define AOS_LOGGING_INTERFACE_H_
 
+#include <stddef.h>
+
 #include <cstdarg>
-#include <functional>
-#include <string>
 #include <string_view>
 
 #include "aos/logging/logging.h"
@@ -13,8 +13,6 @@
 
 namespace aos {
 
-struct MessageType;
-
 namespace logging {
 
 // Takes a message and logs it. It will set everything up and then call DoLog
diff --git a/aos/logging/log_namer.cc b/aos/logging/log_namer.cc
index 412c74e..4b65687 100644
--- a/aos/logging/log_namer.cc
+++ b/aos/logging/log_namer.cc
@@ -1,23 +1,19 @@
 #include "aos/logging/log_namer.h"
 
 #include <dirent.h>
-#include <fcntl.h>
 #include <mntent.h>
-#include <pwd.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <cerrno>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
-#include <ctime>
+#include <ostream>
 #include <string>
 
+#include "gflags/gflags.h"
 #include "glog/logging.h"
 
-#include "aos/configuration.h"
-
 #if defined(__clang)
 #pragma clang diagnostic ignored "-Wformat-nonliteral"
 #elif defined(__GNUC__)
diff --git a/aos/scoped/scoped_fd.cc b/aos/scoped/scoped_fd.cc
index 846736d..f2375ab 100644
--- a/aos/scoped/scoped_fd.cc
+++ b/aos/scoped/scoped_fd.cc
@@ -1,5 +1,9 @@
 #include "aos/scoped/scoped_fd.h"
 
+#include <unistd.h>
+
+#include <ostream>
+
 #include "glog/logging.h"
 
 namespace aos {
diff --git a/aos/scoped/scoped_fd.h b/aos/scoped/scoped_fd.h
index e393e89..bd52657 100644
--- a/aos/scoped/scoped_fd.h
+++ b/aos/scoped/scoped_fd.h
@@ -1,10 +1,6 @@
 #ifndef AOS_SCOPED_SCOPED_FD_H_
 #define AOS_SCOPED_SCOPED_FD_H_
 
-#include <unistd.h>
-
-#include "aos/macros.h"
-
 namespace aos {
 
 // Smart "pointer" (container) for a file descriptor.
diff --git a/aos/stl_mutex/stl_mutex.h b/aos/stl_mutex/stl_mutex.h
index 5aaa6b6..c719805 100644
--- a/aos/stl_mutex/stl_mutex.h
+++ b/aos/stl_mutex/stl_mutex.h
@@ -2,6 +2,7 @@
 #define AOS_STL_MUTEX_H_
 
 #include <mutex>
+#include <ostream>
 
 #include "glog/logging.h"
 
diff --git a/aos/stl_mutex/stl_mutex_test.cc b/aos/stl_mutex/stl_mutex_test.cc
index 5343974..0eb50d4 100644
--- a/aos/stl_mutex/stl_mutex_test.cc
+++ b/aos/stl_mutex/stl_mutex_test.cc
@@ -1,5 +1,7 @@
 #include "aos/stl_mutex/stl_mutex.h"
 
+#include <memory>
+
 #include "gtest/gtest.h"
 
 #include "aos/die.h"
diff --git a/aos/testing/gtest_main.cc b/aos/testing/gtest_main.cc
index 6071bde..1940988 100644
--- a/aos/testing/gtest_main.cc
+++ b/aos/testing/gtest_main.cc
@@ -1,7 +1,3 @@
-#include <getopt.h>
-
-#include <iostream>
-
 #include "gflags/gflags.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
diff --git a/aos/testing/test_logging.cc b/aos/testing/test_logging.cc
index 56875bb..15cb64b 100644
--- a/aos/testing/test_logging.cc
+++ b/aos/testing/test_logging.cc
@@ -1,6 +1,12 @@
 #include "aos/testing/test_logging.h"
 
+#include <string.h>
+
+#include <algorithm>
 #include <cstdio>
+#include <memory>
+#include <mutex>
+#include <string_view>
 #include <vector>
 
 #include "absl/base/call_once.h"
@@ -8,6 +14,7 @@
 
 #include "aos/logging/implementations.h"
 #include "aos/stl_mutex/stl_mutex.h"
+#include "aos/time/time.h"
 
 using ::aos::logging::LogMessage;
 
diff --git a/aos/testing/test_logging.h b/aos/testing/test_logging.h
index 3671aa1..b7e8977 100644
--- a/aos/testing/test_logging.h
+++ b/aos/testing/test_logging.h
@@ -1,8 +1,6 @@
 #ifndef AOS_TESTING_TEST_LOGGING_H_
 #define AOS_TESTING_TEST_LOGGING_H_
 
-#include "aos/time/time.h"
-
 namespace aos::testing {
 
 // Enables the logging framework for use during a gtest test.
diff --git a/aos/testing/test_logging_test.cc b/aos/testing/test_logging_test.cc
index 5731545..b37bec4 100644
--- a/aos/testing/test_logging_test.cc
+++ b/aos/testing/test_logging_test.cc
@@ -1,5 +1,6 @@
 #include "aos/testing/test_logging.h"
 
+#include <memory>
 #include <thread>
 
 #include "gtest/gtest.h"
diff --git a/aos/testing/test_shm.cc b/aos/testing/test_shm.cc
index 1b168ea..4922b63 100644
--- a/aos/testing/test_shm.cc
+++ b/aos/testing/test_shm.cc
@@ -1,9 +1,9 @@
 #include "aos/testing/test_shm.h"
 
+#include <stddef.h>
 #include <sys/mman.h>
 
-#include "gflags/gflags.h"
-
+#include "aos/ipc_lib/shared_mem.h"
 #include "aos/logging/logging.h"
 #include "aos/testing/test_logging.h"
 
diff --git a/aos/testing/test_shm.h b/aos/testing/test_shm.h
index 658a72d..24c87ce 100644
--- a/aos/testing/test_shm.h
+++ b/aos/testing/test_shm.h
@@ -1,7 +1,7 @@
 #ifndef AOS_TESTING_TEST_SHM_H_
 #define AOS_TESTING_TEST_SHM_H_
 
-#include "aos/ipc_lib/shared_mem.h"
+#include "aos/ipc_lib/shared_mem_types.h"
 
 namespace aos::testing {
 
diff --git a/aos/time/time.cc b/aos/time/time.cc
index e75fe45..fe8422b 100644
--- a/aos/time/time.cc
+++ b/aos/time/time.cc
@@ -1,11 +1,16 @@
 #include "aos/time/time.h"
 
+#include <ctype.h>
+#include <errno.h>
+#include <sys/time.h>
+
 #include <algorithm>
 #include <chrono>
-#include <cinttypes>
-#include <cstring>
+#include <compare>
+#include <cstdint>
 #include <ctime>
 #include <iomanip>
+#include <ratio>
 #include <sstream>
 
 #ifdef __linux__
diff --git a/aos/time/time.h b/aos/time/time.h
index 8462625..f0b3a06 100644
--- a/aos/time/time.h
+++ b/aos/time/time.h
@@ -1,14 +1,14 @@
 #ifndef AOS_TIME_H_
 #define AOS_TIME_H_
 
-#include <sys/time.h>
-
 #include <chrono>
-#include <cstdint>
 #include <ctime>
+#include <limits>
 #include <optional>
 #include <ostream>
-#include <thread>
+#include <string>
+#include <string_view>
+#include <thread>  // IWYU pragma: keep
 
 namespace aos {
 
diff --git a/aos/time/time_test.cc b/aos/time/time_test.cc
index 97116b0..eb97859 100644
--- a/aos/time/time_test.cc
+++ b/aos/time/time_test.cc
@@ -1,13 +1,11 @@
 #include "aos/time/time.h"
 
-#include <thread>
+#include <sys/time.h>
 
-#include "glog/logging.h"
+#include <memory>
+
 #include "gtest/gtest.h"
 
-#include "aos/macros.h"
-#include "aos/util/death_test_log_implementation.h"
-
 namespace aos::time::testing {
 
 namespace chrono = std::chrono;
diff --git a/aos/type_traits/type_traits_test.cpp b/aos/type_traits/type_traits_test.cpp
index 4c060bb..dc18887 100644
--- a/aos/type_traits/type_traits_test.cpp
+++ b/aos/type_traits/type_traits_test.cpp
@@ -1,5 +1,9 @@
 #include "aos/type_traits/type_traits.h"
 
+#include <stdint.h>
+#include <string.h>
+#include <memory>
+
 #include "gtest/gtest.h"
 
 namespace aos {
diff --git a/aos/vision/blob/codec.cc b/aos/vision/blob/codec.cc
index be11281..d3ab72d 100644
--- a/aos/vision/blob/codec.cc
+++ b/aos/vision/blob/codec.cc
@@ -1,5 +1,10 @@
 #include "aos/vision/blob/codec.h"
 
+#include <stddef.h>
+
+#include <utility>
+#include <vector>
+
 namespace aos::vision {
 
 size_t CalculateSize(const BlobList &blob_list) {
diff --git a/aos/vision/blob/codec.h b/aos/vision/blob/codec.h
index 5872b44..1656b4a 100644
--- a/aos/vision/blob/codec.h
+++ b/aos/vision/blob/codec.h
@@ -1,6 +1,9 @@
 #ifndef _AOS_VISION_BLOB_CODEC_H_
 #define _AOS_VISION_BLOB_CODEC_H_
 
+#include <stdint.h>
+#include <string.h>
+
 #include <string>
 
 #include "aos/vision/blob/range_image.h"
diff --git a/aos/vision/blob/codec_test.cc b/aos/vision/blob/codec_test.cc
index 889dfbb..3d483a4 100644
--- a/aos/vision/blob/codec_test.cc
+++ b/aos/vision/blob/codec_test.cc
@@ -1,6 +1,10 @@
 #include "aos/vision/blob/codec.h"
 
+#include <stddef.h>
+
 #include <algorithm>
+#include <utility>
+#include <vector>
 
 #include "gtest/gtest.h"
 
diff --git a/aos/vision/blob/contour.cc b/aos/vision/blob/contour.cc
index 5e82d4c..a789b77 100644
--- a/aos/vision/blob/contour.cc
+++ b/aos/vision/blob/contour.cc
@@ -1,5 +1,7 @@
 #include "aos/vision/blob/contour.h"
 
+#include <vector>
+
 namespace aos::vision {
 
 namespace {
diff --git a/aos/vision/blob/find_blob.cc b/aos/vision/blob/find_blob.cc
index 8ee8e92..29b1640 100644
--- a/aos/vision/blob/find_blob.cc
+++ b/aos/vision/blob/find_blob.cc
@@ -1,5 +1,8 @@
 #include "aos/vision/blob/find_blob.h"
 
+#include <utility>
+#include <vector>
+
 #include "aos/vision/blob/disjoint_set.h"
 
 namespace aos::vision {
diff --git a/aos/vision/blob/hierarchical_contour_merge.cc b/aos/vision/blob/hierarchical_contour_merge.cc
index 06c34d4..c5d3eb9 100644
--- a/aos/vision/blob/hierarchical_contour_merge.cc
+++ b/aos/vision/blob/hierarchical_contour_merge.cc
@@ -1,7 +1,11 @@
 #include "aos/vision/blob/hierarchical_contour_merge.h"
 
+#include <stdint.h>
+
+#include <algorithm>
 #include <cmath>
 #include <queue>
+#include <utility>
 
 #include "aos/vision/blob/disjoint_set.h"
 
diff --git a/aos/vision/blob/move_scale.cc b/aos/vision/blob/move_scale.cc
index 45337fc..ac2c30a 100644
--- a/aos/vision/blob/move_scale.cc
+++ b/aos/vision/blob/move_scale.cc
@@ -1,5 +1,8 @@
 #include "aos/vision/blob/move_scale.h"
 
+#include <algorithm>
+#include <utility>
+
 namespace aos::vision {
 
 RangeImage MoveScale(const RangeImage &img, int dx, int dy, int scale) {
diff --git a/aos/vision/blob/move_scale.h b/aos/vision/blob/move_scale.h
index 94a9e9d..4f38bcf 100644
--- a/aos/vision/blob/move_scale.h
+++ b/aos/vision/blob/move_scale.h
@@ -1,7 +1,6 @@
 #ifndef AOS_VISION_BLOB_MOVE_SCALE_H_
 #define AOS_VISION_BLOB_MOVE_SCALE_H_
 
-#include <limits>
 #include <vector>
 
 #include "aos/vision/blob/range_image.h"
diff --git a/aos/vision/blob/range_image.cc b/aos/vision/blob/range_image.cc
index 92f77cc..ef9cc19 100644
--- a/aos/vision/blob/range_image.cc
+++ b/aos/vision/blob/range_image.cc
@@ -1,7 +1,9 @@
 #include "aos/vision/blob/range_image.h"
 
+#include <stdio.h>
+
 #include <algorithm>
-#include <cmath>
+#include <ostream>
 
 namespace aos::vision {
 namespace {
diff --git a/aos/vision/blob/range_image.h b/aos/vision/blob/range_image.h
index 10e801f..3e8c35c 100644
--- a/aos/vision/blob/range_image.h
+++ b/aos/vision/blob/range_image.h
@@ -1,6 +1,9 @@
 #ifndef _AOS_VISION_BLOB_RANGE_IMAGE_H_
 #define _AOS_VISION_BLOB_RANGE_IMAGE_H_
 
+#include <iosfwd>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "aos/vision/image/image_types.h"
diff --git a/aos/vision/blob/test_utils.cc b/aos/vision/blob/test_utils.cc
index 0e7240f..54d5fe6 100644
--- a/aos/vision/blob/test_utils.cc
+++ b/aos/vision/blob/test_utils.cc
@@ -1,5 +1,8 @@
 #include "aos/vision/blob/test_utils.h"
 
+#include <utility>
+#include <vector>
+
 namespace aos::vision {
 
 RangeImage LoadFromTestData(int mini, const char *data) {
diff --git a/aos/vision/blob/threshold.cc b/aos/vision/blob/threshold.cc
index 5403bbd..d25ab44 100644
--- a/aos/vision/blob/threshold.cc
+++ b/aos/vision/blob/threshold.cc
@@ -1,5 +1,7 @@
 #include "aos/vision/blob/threshold.h"
 
+#include <string.h>
+
 #include "aos/logging/logging.h"
 
 namespace aos::vision {
diff --git a/aos/vision/blob/threshold.h b/aos/vision/blob/threshold.h
index 95330c5..effd321 100644
--- a/aos/vision/blob/threshold.h
+++ b/aos/vision/blob/threshold.h
@@ -1,11 +1,16 @@
 #ifndef AOS_VISION_BLOB_THRESHOLD_H_
 #define AOS_VISION_BLOB_THRESHOLD_H_
 
+#include <stdint.h>
+
 #include <array>
 #include <condition_variable>
 #include <functional>
 #include <mutex>
 #include <thread>
+#include <type_traits>
+#include <utility>
+#include <vector>
 
 #include "aos/vision/blob/range_image.h"
 #include "aos/vision/image/image_types.h"
diff --git a/aos/vision/blob/threshold_test.cc b/aos/vision/blob/threshold_test.cc
index 4b23f3e..4ec519c 100644
--- a/aos/vision/blob/threshold_test.cc
+++ b/aos/vision/blob/threshold_test.cc
@@ -1,9 +1,11 @@
 #include "aos/vision/blob/threshold.h"
 
+#include <stddef.h>
+
+#include <limits>
 #include <random>
 #include <vector>
 
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "aos/vision/blob/range_image.h"
diff --git a/aos/vision/blob/transpose.cc b/aos/vision/blob/transpose.cc
index b59e8c5..25fd207 100644
--- a/aos/vision/blob/transpose.cc
+++ b/aos/vision/blob/transpose.cc
@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <utility>
 
 namespace aos::vision {
 
diff --git a/aos/vision/blob/transpose.h b/aos/vision/blob/transpose.h
index d2b609e..e2ff8b4 100644
--- a/aos/vision/blob/transpose.h
+++ b/aos/vision/blob/transpose.h
@@ -1,6 +1,8 @@
 #ifndef AOS_VISION_BLOB_TRANSPOSE_H_
 #define AOS_VISION_BLOB_TRANSPOSE_H_
 
+#include <vector>
+
 #include "aos/vision/blob/range_image.h"
 
 namespace aos::vision {
diff --git a/aos/vision/blob/transpose_test.cc b/aos/vision/blob/transpose_test.cc
index a4a9705..9eb9bac 100644
--- a/aos/vision/blob/transpose_test.cc
+++ b/aos/vision/blob/transpose_test.cc
@@ -1,6 +1,5 @@
 #include "aos/vision/blob/transpose.h"
 
-#include <algorithm>
 #include <string>
 
 #include "gtest/gtest.h"
diff --git a/aos/vision/events/epoll_events.cc b/aos/vision/events/epoll_events.cc
index 1a5ad7e..ce3e5af 100644
--- a/aos/vision/events/epoll_events.cc
+++ b/aos/vision/events/epoll_events.cc
@@ -1,9 +1,6 @@
 #include "aos/vision/events/epoll_events.h"
 
-#include <fcntl.h>
 #include <sys/epoll.h>
-#include <sys/socket.h>
-#include <sys/types.h>
 
 #include <cstring>
 #include <vector>
diff --git a/aos/vision/events/epoll_events.h b/aos/vision/events/epoll_events.h
index e3eeb85..e78db53 100644
--- a/aos/vision/events/epoll_events.h
+++ b/aos/vision/events/epoll_events.h
@@ -3,9 +3,10 @@
 
 #include <sys/epoll.h>
 
+#include <chrono>
 #include <climits>
+#include <compare>
 #include <cstdint>
-#include <memory>
 #include <vector>
 
 #include "aos/scoped/scoped_fd.h"
diff --git a/aos/vision/events/tcp_client.cc b/aos/vision/events/tcp_client.cc
index 65b646c..3c64add 100644
--- a/aos/vision/events/tcp_client.cc
+++ b/aos/vision/events/tcp_client.cc
@@ -4,14 +4,11 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <strings.h>
 #include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
 
-#include <cerrno>
 #include <cstdio>
 #include <cstdlib>
-#include <cstring>
 
 #include "aos/logging/logging.h"
 
diff --git a/aos/vision/events/tcp_client.h b/aos/vision/events/tcp_client.h
index 16d7265..e89e0fe 100644
--- a/aos/vision/events/tcp_client.h
+++ b/aos/vision/events/tcp_client.h
@@ -1,7 +1,6 @@
 #ifndef _AOS_VISION_DEBUG_TCP_CLIENT_H_
 #define _AOS_VISION_DEBUG_TCP_CLIENT_H_
 
-#include <memory>
 #include <string>
 
 #include "aos/vision/events/epoll_events.h"
diff --git a/aos/vision/events/tcp_server.cc b/aos/vision/events/tcp_server.cc
index fa2a369..38e2874 100644
--- a/aos/vision/events/tcp_server.cc
+++ b/aos/vision/events/tcp_server.cc
@@ -2,17 +2,13 @@
 
 #include <arpa/inet.h>
 #include <fcntl.h>
-#include <netdb.h>
 #include <netinet/in.h>
-#include <sys/epoll.h>
+#include <stdint.h>
+#include <strings.h>
 #include <sys/socket.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <cerrno>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
 
 #include "aos/logging/logging.h"
 
diff --git a/aos/vision/events/tcp_server.h b/aos/vision/events/tcp_server.h
index 49a5a8c..b901077 100644
--- a/aos/vision/events/tcp_server.h
+++ b/aos/vision/events/tcp_server.h
@@ -2,19 +2,21 @@
 #define _AOS_VISION_EVENTS_TCP_SERVER_H_
 
 #include <memory>
-#include <vector>
 
 #include "aos/vision/events/epoll_events.h"
 #include "aos/vision/events/intrusive_free_list.h"
 
 namespace aos::events {
 
+// IWYU pragma: begin_keep
 // Non-templatized base class of TCP server.
 // TCPServer implements Construct which specializes the client connection
 // based on the specific use-case.
 template <class T>
 class TCPServer;
+// IWYU pragma: end_keep
 class SocketConnection;
+
 class TCPServerBase : public EpollEvent {
  public:
   TCPServerBase(int fd) : EpollEvent(fd) {}
diff --git a/aos/vision/events/udp.cc b/aos/vision/events/udp.cc
index cf7ad23..1b1a053 100644
--- a/aos/vision/events/udp.cc
+++ b/aos/vision/events/udp.cc
@@ -1,6 +1,11 @@
 #include "aos/vision/events/udp.h"
 
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
 #include <cstring>
+#include <ostream>
 
 #include "glog/logging.h"
 
diff --git a/aos/vision/events/udp.h b/aos/vision/events/udp.h
index 4acb04d..bbe5a54 100644
--- a/aos/vision/events/udp.h
+++ b/aos/vision/events/udp.h
@@ -1,13 +1,7 @@
 #ifndef AOS_VISION_EVENTS_UDP_H_
 #define AOS_VISION_EVENTS_UDP_H_
 
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <cmath>
 #include <string>
-#include <vector>
 
 #include "aos/macros.h"
 #include "aos/scoped/scoped_fd.h"
diff --git a/aos/vision/image/image_dataset.cc b/aos/vision/image/image_dataset.cc
index 82a8886..8eade2b 100644
--- a/aos/vision/image/image_dataset.cc
+++ b/aos/vision/image/image_dataset.cc
@@ -1,5 +1,8 @@
 #include "aos/vision/image/image_dataset.h"
 
+#include <stdlib.h>
+
+#include <cstdio>
 #include <fstream>
 
 #include "aos/vision/image/image_types.h"
diff --git a/aos/vision/image/image_stream.h b/aos/vision/image/image_stream.h
index 5a45c6f..797178f 100644
--- a/aos/vision/image/image_stream.h
+++ b/aos/vision/image/image_stream.h
@@ -1,10 +1,17 @@
 #ifndef AOS_VISION_IMAGE_IMAGE_STREAM_H_
 #define AOS_VISION_IMAGE_IMAGE_STREAM_H_
 
+#include <chrono>
+#include <functional>
 #include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
 
+#include "aos/time/time.h"
 #include "aos/vision/events/epoll_events.h"
 #include "aos/vision/image/camera_params.pb.h"
+#include "aos/vision/image/image_types.h"
 #include "aos/vision/image/reader.h"
 
 namespace aos::vision {
diff --git a/aos/vision/image/jpeg_routines.cc b/aos/vision/image/jpeg_routines.cc
index 9d46895..1c1f738 100644
--- a/aos/vision/image/jpeg_routines.cc
+++ b/aos/vision/image/jpeg_routines.cc
@@ -1,13 +1,8 @@
 #include "aos/vision/image/jpeg_routines.h"
 
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <cerrno>
 #include <csetjmp>
 #include <cstdint>
 #include <cstdio>
-#include <cstdlib>
 #include <cstring>
 
 #include "third_party/libjpeg/jpeglib.h"
diff --git a/aos/vision/image/jpeg_routines.h b/aos/vision/image/jpeg_routines.h
index 4bac976..755a205 100644
--- a/aos/vision/image/jpeg_routines.h
+++ b/aos/vision/image/jpeg_routines.h
@@ -1,11 +1,6 @@
 #ifndef _AOS_VISION_IMAGE_JPEGROUTINES_H_
 #define _AOS_VISION_IMAGE_JPEGROUTINES_H_
 
-#include <unistd.h>
-
-#include <cstdio>
-#include <cstdlib>
-
 #include "aos/vision/image/image_types.h"
 
 namespace aos::vision {
diff --git a/aos/vision/image/reader.cc b/aos/vision/image/reader.cc
index 3fbcd1d..d216490 100644
--- a/aos/vision/image/reader.cc
+++ b/aos/vision/image/reader.cc
@@ -1,20 +1,19 @@
 #include "aos/vision/image/reader.h"
 
 #include <fcntl.h>
-#include <malloc.h>
+#include <linux/v4l2-controls.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include <cerrno>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <utility>
 
 #include "aos/logging/logging.h"
 #include "aos/time/time.h"
+#include "aos/vision/image/V4L2.h"
 
 #define CLEAR(x) memset(&(x), 0, sizeof(x))
 
diff --git a/aos/vision/image/reader.h b/aos/vision/image/reader.h
index 8f7b6b7..8791403 100644
--- a/aos/vision/image/reader.h
+++ b/aos/vision/image/reader.h
@@ -1,12 +1,13 @@
 #ifndef AOS_VISION_IMAGE_READER_H_
 #define AOS_VISION_IMAGE_READER_H_
 
+#include <linux/videodev2.h>
+
 #include <cinttypes>
 #include <functional>
 #include <string>
 
 #include "aos/time/time.h"
-#include "aos/vision/image/V4L2.h"
 #include "aos/vision/image/camera_params.pb.h"
 #include "aos/vision/image/image_types.h"
 
@@ -52,6 +53,7 @@
   // The number of buffers currently queued in v4l2.
   uint32_t queued_;
   struct Buffer;
+
   // TODO(parker): This should be a smart pointer, but it cannot
   // because the buffers are not ummapped.
   Buffer *buffers_;
diff --git a/aos/vision/math/vector.h b/aos/vision/math/vector.h
index ce29901..04203e6 100644
--- a/aos/vision/math/vector.h
+++ b/aos/vision/math/vector.h
@@ -1,9 +1,12 @@
 #ifndef AOS_VISION_MATH_VECTOR_H_
 #define AOS_VISION_MATH_VECTOR_H_
 
+#include <algorithm>
 #include <cmath>
+#include <utility>
 
-#include "Eigen/Dense"
+#include "Eigen/Core"
+#include "Eigen/Geometry"
 
 namespace aos::vision {
 
diff --git a/aos/vision/math/vector_test.cc b/aos/vision/math/vector_test.cc
index 214a50c..d137d5e 100644
--- a/aos/vision/math/vector_test.cc
+++ b/aos/vision/math/vector_test.cc
@@ -1,5 +1,7 @@
 #include "aos/vision/math/vector.h"
 
+#include <memory>
+
 #include "gtest/gtest.h"
 
 namespace aos::vision::testing {
diff --git a/aos/vision/tools/camera_primer.cc b/aos/vision/tools/camera_primer.cc
index fcd7d0b..d409d2d 100644
--- a/aos/vision/tools/camera_primer.cc
+++ b/aos/vision/tools/camera_primer.cc
@@ -1,7 +1,13 @@
-#include "aos/logging/implementations.h"
-#include "aos/logging/logging.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "aos/time/time.h"
 #include "aos/vision/events/epoll_events.h"
+#include "aos/vision/image/camera_params.pb.h"
 #include "aos/vision/image/image_stream.h"
+#include "aos/vision/image/image_types.h"
 
 class ImageStream : public aos::vision::ImageStreamEvent {
  public:
diff --git a/frc971/codelab/basic.cc b/frc971/codelab/basic.cc
index a78da41..9576e6b 100644
--- a/frc971/codelab/basic.cc
+++ b/frc971/codelab/basic.cc
@@ -3,12 +3,12 @@
 namespace frc971::codelab {
 
 Basic::Basic(::aos::EventLoop *event_loop, const ::std::string &name)
-    : frc971::controls::ControlLoop<Goal, Position, Status, Output>(event_loop,
-                                                                    name) {}
+    : frc971::controls::ControlLoop<Goal, Position, StatusStatic, OutputStatic>(
+          event_loop, name) {}
 
 void Basic::RunIteration(const Goal *goal, const Position *position,
-                         aos::Sender<Output>::Builder *output,
-                         aos::Sender<Status>::Builder *status) {
+                         aos::Sender<OutputStatic>::StaticBuilder *output,
+                         aos::Sender<StatusStatic>::StaticBuilder *status) {
   // FIX HERE: Set the intake_voltage to 12 Volts when
   // intake is requested (via intake in goal). Make sure not to set
   // the motor to anything but 0 V when the limit_sensor is pressed.
@@ -19,20 +19,18 @@
   (void)goal, (void)position;
 
   if (output != nullptr) {
-    Output::Builder builder = output->MakeBuilder<Output>();
-
     // FIX HERE: As of now, this sets the intake voltage to 0 in
     // all circumstances. Add to this code to output a different
     // intake voltage depending on the circumstances to make the
     // tests pass.
-    builder.add_intake_voltage(0.0);
+    output->get()->set_intake_voltage(0.0);
 
     // Ignore the return value of Send
-    (void)output->Send(builder.Finish());
+    (void)output->CheckOk(output->Send());
   }
 
   if (status != nullptr) {
-    Status::Builder builder = status->MakeBuilder<Status>();
+    (void)status;
     // FIX HERE: Fill out the Status message! In order to fill the
     // information in the message, use the add_<name of the field>() method
     // on the builder, just like we do with the Output message above.
@@ -40,7 +38,6 @@
     // the name of the field.
 
     // Ignore the return value of Send
-    (void)status->Send(builder.Finish());
   }
 }
 
diff --git a/frc971/codelab/basic.h b/frc971/codelab/basic.h
index 6439d84..3073012 100644
--- a/frc971/codelab/basic.h
+++ b/frc971/codelab/basic.h
@@ -3,23 +3,24 @@
 
 #include "aos/time/time.h"
 #include "frc971/codelab/basic_goal_generated.h"
-#include "frc971/codelab/basic_output_generated.h"
+#include "frc971/codelab/basic_output_static.h"
 #include "frc971/codelab/basic_position_generated.h"
-#include "frc971/codelab/basic_status_generated.h"
+#include "frc971/codelab/basic_status_static.h"
 #include "frc971/control_loops/control_loop.h"
 
 namespace frc971::codelab {
 
 class Basic
-    : public ::frc971::controls::ControlLoop<Goal, Position, Status, Output> {
+    : public ::frc971::controls::ControlLoop<Goal, Position, StatusStatic,
+                                             OutputStatic> {
  public:
   explicit Basic(::aos::EventLoop *event_loop,
                  const ::std::string &name = "/codelab");
 
  protected:
   void RunIteration(const Goal *goal, const Position *position,
-                    aos::Sender<Output>::Builder *output,
-                    aos::Sender<Status>::Builder *status) override;
+                    aos::Sender<OutputStatic>::StaticBuilder *output,
+                    aos::Sender<StatusStatic>::StaticBuilder *status) override;
 };
 
 }  // namespace frc971::codelab
diff --git a/frc971/control_loops/BUILD b/frc971/control_loops/BUILD
index 9ce0e99..f5a720f 100644
--- a/frc971/control_loops/BUILD
+++ b/frc971/control_loops/BUILD
@@ -87,6 +87,7 @@
         "//aos/actions:action_lib",
         "//aos/events:event_loop",
         "//aos/events:shm_event_loop",
+        "//aos/flatbuffers:static_table",
         "//aos/logging",
         "//aos/time",
         "//aos/util:log_interval",
diff --git a/frc971/control_loops/control_loop-tmpl.h b/frc971/control_loops/control_loop-tmpl.h
index d86c97c..bf2a223 100644
--- a/frc971/control_loops/control_loop-tmpl.h
+++ b/frc971/control_loops/control_loop-tmpl.h
@@ -20,9 +20,8 @@
           class OutputType>
 void ControlLoop<GoalType, PositionType, StatusType,
                  OutputType>::ZeroOutputs() {
-  typename ::aos::Sender<OutputType>::Builder builder =
-      output_sender_.MakeBuilder();
-  builder.CheckOk(builder.Send(Zero(&builder)));
+  auto builder = BuilderType<OutputType>::MakeBuilder(&output_sender_);
+  BuilderType<OutputType>::SendZeroFlatbuffer(this, &builder);
 }
 
 template <class GoalType, class PositionType, class StatusType,
@@ -73,21 +72,20 @@
     outputs_enabled = false;
   }
 
-  typename ::aos::Sender<StatusType>::Builder status =
-      status_sender_.MakeBuilder();
+  StatusBuilder status = BuilderType<StatusType>::MakeBuilder(&status_sender_);
   if (outputs_enabled) {
-    typename ::aos::Sender<OutputType>::Builder output =
-        output_sender_.MakeBuilder();
+    OutputBuilder output =
+        BuilderType<OutputType>::MakeBuilder(&output_sender_);
     RunIteration(goal, &position, &output, &status);
 
-    output.CheckSent();
+    BuilderType<OutputType>::CheckSent(&output);
   } else {
     // The outputs are disabled, so pass nullptr in for the output.
     RunIteration(goal, &position, nullptr, &status);
     ZeroOutputs();
   }
 
-  status.CheckSent();
+  BuilderType<StatusType>::CheckSent(&status);
 }
 
 }  // namespace frc971::controls
diff --git a/frc971/control_loops/control_loop.h b/frc971/control_loops/control_loop.h
index c7b8a6d..e9892a3 100644
--- a/frc971/control_loops/control_loop.h
+++ b/frc971/control_loops/control_loop.h
@@ -6,6 +6,7 @@
 
 #include "aos/actions/actor.h"
 #include "aos/events/event_loop.h"
+#include "aos/flatbuffers/static_table.h"
 #include "aos/time/time.h"
 #include "aos/util/log_interval.h"
 #include "frc971/input/joystick_state_generated.h"
@@ -17,6 +18,56 @@
 constexpr ::std::chrono::nanoseconds kLoopFrequency =
     aos::common::actions::kLoopFrequency;
 
+// In order to support using both "raw" and the static flatbuffer APIs with the
+// outputs of the ControlLoop class (i.e., the Output and Status messages), we
+// need to provide different compile-time code for each option. In order to do
+// this, we set up two different specializations of the BuilderType class, and
+// will select the appropriate one depending on the type of the flatbuffer. The
+// methods and types within BuilderType are then used within the ControlLoop
+// class whenever we need to call methods associated with the flatbuffers or
+// senders themselves.
+template <typename T, class Enable = void>
+struct BuilderType;
+
+template <class GoalType, class PositionType, class StatusType,
+          class OutputType>
+class ControlLoop;
+
+template <typename T>
+struct BuilderType<T, typename std::enable_if_t<
+                          std::is_base_of<flatbuffers::Table, T>::value>> {
+  typedef aos::Sender<T>::Builder Builder;
+  typedef T FlatbufferType;
+  static Builder MakeBuilder(aos::Sender<T> *sender) {
+    return sender->MakeBuilder();
+  }
+  template <class GoalType, class PositionType, class StatusType,
+            class OutputType>
+  static void SendZeroFlatbuffer(
+      ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
+      Builder *builder);
+  static void CheckSent(Builder *builder) { builder->CheckSent(); }
+};
+
+template <typename T>
+struct BuilderType<
+    T, typename std::enable_if_t<std::is_base_of<aos::fbs::Table, T>::value>> {
+  typedef aos::Sender<T>::StaticBuilder Builder;
+  typedef T::Flatbuffer FlatbufferType;
+  static Builder MakeBuilder(aos::Sender<T> *sender) {
+    return sender->MakeStaticBuilder();
+  }
+  template <class GoalType, class PositionType, class StatusType,
+            class OutputType>
+  static void SendZeroFlatbuffer(
+      ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
+      Builder *builder);
+  static void CheckSent(Builder *builder) {
+    // TODO (niko) create a CheckSent() function for static flatbuffers
+    (void)builder;
+  }
+};
+
 // Provides helper methods to assist in writing control loops.
 // It will then call the RunIteration method every cycle that it has enough
 // valid data for the control loop to run.
@@ -24,6 +75,10 @@
           class OutputType>
 class ControlLoop {
  public:
+  using StatusBuilder = typename BuilderType<StatusType>::Builder;
+  using OutputBuilder = typename BuilderType<OutputType>::Builder;
+  using OutputFlatbufferType = typename BuilderType<OutputType>::FlatbufferType;
+
   ControlLoop(aos::EventLoop *event_loop, const ::std::string &name)
       : event_loop_(event_loop), name_(name) {
     output_sender_ = event_loop_->MakeSender<OutputType>(name_);
@@ -63,9 +118,9 @@
   // Sets the output to zero.
   // Override this if a value of zero (or false) is not "off" for this
   // subsystem.
-  virtual flatbuffers::Offset<OutputType> Zero(
-      typename ::aos::Sender<OutputType>::Builder *builder) {
-    return builder->template MakeBuilder<OutputType>().Finish();
+  virtual flatbuffers::Offset<OutputFlatbufferType> Zero(
+      typename ::aos::Sender<OutputFlatbufferType>::Builder *builder) {
+    return builder->template MakeBuilder<OutputFlatbufferType>().Finish();
   }
 
  protected:
@@ -87,10 +142,8 @@
   // output is going to be ignored and set to 0.
   // status is the status of the control loop.
   // Both output and status should be filled in by the implementation.
-  virtual void RunIteration(
-      const GoalType *goal, const PositionType *position,
-      typename ::aos::Sender<OutputType>::Builder *output,
-      typename ::aos::Sender<StatusType>::Builder *status) = 0;
+  virtual void RunIteration(const GoalType *goal, const PositionType *position,
+                            OutputBuilder *output, StatusBuilder *status) = 0;
 
  private:
   static constexpr ::std::chrono::milliseconds kStaleLogInterval =
@@ -132,6 +185,28 @@
       SimpleLogInterval(kStaleLogInterval, ERROR, "no goal");
 };
 
+template <typename T>
+template <class GoalType, class PositionType, class StatusType,
+          class OutputType>
+void BuilderType<T, typename std::enable_if_t<
+                        std::is_base_of<flatbuffers::Table, T>::value>>::
+    SendZeroFlatbuffer(
+        ControlLoop<GoalType, PositionType, StatusType, OutputType> *loop,
+        Builder *builder) {
+  builder->CheckOk(builder->Send(loop->Zero(builder)));
+}
+
+template <typename T>
+template <class GoalType, class PositionType, class StatusType,
+          class OutputType>
+void BuilderType<
+    T, typename std::enable_if_t<std::is_base_of<aos::fbs::Table, T>::value>>::
+    SendZeroFlatbuffer(ControlLoop<GoalType, PositionType, StatusType,
+                                   OutputType> * /* loop */,
+                       Builder *builder) {
+  builder->CheckOk(builder->Send());
+}
+
 }  // namespace frc971::controls
 
 #include "frc971/control_loops/control_loop-tmpl.h"  // IWYU pragma: export
diff --git a/frc971/control_loops/swerve/BUILD b/frc971/control_loops/swerve/BUILD
index 4e06932..ccba681 100644
--- a/frc971/control_loops/swerve/BUILD
+++ b/frc971/control_loops/swerve/BUILD
@@ -1,16 +1,34 @@
+load("//aos:config.bzl", "aos_config")
 load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
 
+package(default_visibility = ["//visibility:public"])
+
+static_flatbuffer(
+    name = "swerve_drivetrain_goal_fbs",
+    srcs = ["swerve_drivetrain_goal.fbs"],
+)
+
+static_flatbuffer(
+    name = "swerve_drivetrain_status_fbs",
+    srcs = ["swerve_drivetrain_status.fbs"],
+    deps = ["//frc971/control_loops:profiled_subsystem_fbs"],
+)
+
 static_flatbuffer(
     name = "swerve_drivetrain_output_fbs",
     srcs = ["swerve_drivetrain_output.fbs"],
-    visibility = ["//visibility:public"],
+    deps = ["//frc971/control_loops:can_talonfx_fbs"],
+)
+
+static_flatbuffer(
+    name = "swerve_drivetrain_can_position_fbs",
+    srcs = ["swerve_drivetrain_can_position.fbs"],
     deps = ["//frc971/control_loops:can_talonfx_fbs"],
 )
 
 static_flatbuffer(
     name = "swerve_drivetrain_position_fbs",
     srcs = ["swerve_drivetrain_position.fbs"],
-    visibility = ["//visibility:public"],
     deps = ["//frc971/control_loops:control_loops_fbs"],
 )
 
@@ -27,3 +45,54 @@
         "@pip//sympy",
     ],
 )
+
+cc_library(
+    name = "swerve_control_loops",
+    srcs = ["swerve_control_loops.cc"],
+    hdrs = ["swerve_control_loops.h"],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        ":swerve_drivetrain_can_position_fbs",
+        ":swerve_drivetrain_goal_fbs",
+        ":swerve_drivetrain_output_fbs",
+        ":swerve_drivetrain_position_fbs",
+        ":swerve_drivetrain_status_fbs",
+        "//frc971/control_loops:control_loop",
+    ],
+)
+
+cc_test(
+    name = "swerve_control_test",
+    srcs = ["swerve_control_test.cc"],
+    data = [
+        ":aos_config",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        ":swerve_control_loops",
+        ":swerve_drivetrain_can_position_fbs",
+        ":swerve_drivetrain_goal_fbs",
+        ":swerve_drivetrain_output_fbs",
+        ":swerve_drivetrain_position_fbs",
+        ":swerve_drivetrain_status_fbs",
+        "//aos/events:shm_event_loop",
+        "//aos/testing:googletest",
+        "//frc971/control_loops:control_loop_test",
+        "//frc971/control_loops:state_feedback_loop",
+        "//frc971/control_loops:team_number_test_environment",
+    ],
+)
+
+aos_config(
+    name = "aos_config",
+    src = "swerve.json",
+    flatbuffers = [
+        ":swerve_drivetrain_goal_fbs",
+        ":swerve_drivetrain_output_fbs",
+        ":swerve_drivetrain_position_fbs",
+        ":swerve_drivetrain_can_position_fbs",
+        ":swerve_drivetrain_status_fbs",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = ["//frc971/input:aos_config"],
+)
diff --git a/frc971/control_loops/swerve/swerve.json b/frc971/control_loops/swerve/swerve.json
new file mode 100644
index 0000000..1282b54
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve.json
@@ -0,0 +1,27 @@
+{
+    "channels": [
+        {
+            "name": "/swerve",
+            "type": "frc971.control_loops.swerve.Goal"
+        },
+        {
+            "name": "/swerve",
+            "type": "frc971.control_loops.swerve.Output"
+        },
+        {
+            "name": "/swerve",
+            "type": "frc971.control_loops.swerve.CanPosition"
+        },
+        {
+            "name": "/swerve",
+            "type": "frc971.control_loops.swerve.Position"
+        },
+        {
+            "name": "/swerve",
+            "type": "frc971.control_loops.swerve.Status"
+        }
+    ],
+    "imports": [
+        "../../../frc971/input/robot_state_config.json"
+    ]
+}
diff --git a/frc971/control_loops/swerve/swerve_control_loops.cc b/frc971/control_loops/swerve/swerve_control_loops.cc
new file mode 100644
index 0000000..4689e55
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_control_loops.cc
@@ -0,0 +1,71 @@
+#include "frc971/control_loops/swerve/swerve_control_loops.h"
+
+namespace frc971::control_loops::swerve {
+
+SwerveControlLoops::SwerveControlLoops(::aos::EventLoop *event_loop,
+                                       const ::std::string &name)
+    : frc971::controls::ControlLoop<Goal, Position, StatusStatic, OutputStatic>(
+          event_loop, name) {}
+
+void SwerveControlLoops::RunIteration(
+    const Goal *goal, const Position *position,
+    aos::Sender<OutputStatic>::StaticBuilder *output_builder,
+    aos::Sender<StatusStatic>::StaticBuilder *status_builder) {
+  (void)goal, (void)position;
+
+  if (output_builder != nullptr && goal != nullptr) {
+    OutputStatic *output = output_builder->get();
+
+    auto front_left_output = output->add_front_left_output();
+    front_left_output->set_rotation_current(0);
+    front_left_output->set_translation_current(
+        goal->front_left_goal()->translation_current());
+
+    auto front_right_output = output->add_front_right_output();
+    front_right_output->set_rotation_current(0);
+    front_right_output->set_translation_current(
+        goal->front_right_goal()->translation_current());
+
+    auto back_left_output = output->add_back_left_output();
+    back_left_output->set_rotation_current(0);
+    back_left_output->set_translation_current(
+        goal->back_left_goal()->translation_current());
+
+    auto back_right_output = output->add_back_right_output();
+    back_right_output->set_rotation_current(0);
+    back_right_output->set_translation_current(
+        goal->back_right_goal()->translation_current());
+
+    // Ignore the return value of Send
+    output_builder->CheckOk(output_builder->Send());
+  }
+
+  if (status_builder != nullptr) {
+    StatusStatic *status = status_builder->get();
+
+    auto front_left_status = status->add_front_left_status();
+    front_left_status->set_goal_translation_speed(0);
+    front_left_status->set_translation_speed(0);
+    front_left_status->add_rotation();
+
+    auto front_right_status = status->add_front_right_status();
+    front_right_status->set_goal_translation_speed(0);
+    front_right_status->set_translation_speed(0);
+    front_right_status->add_rotation();
+
+    auto back_left_status = status->add_back_left_status();
+    back_left_status->set_goal_translation_speed(0);
+    back_left_status->set_translation_speed(0);
+    back_left_status->add_rotation();
+
+    auto back_right_status = status->add_back_right_status();
+    back_right_status->set_goal_translation_speed(0);
+    back_right_status->set_translation_speed(0);
+    back_right_status->add_rotation();
+
+    // Ignore the return value of Send
+    status_builder->CheckOk(status_builder->Send());
+  }
+}
+
+}  // namespace frc971::control_loops::swerve
diff --git a/frc971/control_loops/swerve/swerve_control_loops.h b/frc971/control_loops/swerve/swerve_control_loops.h
new file mode 100644
index 0000000..78f6ba0
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_control_loops.h
@@ -0,0 +1,39 @@
+#ifndef FRC971_CONTROL_LOOPS_SWERVE_SWERVE_CONTROL_LOOPS_H_
+#define FRC971_CONTROL_LOOPS_SWERVE_SWERVE_CONTROL_LOOPS_H_
+
+#include "aos/time/time.h"
+#include "frc971/control_loops/control_loop.h"
+#include "frc971/control_loops/profiled_subsystem_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_can_position_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_goal_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_output_static.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_position_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_status_static.h"
+
+namespace frc971::control_loops::swerve {
+
+inline void PopulateSwerveModuleRotation(
+    SwerveModuleStatusStatic *swerve_module_table) {
+  auto rotation = swerve_module_table->add_rotation();
+  auto estimator_state = rotation->add_estimator_state();
+  (void)estimator_state;
+}
+
+// Handles the translation and rotation current for each swerve module
+class SwerveControlLoops
+    : public ::frc971::controls::ControlLoop<Goal, Position, StatusStatic,
+                                             OutputStatic> {
+ public:
+  explicit SwerveControlLoops(::aos::EventLoop *event_loop,
+                              const ::std::string &name = "/swerve");
+
+ protected:
+  void RunIteration(
+      const Goal *goal, const Position *position,
+      aos::Sender<OutputStatic>::StaticBuilder *output_builder,
+      aos::Sender<StatusStatic>::StaticBuilder *status_builder) override;
+};
+
+}  // namespace frc971::control_loops::swerve
+
+#endif  // FRC971_CONTROL_LOOPS_SWERVE_SWERVE_CONTROL_LOOPS_H_
diff --git a/frc971/control_loops/swerve/swerve_control_test.cc b/frc971/control_loops/swerve/swerve_control_test.cc
new file mode 100644
index 0000000..9378f1c
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_control_test.cc
@@ -0,0 +1,161 @@
+#include <unistd.h>
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+#include "aos/events/shm_event_loop.h"
+#include "frc971/control_loops/control_loop_test.h"
+#include "frc971/control_loops/swerve/swerve_control_loops.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_can_position_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_goal_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_output_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_position_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_status_generated.h"
+#include "frc971/control_loops/team_number_test_environment.h"
+
+using namespace std;
+
+namespace frc971::control_loops::swerve::testing {
+namespace chrono = ::std::chrono;
+using aos::monotonic_clock;
+
+// Class which simulates stuff and sends out queue messages with the position.
+class SwerveControlSimulation {
+ public:
+  SwerveControlSimulation(::aos::EventLoop *event_loop, chrono::nanoseconds dt)
+      : event_loop_(event_loop),
+        position_sender_(event_loop_->MakeSender<Position>("/swerve")),
+        can_position_sender_(event_loop_->MakeSender<CanPosition>("/swerve")),
+        goal_fetcher_(event_loop_->MakeFetcher<Goal>("/swerve")),
+        status_fetcher_(event_loop_->MakeFetcher<Status>("/swerve")),
+        output_fetcher_(event_loop_->MakeFetcher<Output>("/swerve")) {
+    event_loop_->AddPhasedLoop(
+        [this](int) {
+          if (!first_) {
+            Simulate();
+          }
+          first_ = false;
+          SendPositionMessage();
+        },
+        dt);
+  }
+
+  // Sends a queue message with the position data.
+  void SendPositionMessage() {
+    auto builder = position_sender_.MakeBuilder();
+
+    Position::Builder position_builder = builder.MakeBuilder<Position>();
+
+    EXPECT_EQ(builder.Send(position_builder.Finish()),
+              aos::RawSender::Error::kOk);
+  }
+
+  void VerifyNearGoal() {
+    goal_fetcher_.Fetch();
+    status_fetcher_.Fetch();
+
+    ASSERT_TRUE(goal_fetcher_.get() != nullptr) << ": No goal";
+    ASSERT_TRUE(status_fetcher_.get() != nullptr) << ": No status";
+
+    constexpr double kEpsRotationAngle = 0.03;
+    constexpr double kEpsRotationVelocity = 0.03;
+    constexpr double kEpsTranslationSpeed = 0.03;
+
+    std::vector<const SwerveModuleStatus *> modules_status{
+        status_fetcher_->front_left_status(),
+        status_fetcher_->front_right_status(),
+        status_fetcher_->back_left_status(),
+        status_fetcher_->back_right_status()};
+
+    for (auto &module_status : modules_status) {
+      EXPECT_NEAR(module_status->rotation()->position(),
+                  module_status->rotation()->goal_position(),
+                  kEpsRotationAngle);
+      EXPECT_NEAR(module_status->rotation()->velocity(),
+                  module_status->rotation()->goal_velocity(),
+                  kEpsRotationVelocity);
+      EXPECT_NEAR(module_status->goal_translation_speed(),
+                  module_status->translation_speed(), kEpsTranslationSpeed);
+    }
+  }
+
+  // Simulates basic control loop for a single timestep.
+  void Simulate() { EXPECT_TRUE(output_fetcher_.Fetch()); }
+
+ private:
+  ::aos::EventLoop *event_loop_;
+
+  ::aos::Sender<Position> position_sender_;
+  ::aos::Sender<CanPosition> can_position_sender_;
+  ::aos::Sender<Goal> goal_sender_;
+
+  ::aos::Fetcher<CanPosition> can_position_fetcher_;
+  ::aos::Fetcher<Position> position_fetcher_;
+  ::aos::Fetcher<Goal> goal_fetcher_;
+  ::aos::Fetcher<Status> status_fetcher_;
+  ::aos::Fetcher<Output> output_fetcher_;
+
+  bool first_ = true;
+};
+
+class SwerveControlLoopTest : public ::frc971::testing::ControlLoopTest {
+ public:
+  SwerveControlLoopTest()
+      : ::frc971::testing::ControlLoopTest(
+            aos::configuration::ReadConfig(
+                "frc971/control_loops/swerve/aos_config.json"),
+            chrono::microseconds(5050)),
+        swerve_test_event_loop_(MakeEventLoop("test")),
+        goal_sender_(swerve_test_event_loop_->MakeSender<Goal>("/swerve")),
+
+        swerve_control_event_loop_(MakeEventLoop("swerve_control")),
+        swerve_control_loops_(swerve_control_event_loop_.get(), "/swerve"),
+
+        swerve_control_simulation_event_loop_(MakeEventLoop("simulation")),
+        swerve_control_simulation_(swerve_control_simulation_event_loop_.get(),
+                                   dt()) {
+    set_team_id(control_loops::testing::kTeamNumber);
+    SetEnabled(true);
+  }
+
+  ::std::unique_ptr<::aos::EventLoop> swerve_test_event_loop_;
+  ::aos::Sender<Goal> goal_sender_;
+
+  ::std::unique_ptr<::aos::EventLoop> swerve_control_event_loop_;
+  SwerveControlLoops swerve_control_loops_;
+
+  ::std::unique_ptr<::aos::EventLoop> swerve_control_simulation_event_loop_;
+  SwerveControlSimulation swerve_control_simulation_;
+};
+
+// Tests that the swerve modules' speeds are all set to 0.
+TEST_F(SwerveControlLoopTest, SwerveModulesDontMove) {
+  {
+    auto builder = goal_sender_.MakeBuilder();
+    SwerveModuleGoal::Builder swerve_module_builder =
+        builder.MakeBuilder<SwerveModuleGoal>();
+    swerve_module_builder.add_translation_control_type_goal(
+        TranslationControlTypeGoal::CURRENT);
+    swerve_module_builder.add_rotation_angle(0.0);
+    swerve_module_builder.add_translation_current(0.0);
+    swerve_module_builder.add_translation_speed(0.0);
+    auto empty_module_offset = swerve_module_builder.Finish();
+
+    Goal::Builder goal_builder = builder.MakeBuilder<Goal>();
+    goal_builder.add_front_left_goal(empty_module_offset);
+    goal_builder.add_front_right_goal(empty_module_offset);
+    goal_builder.add_back_left_goal(empty_module_offset);
+    goal_builder.add_back_right_goal(empty_module_offset);
+
+    ASSERT_EQ(builder.Send(goal_builder.Finish()), aos::RawSender::Error::kOk);
+  }
+
+  RunFor(dt() * 2);
+
+  swerve_control_simulation_.VerifyNearGoal();
+}
+
+}  // namespace frc971::control_loops::swerve::testing
\ No newline at end of file
diff --git a/frc971/control_loops/swerve/swerve_drivetrain_can_position.fbs b/frc971/control_loops/swerve/swerve_drivetrain_can_position.fbs
new file mode 100644
index 0000000..957bce8
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_drivetrain_can_position.fbs
@@ -0,0 +1,18 @@
+include "frc971/control_loops/can_talonfx.fbs";
+
+namespace frc971.control_loops.swerve;
+
+table SwerveModuleCanPosition {
+  rotation: frc971.control_loops.CANTalonFX (id: 0);
+  translation: frc971.control_loops.CANTalonFX (id: 1);
+}
+
+// CAN Readings from the CAN sensor reader loop for each swerve module
+table CanPosition {
+  front_left: SwerveModuleCanPosition (id: 0);
+  front_right: SwerveModuleCanPosition (id: 1);
+  back_left: SwerveModuleCanPosition (id: 2);
+  back_right: SwerveModuleCanPosition (id: 3);
+}
+
+root_type CanPosition;
diff --git a/frc971/control_loops/swerve/swerve_drivetrain_goal.fbs b/frc971/control_loops/swerve/swerve_drivetrain_goal.fbs
new file mode 100644
index 0000000..eab0d0d
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_drivetrain_goal.fbs
@@ -0,0 +1,32 @@
+namespace frc971.control_loops.swerve;
+
+// States what translation control type goal we will care about
+// SPEED means we will control translation with speed (m/s)
+// CURRENT mean we will control translation with current (amps)
+enum TranslationControlTypeGoal : ubyte {
+  SPEED,
+  CURRENT,
+}
+
+// Takes in either current or speed
+// Uses current if both are given
+table SwerveModuleGoal {
+  // Angle in radians.
+  rotation_angle:double (id: 0);
+  // Tells us whether we use current
+  // or speed to control translation
+  translation_control_type_goal: TranslationControlTypeGoal (id: 1);
+  // Current in amps.
+  translation_current:double (id: 2);
+  // Speed in meters per second.
+  translation_speed:double (id: 3);
+}
+
+table Goal {
+    front_left_goal:SwerveModuleGoal (id: 0);
+    front_right_goal:SwerveModuleGoal (id: 1);
+    back_left_goal:SwerveModuleGoal (id: 2);
+    back_right_goal:SwerveModuleGoal (id: 3);
+}
+
+root_type Goal;
diff --git a/frc971/control_loops/swerve/swerve_drivetrain_output.fbs b/frc971/control_loops/swerve/swerve_drivetrain_output.fbs
index 43ba0ed..72517a6 100644
--- a/frc971/control_loops/swerve/swerve_drivetrain_output.fbs
+++ b/frc971/control_loops/swerve/swerve_drivetrain_output.fbs
@@ -1,4 +1,4 @@
-namespace frc971.control_loops.drivetrain.swerve;
+namespace frc971.control_loops.swerve;
 
 table SwerveModuleOutput {
   // Current in Amps.
diff --git a/frc971/control_loops/swerve/swerve_drivetrain_position.fbs b/frc971/control_loops/swerve/swerve_drivetrain_position.fbs
index bad8af1..a5c921a 100644
--- a/frc971/control_loops/swerve/swerve_drivetrain_position.fbs
+++ b/frc971/control_loops/swerve/swerve_drivetrain_position.fbs
@@ -1,17 +1,19 @@
-namespace frc971.control_loops.drivetrain.swerve;
+include "frc971/control_loops/control_loops.fbs";
+
+namespace frc971.control_loops.swerve;
 
 table SwerveModulePosition {
-  // Rotation in radians
-  rotation_encoder:double (id: 0);
-  // Translation in meters
-  translation_encoder:double (id: 1);
+  // Position of the mag encoder for the rotation of the module.
+  rotation_position: frc971.AbsolutePosition (id: 0);
 }
 
+// Captures all of the roborio-sourced position information for a
+// swerve drivetrain.
 table Position {
-  front_left_position:SwerveModulePosition (id: 0);
-  front_right_position:SwerveModulePosition (id: 1);
-  back_left_position:SwerveModulePosition (id: 2);
-  back_right_position:SwerveModulePosition (id: 3);
+  front_left:SwerveModulePosition (id: 0);
+  front_right:SwerveModulePosition (id: 1);
+  back_left:SwerveModulePosition (id: 2);
+  back_right:SwerveModulePosition (id: 3);
 }
 
 root_type Position;
diff --git a/frc971/control_loops/swerve/swerve_drivetrain_status.fbs b/frc971/control_loops/swerve/swerve_drivetrain_status.fbs
new file mode 100644
index 0000000..dc50af5
--- /dev/null
+++ b/frc971/control_loops/swerve/swerve_drivetrain_status.fbs
@@ -0,0 +1,21 @@
+include "frc971/control_loops/profiled_subsystem.fbs";
+
+namespace frc971.control_loops.swerve;
+
+table SwerveModuleStatus {
+    // Goal speed in meters per second.
+    goal_translation_speed:double (id: 0);
+    // Absolute encoder for rotation
+    rotation:frc971.control_loops.AbsoluteEncoderProfiledJointStatus (id: 1);
+    // Translation speed in meters per second.
+    translation_speed:double (id: 2);
+}
+
+table Status {
+    front_left_status:SwerveModuleStatus (id: 0);
+    front_right_status:SwerveModuleStatus (id: 1);
+    back_left_status:SwerveModuleStatus (id: 2);
+    back_right_status:SwerveModuleStatus (id: 3);
+}
+
+root_type Status;
diff --git a/frc971/orin/doflash_frc971.sh b/frc971/orin/doflash_frc971.sh
index f8cbbc3..ff4a705 100755
--- a/frc971/orin/doflash_frc971.sh
+++ b/frc971/orin/doflash_frc971.sh
@@ -1,23 +1,31 @@
 #!/bin/bash
 set -ex
 
-TMPDIR=$(mktemp -d /tmp/yoctoflash.XXXXXXXXXX)
+# If we pass in a folder, then use that as TMPDIR and don't clean it up
+if [[ -d $1 ]]; then
+    TMPDIR=$1
+else
+    TMPDIR=$(mktemp -d /tmp/yoctoflash.XXXXXXXXXX)
+    trap finish EXIT
+fi
 
 # Cleanup on exit.
 function finish {
   sudo rm -rf "${TMPDIR}"
 }
-trap finish EXIT
 
 # Call sudo to get it started here, rather than waiting for after tar/cp's
 sudo echo "Flashing orin"
 
-# Assumes that the image has been copied into ./
-tar xf frc971-image-orin-nx-8g.tegraflash.tar.gz -C "${TMPDIR}"
+# If the files haven't been extracted yet, do so now; otherwise, just
+# re-use what's already there
+if [[ ! -f ${TMPDIR}/initrd-flash ]]; then 
+    # Assumes that the image has been copied into ./
+    tar xf frc971-image-orin-nx-8g.tegraflash.tar.gz -C "${TMPDIR}"
 
-# Replace the rootfs with our new image.
-cp --sparse=always arm64_bookworm_debian_yocto.img "${TMPDIR}/frc971-image.ext4"
+    # Replace the rootfs with our new image.
+    cp --sparse=always arm64_bookworm_debian_yocto.img "${TMPDIR}/frc971-image.ext4"
+fi
 
 cd ${TMPDIR}
-
 sudo ./initrd-flash
diff --git a/frc971/vision/calibration_accumulator.cc b/frc971/vision/calibration_accumulator.cc
index 13481b4..bbf51ee 100644
--- a/frc971/vision/calibration_accumulator.cc
+++ b/frc971/vision/calibration_accumulator.cc
@@ -1,6 +1,7 @@
 #include "frc971/vision/calibration_accumulator.h"
 
 #include <algorithm>
+#include <iomanip>
 #include <limits>
 
 #include "Eigen/Dense"
diff --git a/frc971/wpilib/swerve/BUILD b/frc971/wpilib/swerve/BUILD
index 8eb5651..0b2cc1c 100644
--- a/frc971/wpilib/swerve/BUILD
+++ b/frc971/wpilib/swerve/BUILD
@@ -12,7 +12,9 @@
         ":swerve_module",
         "//aos/logging",
         "//frc971:can_configuration_fbs",
+        "//frc971/control_loops/swerve:swerve_drivetrain_can_position_fbs",
         "//frc971/control_loops/swerve:swerve_drivetrain_output_fbs",
+        "//frc971/control_loops/swerve:swerve_drivetrain_position_fbs",
         "//frc971/wpilib:loop_output_handler",
         "//frc971/wpilib:talonfx",
         "//third_party:phoenix6",
@@ -25,6 +27,7 @@
         "swerve_module.h",
     ],
     deps = [
+        "//frc971/wpilib:encoder_and_potentiometer",
         "//frc971/wpilib:talonfx",
     ],
 )
diff --git a/frc971/wpilib/swerve/swerve_drivetrain_writer.cc b/frc971/wpilib/swerve/swerve_drivetrain_writer.cc
index e4b4ee9..44aff53 100644
--- a/frc971/wpilib/swerve/swerve_drivetrain_writer.cc
+++ b/frc971/wpilib/swerve/swerve_drivetrain_writer.cc
@@ -6,27 +6,21 @@
                                    int drivetrain_writer_priority,
                                    double max_voltage)
     : ::frc971::wpilib::LoopOutputHandler<
-          ::frc971::control_loops::drivetrain::swerve::Output>(event_loop,
-                                                               "/drivetrain"),
+          ::frc971::control_loops::swerve::Output>(event_loop, "/drivetrain"),
       max_voltage_(max_voltage) {
   event_loop->SetRuntimeRealtimePriority(drivetrain_writer_priority);
 
   event_loop->OnRun([this]() { WriteConfigs(); });
 }
 
-void DrivetrainWriter::set_talonfxs(std::shared_ptr<SwerveModule> front_left,
-                                    std::shared_ptr<SwerveModule> front_right,
-                                    std::shared_ptr<SwerveModule> back_left,
-                                    std::shared_ptr<SwerveModule> back_right) {
-  front_left_ = std::move(front_left);
-  front_right_ = std::move(front_right);
-  back_left_ = std::move(back_left);
-  back_right_ = std::move(back_right);
+void DrivetrainWriter::set_talonfxs(SwerveModules modules) {
+  modules_ = std::move(modules);
 }
 
 void DrivetrainWriter::HandleCANConfiguration(
     const CANConfiguration &configuration) {
-  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+  for (auto module : {modules_.front_left, modules_.front_right,
+                      modules_.back_left, modules_.back_right}) {
     module->rotation->PrintConfigs();
     module->translation->PrintConfigs();
   }
@@ -36,24 +30,26 @@
 }
 
 void DrivetrainWriter::WriteConfigs() {
-  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+  for (auto module : {modules_.front_left, modules_.front_right,
+                      modules_.back_left, modules_.back_right}) {
     module->rotation->WriteConfigs();
     module->translation->WriteConfigs();
   }
 }
 
 void DrivetrainWriter::Write(
-    const ::frc971::control_loops::drivetrain::swerve::Output &output) {
-  front_left_->WriteModule(output.front_left_output(), max_voltage_);
-  front_right_->WriteModule(output.front_right_output(), max_voltage_);
-  back_left_->WriteModule(output.back_left_output(), max_voltage_);
-  back_right_->WriteModule(output.back_right_output(), max_voltage_);
+    const ::frc971::control_loops::swerve::Output &output) {
+  modules_.front_left->WriteModule(output.front_left_output(), max_voltage_);
+  modules_.front_right->WriteModule(output.front_right_output(), max_voltage_);
+  modules_.back_left->WriteModule(output.back_left_output(), max_voltage_);
+  modules_.back_right->WriteModule(output.back_right_output(), max_voltage_);
 }
 
 void DrivetrainWriter::Stop() {
   AOS_LOG(WARNING, "drivetrain output too old\n");
 
-  for (auto module : {front_left_, front_right_, back_left_, back_right_}) {
+  for (auto module : {modules_.front_left, modules_.front_right,
+                      modules_.back_left, modules_.back_right}) {
     module->rotation->WriteCurrent(0, 0);
     module->translation->WriteCurrent(0, 0);
   }
diff --git a/frc971/wpilib/swerve/swerve_drivetrain_writer.h b/frc971/wpilib/swerve/swerve_drivetrain_writer.h
index 16db158..3b79186 100644
--- a/frc971/wpilib/swerve/swerve_drivetrain_writer.h
+++ b/frc971/wpilib/swerve/swerve_drivetrain_writer.h
@@ -13,32 +13,26 @@
 
 // Reads from the swerve output flatbuffer and uses wpilib to set the current
 // for each motor.
-class DrivetrainWriter
-    : public ::frc971::wpilib::LoopOutputHandler<
-          ::frc971::control_loops::drivetrain::swerve::Output> {
+class DrivetrainWriter : public ::frc971::wpilib::LoopOutputHandler<
+                             ::frc971::control_loops::swerve::Output> {
  public:
   DrivetrainWriter(::aos::EventLoop *event_loop, int drivetrain_writer_priority,
                    double max_voltage);
 
-  void set_talonfxs(std::shared_ptr<SwerveModule> front_left,
-                    std::shared_ptr<SwerveModule> front_right,
-                    std::shared_ptr<SwerveModule> back_left,
-                    std::shared_ptr<SwerveModule> back_right);
+  void set_talonfxs(SwerveModules modules);
 
   void HandleCANConfiguration(const CANConfiguration &configuration);
 
  private:
   void WriteConfigs();
 
-  void Write(const ::frc971::control_loops::drivetrain::swerve::Output &output)
-      override;
+  void Write(const ::frc971::control_loops::swerve::Output &output) override;
 
   void Stop() override;
 
   double SafeSpeed(double voltage);
 
-  std::shared_ptr<SwerveModule> front_left_, front_right_, back_left_,
-      back_right_;
+  SwerveModules modules_;
 
   double max_voltage_;
 };
diff --git a/frc971/wpilib/swerve/swerve_module.h b/frc971/wpilib/swerve/swerve_module.h
index 729ebab..c01df24 100644
--- a/frc971/wpilib/swerve/swerve_module.h
+++ b/frc971/wpilib/swerve/swerve_module.h
@@ -1,11 +1,17 @@
 #ifndef FRC971_WPILIB_SWERVE_SWERVE_MODULE_H_
 #define FRC971_WPILIB_SWERVE_SWERVE_MODULE_H_
 
+#include "frc971/control_loops/swerve/swerve_drivetrain_can_position_static.h"
 #include "frc971/control_loops/swerve/swerve_drivetrain_output_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_position_static.h"
+#include "frc971/wpilib/encoder_and_potentiometer.h"
 #include "frc971/wpilib/talonfx.h"
 
 namespace frc971::wpilib::swerve {
 
+// Contains the objects for interacting with the hardware for a given swerve
+// module, assuming that the module uses two TalonFX-based motor controllers and
+// has a CTRE mag encoder on the rotation of the module.
 struct SwerveModule {
   SwerveModule(TalonFXParams rotation_params, TalonFXParams translation_params,
                std::string canbus,
@@ -18,9 +24,10 @@
                                               signals, stator_current_limit,
                                               supply_current_limit)) {}
 
+  // Writes the requested torque currents from the module_output to the motors,
+  // setting the maximum voltage of the motor outputs to the requested value.
   void WriteModule(
-      const frc971::control_loops::drivetrain::swerve::SwerveModuleOutput
-          *module_output,
+      const frc971::control_loops::swerve::SwerveModuleOutput *module_output,
       double max_voltage) {
     double rotation_current = 0.0;
     double translation_current = 0.0;
@@ -34,8 +41,58 @@
     translation->WriteCurrent(translation_current, max_voltage);
   }
 
+  // Used during initialization to set the WPILib objects used by the mag
+  // encoder on the rotation joint.
+  void set_rotation_encoder(std::unique_ptr<frc::Encoder> encoder,
+                            std::unique_ptr<frc::DigitalInput> absolute_pwm) {
+    rotation_encoder.set_encoder(std::move(encoder));
+    rotation_encoder.set_absolute_pwm(std::move(absolute_pwm));
+  }
+
+  // Populates the Position message with the mag encoder values.
+  void PopulatePosition(
+      frc971::control_loops::swerve::SwerveModulePositionStatic *fbs) {
+    auto rotation_position = fbs->add_rotation_position();
+    rotation_position->set_encoder(rotation_encoder.ReadRelativeEncoder());
+    rotation_position->set_absolute_encoder(
+        rotation_encoder.ReadAbsoluteEncoder());
+  }
+
+  // Populates a CAN-position message with the CAN-based devices (currently,
+  // just the motors themselves).
+  void PopulateCanPosition(
+      frc971::control_loops::swerve::SwerveModuleCanPositionStatic
+          *can_position) {
+    // TODO(james): Source these numbers from the constants.json.
+    rotation->SerializePosition(can_position->add_rotation(), 1.0);
+    translation->SerializePosition(can_position->add_translation(), 1.0);
+  }
+
   std::shared_ptr<TalonFX> rotation;
   std::shared_ptr<TalonFX> translation;
+  frc971::wpilib::AbsoluteEncoder rotation_encoder;
+};
+
+// Represents all the modules in a swerve drivetrain.
+struct SwerveModules {
+  void PopulateFalconsVector(std::vector<std::shared_ptr<TalonFX>> *falcons) {
+    CHECK_NOTNULL(falcons)->push_back(front_left->rotation);
+    falcons->push_back(front_left->translation);
+
+    falcons->push_back(front_right->rotation);
+    falcons->push_back(front_right->translation);
+
+    falcons->push_back(back_left->rotation);
+    falcons->push_back(back_left->translation);
+
+    falcons->push_back(back_right->rotation);
+    falcons->push_back(back_right->translation);
+  }
+
+  std::shared_ptr<SwerveModule> front_left;
+  std::shared_ptr<SwerveModule> front_right;
+  std::shared_ptr<SwerveModule> back_left;
+  std::shared_ptr<SwerveModule> back_right;
 };
 
 }  // namespace frc971::wpilib::swerve
diff --git a/tools/iwyu/by_hand.imp b/tools/iwyu/by_hand.imp
index 66dfcd9..bbbeec8 100644
--- a/tools/iwyu/by_hand.imp
+++ b/tools/iwyu/by_hand.imp
@@ -46,5 +46,8 @@
   # nlohmann
   { "include": ["<nlohmann/json.hpp>", "private", "\"nlohmann/json.hpp\"", "public"] },
   { "include": ["<nlohmann/json_fwd.hpp>", "private", "\"nlohmann/json_fwd.hpp\"", "public"] },
+
+  # aos
+  { "symbol": ["log_level", "private", "\"aos/logging/logging.h\"", "public"] },
 ]
 
diff --git a/y2024/constants/7971.json b/y2024/constants/7971.json
index 4aed3cb..c924174 100644
--- a/y2024/constants/7971.json
+++ b/y2024/constants/7971.json
@@ -6,6 +6,20 @@
 {% from 'y2024/constants/common.jinja2' import extend_zero %}
 
 {
+  "cameras": [
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-7971-0_cam-24-04_2024-04-07_19-59-36.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-7971-1_cam-24-02_2024-04-07_19-59-36.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-7971-1_cam-24-03_2024-04-07_19-59-36.json' %}
+    }
+  ],
   "robot": {
     {% set _ = intake_pivot_zero.update(
       {
@@ -51,21 +65,7 @@
       "potentiometer_offset": 0.0
     },
     "disable_extend": false,
-    "disable_climber": false,
-    "cameras": [
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-7971-0_cam-24-01_2024-03-02_19-44-12.098903651.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-7971-0_cam-24-04_2024-04-07_19-59-36.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-7971-1_cam-24-02_2024-04-07_19-59-36.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-7971-1_cam-24-03_2024-04-07_19-59-36.json' %}
-      }
-    ]
+    "disable_climber": false
   },
   {% include 'y2024/constants/common.json' %}
 }
diff --git a/y2024/constants/971.json b/y2024/constants/971.json
index ce70e07..5145613 100644
--- a/y2024/constants/971.json
+++ b/y2024/constants/971.json
@@ -6,6 +6,20 @@
 {% from 'y2024/constants/common.jinja2' import extend_zero %}
 
 {
+  "cameras": [
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-0_cam-24-08_2024-03-01_11-02-11.json' %}
+    }
+  ],
   "robot": {
     {% set _ = intake_pivot_zero.update(
       {
@@ -51,21 +65,7 @@
       "potentiometer_offset": {{ -0.2574404033256 + 0.0170793439542 - 0.177097393974999 + 0.3473623911879  - 0.1577 - 0.0214825988393}}
     },
     "disable_extend": false,
-    "disable_climber": false,
-    "cameras": [
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-1_cam-24-05_2024-03-24_14-54-27.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-0_cam-24-06_2024-03-24_14-54-27.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-971-1_cam-24-07_2024-03-24_14-54-27.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-971-0_cam-24-08_2024-03-01_11-02-11.json' %}
-      }
-    ]
+    "disable_climber": false
   },
   {% include 'y2024/constants/common.json' %}
 }
diff --git a/y2024/constants/9971.json b/y2024/constants/9971.json
index 5a3e7fc..0cde2ad 100644
--- a/y2024/constants/9971.json
+++ b/y2024/constants/9971.json
@@ -6,6 +6,20 @@
 {% from 'y2024/constants/common.jinja2' import extend_zero %}
 
 {
+  "cameras": [
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json' %}
+    },
+  ],
   "robot": {
     {% set _ = intake_pivot_zero.update(
       {
@@ -51,21 +65,7 @@
       "potentiometer_offset": 0.0
     },
     "disable_extend": false,
-    "disable_climber": false,
-    "cameras": [
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-0_cam-24-10_2024-02-24_16-44-05.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_imu-9971-1_cam-24-12_2024-03-24_11-52-49.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-0_cam-24-09_2024-03-24_11-52-49.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/calib_files/calibration_orin1-9971-1_cam-24-11_2024-03-24_11-52-49.json' %}
-      },
-    ]
+    "disable_climber": false
   },
   {% include 'y2024/constants/common.json' %}
 }
diff --git a/y2024/constants/constants.fbs b/y2024/constants/constants.fbs
index d4d05b7..6c1480c 100644
--- a/y2024/constants/constants.fbs
+++ b/y2024/constants/constants.fbs
@@ -150,7 +150,6 @@
   extend_constants:PotAndAbsEncoderConstants (id: 5);
   disable_extend:bool (id: 6);
   disable_climber:bool (id: 7);
-  cameras:[CameraConfiguration] (id: 9);
 }
 
 table ShooterSetPoint {
@@ -224,8 +223,9 @@
 }
 
 table Constants {
-  robot:RobotConstants (id: 0);
-  common:Common (id: 1);
+  cameras:[CameraConfiguration] (id: 0);
+  robot:RobotConstants (id: 1);
+  common:Common (id: 2);
 }
 
 root_type Constants;
diff --git a/y2024/constants/test_data/test_team.json b/y2024/constants/test_data/test_team.json
index 4601f50..9d40027 100644
--- a/y2024/constants/test_data/test_team.json
+++ b/y2024/constants/test_data/test_team.json
@@ -6,6 +6,20 @@
 {% from 'y2024/constants/common.jinja2' import extend_zero %}
 
 {
+  "cameras": [
+    {
+      "calibration": {% include 'y2024/constants/test_data/calibration_cam-1.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/test_data/calibration_cam-2.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/test_data/calibration_cam-3.json' %}
+    },
+    {
+      "calibration": {% include 'y2024/constants/test_data/calibration_cam-4.json' %}
+    }
+  ],
   "robot": {
     {% set _ = intake_pivot_zero.update(
       {
@@ -51,21 +65,7 @@
       "potentiometer_offset": 0.0
     },
     "disable_extend": false,
-    "disable_climber": false,
-    "cameras": [
-      {
-        "calibration": {% include 'y2024/constants/test_data/calibration_cam-1.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/test_data/calibration_cam-2.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/test_data/calibration_cam-3.json' %}
-      },
-      {
-        "calibration": {% include 'y2024/constants/test_data/calibration_cam-4.json' %}
-      }
-    ]
+    "disable_climber": false
   },
   {% include 'y2024/constants/common.json' %}
 }
diff --git a/y2024/localizer/localizer.cc b/y2024/localizer/localizer.cc
index 2b72f94..d73d171 100644
--- a/y2024/localizer/localizer.cc
+++ b/y2024/localizer/localizer.cc
@@ -140,9 +140,9 @@
 
 std::array<Localizer::CameraState, Localizer::kNumCameras>
 Localizer::MakeCameras(const Constants &constants, aos::EventLoop *event_loop) {
-  CHECK(constants.robot()->has_cameras());
+  CHECK(constants.has_cameras());
   std::array<Localizer::CameraState, Localizer::kNumCameras> cameras;
-  for (const CameraConfiguration *camera : *constants.robot()->cameras()) {
+  for (const CameraConfiguration *camera : *constants.cameras()) {
     CHECK(camera->has_calibration());
     const frc971::vision::calibration::CameraCalibration *calibration =
         camera->calibration();
diff --git a/y2024/localizer/localizer_test.cc b/y2024/localizer/localizer_test.cc
index bd97059..e5815e0 100644
--- a/y2024/localizer/localizer_test.cc
+++ b/y2024/localizer/localizer_test.cc
@@ -104,23 +104,20 @@
       // Sanity check that the test calibration files look like what we
       // expect.
       CHECK_EQ("orin1", constants_fetcher_.constants()
-                            .robot()
-                            ->cameras()
+                            .cameras()
                             ->Get(0)
                             ->calibration()
                             ->node_name()
                             ->string_view());
       CHECK_EQ(0, constants_fetcher_.constants()
-                      .robot()
-                      ->cameras()
+                      .cameras()
                       ->Get(0)
                       ->calibration()
                       ->camera_number());
       const Eigen::Matrix<double, 4, 4> H_robot_camera =
           frc971::control_loops::drivetrain::FlatbufferToTransformationMatrix(
               *constants_fetcher_.constants()
-                   .robot()
-                   ->cameras()
+                   .cameras()
                    ->Get(0)
                    ->calibration()
                    ->fixed_extrinsics());
diff --git a/y2024/vision/vision_util.cc b/y2024/vision/vision_util.cc
index cf52507..becb1cf 100644
--- a/y2024/vision/vision_util.cc
+++ b/y2024/vision/vision_util.cc
@@ -31,9 +31,9 @@
 const frc971::vision::calibration::CameraCalibration *FindCameraCalibration(
     const y2024::Constants &calibration_data, std::string_view node_name,
     int camera_number) {
-  CHECK(calibration_data.robot()->has_cameras());
+  CHECK(calibration_data.has_cameras());
   for (const y2024::CameraConfiguration *candidate :
-       *calibration_data.robot()->cameras()) {
+       *calibration_data.cameras()) {
     CHECK(candidate->has_calibration());
     if (candidate->calibration()->node_name()->string_view() != node_name ||
         candidate->calibration()->camera_number() != camera_number) {
diff --git a/y2024_swerve/BUILD b/y2024_swerve/BUILD
index ee9c3cb..b24c469 100644
--- a/y2024_swerve/BUILD
+++ b/y2024_swerve/BUILD
@@ -1,5 +1,4 @@
 load("//aos:config.bzl", "aos_config")
-load("//aos/flatbuffers:generate.bzl", "static_flatbuffer")
 load("//aos/util:config_validator_macro.bzl", "config_validator_test")
 load("//frc971:downloader.bzl", "robot_downloader")
 
@@ -106,18 +105,6 @@
     ],
 )
 
-static_flatbuffer(
-    name = "drivetrain_position_fbs",
-    srcs = ["drivetrain_position.fbs"],
-    deps = ["//frc971/control_loops:control_loops_fbs"],
-)
-
-static_flatbuffer(
-    name = "drivetrain_can_position_fbs",
-    srcs = ["drivetrain_can_position.fbs"],
-    deps = ["//frc971/control_loops:can_talonfx_fbs"],
-)
-
 cc_binary(
     name = "swerve_publisher",
     srcs = ["swerve_publisher_main.cc"],
@@ -162,11 +149,10 @@
     target_compatible_with = ["//tools/platforms/hardware:roborio"],
     deps = [
         ":constants",
-        ":drivetrain_can_position_fbs",
-        ":drivetrain_position_fbs",
         "//aos:init",
         "//aos/events:shm_event_loop",
         "//frc971/control_loops:control_loops_fbs",
+        "//frc971/control_loops/swerve:swerve_drivetrain_can_position_fbs",
         "//frc971/control_loops/swerve:swerve_drivetrain_position_fbs",
         "//frc971/wpilib:can_sensor_reader",
         "//frc971/wpilib:sensor_reader",
@@ -200,8 +186,6 @@
     name = "config_roborio",
     src = "y2024_swerve_roborio.json",
     flatbuffers = [
-        ":drivetrain_position_fbs",
-        ":drivetrain_can_position_fbs",
         "//frc971:can_configuration_fbs",
         "//aos/network:remote_message_fbs",
         "//aos/network:message_bridge_client_fbs",
@@ -209,6 +193,7 @@
         "//aos/network:timestamp_fbs",
         "//frc971/control_loops/swerve:swerve_drivetrain_output_fbs",
         "//frc971/control_loops/swerve:swerve_drivetrain_position_fbs",
+        "//frc971/control_loops/swerve:swerve_drivetrain_can_position_fbs",
         "//frc971/control_loops/drivetrain:drivetrain_can_position_fbs",
         "//frc971/can_logger:can_logging_fbs",
     ],
diff --git a/y2024_swerve/drivetrain_can_position.fbs b/y2024_swerve/drivetrain_can_position.fbs
deleted file mode 100644
index f0d41a8..0000000
--- a/y2024_swerve/drivetrain_can_position.fbs
+++ /dev/null
@@ -1,17 +0,0 @@
-include "frc971/control_loops/can_talonfx.fbs";
-namespace y2024_swerve;
-
-table SwerveModuleCANPosition {
-  rotation: frc971.control_loops.CANTalonFX (id: 0);
-  translation: frc971.control_loops.CANTalonFX (id: 1);
-}
-
-// CAN Readings from the CAN sensor reader loop for each swerve module
-table AbsoluteCANPosition {
-  front_left: SwerveModuleCANPosition (id: 0);
-  front_right: SwerveModuleCANPosition (id: 1);
-  back_left: SwerveModuleCANPosition (id: 2);
-  back_right: SwerveModuleCANPosition (id: 3);
-}
-
-root_type AbsoluteCANPosition;
diff --git a/y2024_swerve/drivetrain_position.fbs b/y2024_swerve/drivetrain_position.fbs
deleted file mode 100644
index 902cfee..0000000
--- a/y2024_swerve/drivetrain_position.fbs
+++ /dev/null
@@ -1,16 +0,0 @@
-include "frc971/control_loops/control_loops.fbs";
-namespace y2024_swerve;
-
-table AbsoluteDrivetrainPosition {
-  // Position of the follower wheels from the encoders
-  follower_wheel_one_position:double (id: 0);
-  follower_wheel_two_position:double (id: 1);
-
-  // Position from the mag encoder on each module.
-  front_left_position: frc971.AbsolutePosition (id: 2);
-  front_right_position: frc971.AbsolutePosition (id: 3);
-  back_left_position: frc971.AbsolutePosition (id: 4);
-  back_right_position: frc971.AbsolutePosition  (id: 5);
-}
-
-root_type AbsoluteDrivetrainPosition;
diff --git a/y2024_swerve/swerve_publisher_lib.cc b/y2024_swerve/swerve_publisher_lib.cc
index 771d770..2eb9cce 100644
--- a/y2024_swerve/swerve_publisher_lib.cc
+++ b/y2024_swerve/swerve_publisher_lib.cc
@@ -5,16 +5,18 @@
                                                const std::string &filename,
                                                double duration)
     : drivetrain_output_sender_(
-          event_loop->MakeSender<drivetrain::swerve::Output>("/drivetrain")) {
+          event_loop->MakeSender<frc971::control_loops::swerve::Output>(
+              "/drivetrain")) {
   event_loop
       ->AddTimer([this, filename]() {
         auto output_builder = drivetrain_output_sender_.MakeBuilder();
 
         auto drivetrain_output =
-            aos::JsonFileToFlatbuffer<drivetrain::swerve::Output>(filename);
+            aos::JsonFileToFlatbuffer<frc971::control_loops::swerve::Output>(
+                filename);
 
         auto copied_flatbuffer =
-            aos::CopyFlatBuffer<drivetrain::swerve::Output>(
+            aos::CopyFlatBuffer<frc971::control_loops::swerve::Output>(
                 drivetrain_output, output_builder.fbb());
         CHECK(drivetrain_output.Verify());
 
@@ -26,16 +28,18 @@
   event_loop
       ->AddTimer([this, exit_handle]() {
         auto builder = drivetrain_output_sender_.MakeBuilder();
-        drivetrain::swerve::SwerveModuleOutput::Builder swerve_module_builder =
-            builder.MakeBuilder<drivetrain::swerve::SwerveModuleOutput>();
+        frc971::control_loops::swerve::SwerveModuleOutput::Builder
+            swerve_module_builder = builder.MakeBuilder<
+                frc971::control_loops::swerve::SwerveModuleOutput>();
 
         swerve_module_builder.add_rotation_current(0.0);
         swerve_module_builder.add_translation_current(0.0);
 
         auto swerve_module_offset = swerve_module_builder.Finish();
 
-        drivetrain::swerve::Output::Builder drivetrain_output_builder =
-            builder.MakeBuilder<drivetrain::swerve::Output>();
+        frc971::control_loops::swerve::Output::Builder
+            drivetrain_output_builder =
+                builder.MakeBuilder<frc971::control_loops::swerve::Output>();
 
         drivetrain_output_builder.add_front_left_output(swerve_module_offset);
         drivetrain_output_builder.add_front_right_output(swerve_module_offset);
diff --git a/y2024_swerve/swerve_publisher_lib.h b/y2024_swerve/swerve_publisher_lib.h
index 1a07865..e577455 100644
--- a/y2024_swerve/swerve_publisher_lib.h
+++ b/y2024_swerve/swerve_publisher_lib.h
@@ -12,16 +12,13 @@
 
 namespace y2024_swerve {
 
-namespace drivetrain = frc971::control_loops::drivetrain;
-
 class SwervePublisher {
  public:
   SwervePublisher(aos::EventLoop *event_loop, aos::ExitHandle *exit_handle,
                   const std::string &filename, double duration);
 
  private:
-  aos::Sender<frc971::control_loops::drivetrain::swerve::Output>
-      drivetrain_output_sender_;
+  aos::Sender<frc971::control_loops::swerve::Output> drivetrain_output_sender_;
 };
 
 }  // namespace y2024_swerve
diff --git a/y2024_swerve/swerve_publisher_lib_test.cc b/y2024_swerve/swerve_publisher_lib_test.cc
index 95892cc..096dc9e 100644
--- a/y2024_swerve/swerve_publisher_lib_test.cc
+++ b/y2024_swerve/swerve_publisher_lib_test.cc
@@ -16,8 +16,7 @@
             event_loop_factory_.MakeEventLoop("swerve_publisher", roborio_)),
         exit_handle_(event_loop_factory_.MakeExitHandle()),
         drivetrain_swerve_output_fetcher_(
-            event_loop_->MakeFetcher<
-                frc971::control_loops::drivetrain::swerve::Output>(
+            event_loop_->MakeFetcher<frc971::control_loops::swerve::Output>(
                 "/drivetrain")),
         swerve_publisher_(event_loop_.get(), exit_handle_.get(),
                           "y2024_swerve/swerve_drivetrain_output.json", 100) {}
@@ -39,7 +38,7 @@
   std::unique_ptr<aos::EventLoop> event_loop_;
   std::unique_ptr<aos::ExitHandle> exit_handle_;
 
-  aos::Fetcher<frc971::control_loops::drivetrain::swerve::Output>
+  aos::Fetcher<frc971::control_loops::swerve::Output>
       drivetrain_swerve_output_fetcher_;
 
   y2024_swerve::SwervePublisher swerve_publisher_;
diff --git a/y2024_swerve/wpilib_interface.cc b/y2024_swerve/wpilib_interface.cc
index 65741a4..853164f 100644
--- a/y2024_swerve/wpilib_interface.cc
+++ b/y2024_swerve/wpilib_interface.cc
@@ -4,14 +4,14 @@
 #include "aos/events/shm_event_loop.h"
 #include "aos/init.h"
 #include "frc971/control_loops/control_loops_generated.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_can_position_static.h"
+#include "frc971/control_loops/swerve/swerve_drivetrain_position_static.h"
 #include "frc971/wpilib/can_sensor_reader.h"
 #include "frc971/wpilib/sensor_reader.h"
 #include "frc971/wpilib/swerve/swerve_drivetrain_writer.h"
 #include "frc971/wpilib/talonfx.h"
 #include "frc971/wpilib/wpilib_robot_base.h"
 #include "y2024_swerve/constants.h"
-#include "y2024_swerve/drivetrain_can_position_static.h"
-#include "y2024_swerve/drivetrain_position_generated.h"
 
 DEFINE_bool(ctre_diag_server, false,
             "If true, enable the diagnostics server for interacting with "
@@ -33,24 +33,6 @@
   return optional.value();
 }
 
-flatbuffers::Offset<frc971::AbsolutePosition> module_offset(
-    frc971::AbsolutePosition::Builder builder,
-    frc971::wpilib::AbsoluteEncoder *module) {
-  builder.add_encoder(module->ReadRelativeEncoder());
-  builder.add_absolute_encoder(module->ReadAbsoluteEncoder());
-
-  return builder.Finish();
-}
-
-void populate_can_module(SwerveModuleCANPositionStatic *can_position,
-                         std::shared_ptr<SwerveModule> module) {
-  auto rotation = can_position->add_rotation();
-  module->rotation->SerializePosition(rotation, 1.0);
-
-  auto translation = can_position->add_translation();
-  module->translation->SerializePosition(translation, 1.0);
-}
-
 constexpr double kMaxFastEncoderPulsesPerSecond = std::max({
     constants::Values::kMaxDrivetrainEncoderPulsesPerSecond(),
 });
@@ -61,105 +43,68 @@
 class SensorReader : public ::frc971::wpilib::SensorReader {
  public:
   SensorReader(aos::ShmEventLoop *event_loop,
-               std::shared_ptr<const constants::Values> values)
+               std::shared_ptr<const constants::Values> values,
+               frc971::wpilib::swerve::SwerveModules modules)
       : ::frc971::wpilib::SensorReader(event_loop),
         values_(values),
         drivetrain_position_sender_(
-            event_loop->MakeSender<AbsoluteDrivetrainPosition>("/drivetrain")) {
+            event_loop
+                ->MakeSender<frc971::control_loops::swerve::PositionStatic>(
+                    "/drivetrain")),
+        modules_(modules) {
     UpdateFastEncoderFilterHz(kMaxFastEncoderPulsesPerSecond);
     event_loop->SetRuntimeAffinity(aos::MakeCpusetFromCpus({0}));
   }
 
   void RunIteration() override {
     {
-      auto builder = drivetrain_position_sender_.MakeBuilder();
+      auto builder = drivetrain_position_sender_.MakeStaticBuilder();
 
-      auto front_left_offset =
-          module_offset(builder.MakeBuilder<frc971::AbsolutePosition>(),
-                        &front_left_encoder_);
-      auto front_right_offset =
-          module_offset(builder.MakeBuilder<frc971::AbsolutePosition>(),
-                        &front_right_encoder_);
-      auto back_left_offset = module_offset(
-          builder.MakeBuilder<frc971::AbsolutePosition>(), &back_left_encoder_);
-      auto back_right_offset =
-          module_offset(builder.MakeBuilder<frc971::AbsolutePosition>(),
-                        &back_right_encoder_);
+      modules_.front_left->PopulatePosition(builder->add_front_left());
+      modules_.front_right->PopulatePosition(builder->add_front_right());
+      modules_.back_left->PopulatePosition(builder->add_back_left());
+      modules_.back_right->PopulatePosition(builder->add_back_right());
 
-      AbsoluteDrivetrainPosition::Builder drivetrain_position_builder =
-          builder.MakeBuilder<AbsoluteDrivetrainPosition>();
-
-      drivetrain_position_builder.add_follower_wheel_one_position(
-          follower_wheel_one_encoder_->GetRaw());
-      drivetrain_position_builder.add_follower_wheel_two_position(
-          follower_wheel_two_encoder_->GetRaw());
-
-      drivetrain_position_builder.add_front_left_position(front_left_offset);
-      drivetrain_position_builder.add_front_right_position(front_right_offset);
-      drivetrain_position_builder.add_back_left_position(back_left_offset);
-      drivetrain_position_builder.add_back_right_position(back_right_offset);
-
-      builder.CheckOk(builder.Send(drivetrain_position_builder.Finish()));
+      builder.CheckOk(builder.Send());
     }
   }
 
-  void set_follower_wheel_one_encoder(std::unique_ptr<frc::Encoder> encoder) {
+  void set_front_left_encoder(std::unique_ptr<frc::Encoder> encoder,
+                              std::unique_ptr<frc::DigitalInput> absolute_pwm) {
     fast_encoder_filter_.Add(encoder.get());
-    follower_wheel_one_encoder_ = std::move(encoder);
-    follower_wheel_one_encoder_->SetMaxPeriod(0.005);
-  }
-  void set_follower_wheel_two_encoder(std::unique_ptr<frc::Encoder> encoder) {
-    fast_encoder_filter_.Add(encoder.get());
-    follower_wheel_two_encoder_ = std::move(encoder);
-    follower_wheel_two_encoder_->SetMaxPeriod(0.005);
+    modules_.front_left->set_rotation_encoder(std::move(encoder),
+                                              std::move(absolute_pwm));
   }
 
-  void set_front_left_encoder(std::unique_ptr<frc::Encoder> encoder) {
-    fast_encoder_filter_.Add(encoder.get());
-    front_left_encoder_.set_encoder(std::move(encoder));
-  }
-  void set_front_left_absolute_pwm(
+  void set_front_right_encoder(
+      std::unique_ptr<frc::Encoder> encoder,
       std::unique_ptr<frc::DigitalInput> absolute_pwm) {
-    front_left_encoder_.set_absolute_pwm(std::move(absolute_pwm));
+    fast_encoder_filter_.Add(encoder.get());
+    modules_.front_right->set_rotation_encoder(std::move(encoder),
+                                               std::move(absolute_pwm));
   }
 
-  void set_front_right_encoder(std::unique_ptr<frc::Encoder> encoder) {
+  void set_back_left_encoder(std::unique_ptr<frc::Encoder> encoder,
+                             std::unique_ptr<frc::DigitalInput> absolute_pwm) {
     fast_encoder_filter_.Add(encoder.get());
-    front_right_encoder_.set_encoder(std::move(encoder));
-  }
-  void set_front_right_absolute_pwm(
-      std::unique_ptr<frc::DigitalInput> absolute_pwm) {
-    front_right_encoder_.set_absolute_pwm(std::move(absolute_pwm));
+    modules_.back_left->set_rotation_encoder(std::move(encoder),
+                                             std::move(absolute_pwm));
   }
 
-  void set_back_left_encoder(std::unique_ptr<frc::Encoder> encoder) {
+  void set_back_right_encoder(std::unique_ptr<frc::Encoder> encoder,
+                              std::unique_ptr<frc::DigitalInput> absolute_pwm) {
     fast_encoder_filter_.Add(encoder.get());
-    back_left_encoder_.set_encoder(std::move(encoder));
-  }
-  void set_back_left_absolute_pwm(
-      std::unique_ptr<frc::DigitalInput> absolute_pwm) {
-    back_left_encoder_.set_absolute_pwm(std::move(absolute_pwm));
-  }
-
-  void set_back_right_encoder(std::unique_ptr<frc::Encoder> encoder) {
-    fast_encoder_filter_.Add(encoder.get());
-    back_right_encoder_.set_encoder(std::move(encoder));
-  }
-  void set_back_right_absolute_pwm(
-      std::unique_ptr<frc::DigitalInput> absolute_pwm) {
-    back_right_encoder_.set_absolute_pwm(std::move(absolute_pwm));
+    modules_.back_right->set_rotation_encoder(std::move(encoder),
+                                              std::move(absolute_pwm));
   }
 
  private:
   std::shared_ptr<const constants::Values> values_;
 
-  aos::Sender<AbsoluteDrivetrainPosition> drivetrain_position_sender_;
+  aos::Sender<frc971::control_loops::swerve::PositionStatic>
+      drivetrain_position_sender_;
 
-  std::unique_ptr<frc::Encoder> follower_wheel_one_encoder_,
-      follower_wheel_two_encoder_;
-
-  frc971::wpilib::AbsoluteEncoder front_left_encoder_, front_right_encoder_,
-      back_left_encoder_, back_right_encoder_;
+  frc971::wpilib::swerve::SwerveModules modules_;
 };
 
 class WPILibRobot : public ::frc971::wpilib::WPILibRobotBase {
@@ -179,70 +124,62 @@
     std::vector<std::shared_ptr<TalonFX>> falcons;
 
     // TODO(max): Change the CanBus names with TalonFX software.
-    std::shared_ptr<SwerveModule> front_left = std::make_shared<SwerveModule>(
-        frc971::wpilib::TalonFXParams{6, false},
-        frc971::wpilib::TalonFXParams{5, false}, "Drivetrain Bus",
-        &signals_registry, constants::Values::kDrivetrainStatorCurrentLimit(),
-        constants::Values::kDrivetrainSupplyCurrentLimit());
-    std::shared_ptr<SwerveModule> front_right = std::make_shared<SwerveModule>(
-        frc971::wpilib::TalonFXParams{3, false},
-        frc971::wpilib::TalonFXParams{4, false}, "Drivetrain Bus",
-        &signals_registry, constants::Values::kDrivetrainStatorCurrentLimit(),
-        constants::Values::kDrivetrainSupplyCurrentLimit());
-    std::shared_ptr<SwerveModule> back_left = std::make_shared<SwerveModule>(
-        frc971::wpilib::TalonFXParams{8, false},
-        frc971::wpilib::TalonFXParams{7, false}, "Drivetrain Bus",
-        &signals_registry, constants::Values::kDrivetrainStatorCurrentLimit(),
-        constants::Values::kDrivetrainSupplyCurrentLimit());
-    std::shared_ptr<SwerveModule> back_right = std::make_shared<SwerveModule>(
-        frc971::wpilib::TalonFXParams{2, false},
-        frc971::wpilib::TalonFXParams{1, false}, "Drivetrain Bus",
-        &signals_registry, constants::Values::kDrivetrainStatorCurrentLimit(),
-        constants::Values::kDrivetrainSupplyCurrentLimit());
+    frc971::wpilib::swerve::SwerveModules modules{
+        .front_left = std::make_shared<SwerveModule>(
+            frc971::wpilib::TalonFXParams{6, false},
+            frc971::wpilib::TalonFXParams{5, false}, "Drivetrain Bus",
+            &signals_registry,
+            constants::Values::kDrivetrainStatorCurrentLimit(),
+            constants::Values::kDrivetrainSupplyCurrentLimit()),
+        .front_right = std::make_shared<SwerveModule>(
+            frc971::wpilib::TalonFXParams{3, false},
+            frc971::wpilib::TalonFXParams{4, false}, "Drivetrain Bus",
+            &signals_registry,
+            constants::Values::kDrivetrainStatorCurrentLimit(),
+            constants::Values::kDrivetrainSupplyCurrentLimit()),
+        .back_left = std::make_shared<SwerveModule>(
+            frc971::wpilib::TalonFXParams{8, false},
+            frc971::wpilib::TalonFXParams{7, false}, "Drivetrain Bus",
+            &signals_registry,
+            constants::Values::kDrivetrainStatorCurrentLimit(),
+            constants::Values::kDrivetrainSupplyCurrentLimit()),
+        .back_right = std::make_shared<SwerveModule>(
+            frc971::wpilib::TalonFXParams{2, false},
+            frc971::wpilib::TalonFXParams{1, false}, "Drivetrain Bus",
+            &signals_registry,
+            constants::Values::kDrivetrainStatorCurrentLimit(),
+            constants::Values::kDrivetrainSupplyCurrentLimit())};
 
     // Thread 1
     aos::ShmEventLoop can_sensor_reader_event_loop(&config.message());
     can_sensor_reader_event_loop.set_name("CANSensorReader");
 
-    falcons.push_back(front_left->rotation);
-    falcons.push_back(front_left->translation);
+    modules.PopulateFalconsVector(&falcons);
 
-    falcons.push_back(front_right->rotation);
-    falcons.push_back(front_right->translation);
-
-    falcons.push_back(back_left->rotation);
-    falcons.push_back(back_left->translation);
-
-    falcons.push_back(back_right->rotation);
-    falcons.push_back(back_right->translation);
-
-    aos::Sender<AbsoluteCANPositionStatic> can_position_sender =
-        can_sensor_reader_event_loop.MakeSender<AbsoluteCANPositionStatic>(
-            "/drivetrain");
+    aos::Sender<frc971::control_loops::swerve::CanPositionStatic>
+        can_position_sender =
+            can_sensor_reader_event_loop
+                .MakeSender<frc971::control_loops::swerve::CanPositionStatic>(
+                    "/drivetrain");
 
     CANSensorReader can_sensor_reader(
         &can_sensor_reader_event_loop, std::move(signals_registry), falcons,
-        [this, falcons, front_left, front_right, back_left, back_right,
+        [this, falcons, modules,
          &can_position_sender](ctre::phoenix::StatusCode status) {
           // TODO(max): use status properly in the flatbuffer.
           (void)status;
 
-          aos::Sender<AbsoluteCANPositionStatic>::StaticBuilder builder =
-              can_position_sender.MakeStaticBuilder();
+          aos::Sender<frc971::control_loops::swerve::CanPositionStatic>::
+              StaticBuilder builder = can_position_sender.MakeStaticBuilder();
 
           for (auto falcon : falcons) {
             falcon->RefreshNontimesyncedSignals();
           }
 
-          auto front_left_flatbuffer = builder->add_front_left();
-          auto front_right_flatbuffer = builder->add_front_right();
-          auto back_left_flatbuffer = builder->add_back_left();
-          auto back_right_flatbuffer = builder->add_back_right();
-
-          populate_can_module(front_left_flatbuffer, front_left);
-          populate_can_module(front_right_flatbuffer, front_right);
-          populate_can_module(back_left_flatbuffer, back_left);
-          populate_can_module(back_right_flatbuffer, back_right);
+          modules.front_left->PopulateCanPosition(builder->add_front_left());
+          modules.front_right->PopulateCanPosition(builder->add_front_right());
+          modules.back_left->PopulateCanPosition(builder->add_back_left());
+          modules.back_right->PopulateCanPosition(builder->add_back_right());
 
           builder.CheckOk(builder.Send());
         });
@@ -268,34 +205,26 @@
         &drivetrain_writer_event_loop,
         constants::Values::kDrivetrainWriterPriority, 12);
 
-    drivetrain_writer.set_talonfxs(front_left, front_right, back_left,
-                                   back_right);
+    drivetrain_writer.set_talonfxs(modules);
 
     AddLoop(&drivetrain_writer_event_loop);
 
     // Thread 3
     aos::ShmEventLoop sensor_reader_event_loop(&config.message());
     sensor_reader_event_loop.set_name("SensorReader");
-    SensorReader sensor_reader(&sensor_reader_event_loop, values);
+    SensorReader sensor_reader(&sensor_reader_event_loop, values, modules);
 
-    sensor_reader.set_follower_wheel_one_encoder(make_encoder(4));
-    sensor_reader.set_follower_wheel_two_encoder(make_encoder(5));
+    sensor_reader.set_front_left_encoder(
+        make_encoder(1), std::make_unique<frc::DigitalInput>(1));
 
-    sensor_reader.set_front_left_encoder(make_encoder(1));
-    sensor_reader.set_front_left_absolute_pwm(
-        std::make_unique<frc::DigitalInput>(1));
+    sensor_reader.set_front_right_encoder(
+        make_encoder(0), std::make_unique<frc::DigitalInput>(0));
 
-    sensor_reader.set_front_right_encoder(make_encoder(0));
-    sensor_reader.set_front_right_absolute_pwm(
-        std::make_unique<frc::DigitalInput>(0));
+    sensor_reader.set_back_left_encoder(make_encoder(2),
+                                        std::make_unique<frc::DigitalInput>(2));
 
-    sensor_reader.set_back_left_encoder(make_encoder(2));
-    sensor_reader.set_back_left_absolute_pwm(
-        std::make_unique<frc::DigitalInput>(2));
-
-    sensor_reader.set_back_right_encoder(make_encoder(3));
-    sensor_reader.set_back_right_absolute_pwm(
-        std::make_unique<frc::DigitalInput>(3));
+    sensor_reader.set_back_right_encoder(
+        make_encoder(3), std::make_unique<frc::DigitalInput>(3));
 
     AddLoop(&sensor_reader_event_loop);
 
diff --git a/y2024_swerve/y2024_swerve_roborio.json b/y2024_swerve/y2024_swerve_roborio.json
index 1b06f9f..1330e43 100644
--- a/y2024_swerve/y2024_swerve_roborio.json
+++ b/y2024_swerve/y2024_swerve_roborio.json
@@ -27,6 +27,14 @@
         },
         {
             "name": "/roborio/aos",
+            "type": "aos.RobotState",
+            "source_node": "roborio",
+            "frequency": 50,
+            "num_senders": 20,
+            "max_size": 192
+        },
+        {
+            "name": "/roborio/aos",
             "type": "aos.logging.LogMessageFbs",
             "source_node": "roborio",
             "frequency": 500,
@@ -39,7 +47,7 @@
             "source_node": "roborio",
             "frequency": 50,
             "num_senders": 20,
-            "max_size": 2000
+            "max_size": 3000
         },
         {
             "name": "/roborio/aos",
@@ -103,7 +111,7 @@
         },
         {
             "name": "/drivetrain",
-            "type": "y2024_swerve.AbsoluteDrivetrainPosition",
+            "type": "frc971.control_loops.swerve.Position",
             "source_node": "roborio",
             "frequency": 250,
             "num_senders": 1,
@@ -111,11 +119,11 @@
         },
         {
             "name": "/drivetrain",
-            "type": "y2024_swerve.AbsoluteCANPosition",
+            "type": "frc971.control_loops.swerve.CanPosition",
             "source_node": "roborio",
             "frequency": 250,
             "num_senders": 1,
-            "max_size": 480
+            "max_size": 656
         },
         {
             "name": "/can",
@@ -148,15 +156,7 @@
         },
         {
             "name": "/drivetrain",
-            "type": "frc971.control_loops.drivetrain.swerve.Position",
-            "source_node": "roborio",
-            "frequency": 400,
-            "max_size": 112,
-            "num_senders": 2
-        },
-        {
-            "name": "/drivetrain",
-            "type": "frc971.control_loops.drivetrain.swerve.Output",
+            "type": "frc971.control_loops.swerve.Output",
             "source_node": "roborio",
             "frequency": 400,
             "max_size": 200,