From 90dc3729a92bbdca7be11a4906c23dda6152a2a6 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+asorge29@users.noreply.github.com> Date: Sat, 28 Feb 2026 02:24:22 -0800 Subject: [PATCH 1/9] feat: service to setup can on boot --- util/guppy_can/install_can_service.sh | 16 +++++++++++++++ util/guppy_can/setup_can.service | 11 +++++++++++ util/guppy_can/setup_can.sh | 28 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100755 util/guppy_can/install_can_service.sh create mode 100755 util/guppy_can/setup_can.service create mode 100755 util/guppy_can/setup_can.sh diff --git a/util/guppy_can/install_can_service.sh b/util/guppy_can/install_can_service.sh new file mode 100755 index 0000000..a27111c --- /dev/null +++ b/util/guppy_can/install_can_service.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Check if script is run as root +if [[ "$EUID" -ne 0 ]]; then + echo "Error: This script must be run as sudo or as root." + echo "Usage: sudo $0" + exit 1 +fi + +set -e + +cp setup_can.sh /etc/setup_can.sh + +cp setup_can.service /etc/systemd/system/guppy_can.service + +systemctl enable --now guppy_can.service \ No newline at end of file diff --git a/util/guppy_can/setup_can.service b/util/guppy_can/setup_can.service new file mode 100755 index 0000000..4b8d5b6 --- /dev/null +++ b/util/guppy_can/setup_can.service @@ -0,0 +1,11 @@ +[Unit] +Description=Sets up can interface can0 +After=network.target + +[Service] +Type=oneshot +ExecStart=/etc/setup_can.sh +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/util/guppy_can/setup_can.sh b/util/guppy_can/setup_can.sh new file mode 100755 index 0000000..5b73986 --- /dev/null +++ b/util/guppy_can/setup_can.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Check if script is run as root +if [[ "$EUID" -ne 0 ]]; then + echo "Error: This script must be run as sudo or as root." + echo "Usage: sudo $0" + exit 1 +fi + +set -e + +adapter="" +for dev in /dev/ttyACM*; do + [ -e "$dev" ] || continue + if udevadm info -a -n "$dev" | grep -qi 'canable'; then + adapter="$dev" + echo "Found CAN adapter at $adapter" + break + fi +done + +if [ -z "$adapter" ]; then + echo "No CAN adapter found" >&2 + exit 1 +fi + +slcand -o -c -s6 "$adapter" +ip link set can0 up \ No newline at end of file From beb9896e520960b3f479331e6e304327924f6574 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+asorge29@users.noreply.github.com> Date: Sun, 1 Mar 2026 21:53:13 -0800 Subject: [PATCH 2/9] fix: change subnet to match dvl --- util/guppy_network/99-guppy-netplan.yaml | 4 ++-- util/guppy_network/dnsmasq.conf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/util/guppy_network/99-guppy-netplan.yaml b/util/guppy_network/99-guppy-netplan.yaml index 820918c..e3bb4e6 100755 --- a/util/guppy_network/99-guppy-netplan.yaml +++ b/util/guppy_network/99-guppy-netplan.yaml @@ -8,8 +8,8 @@ network: enp2s0: dhcp4: no addresses: - - 192.168.3.1/24 + - 192.168.194.1/24 enp1s0: dhcp4: no addresses: - - 192.168.3.1/24 \ No newline at end of file + - 192.168.194.1/24 \ No newline at end of file diff --git a/util/guppy_network/dnsmasq.conf b/util/guppy_network/dnsmasq.conf index 5a1a59f..7cb468c 100755 --- a/util/guppy_network/dnsmasq.conf +++ b/util/guppy_network/dnsmasq.conf @@ -3,8 +3,8 @@ interface=enp2s0 bind-dynamic # dnsmasq will watch for the interfaces to come up and down, and will bind to them only when they are up # 12 hour lease time for DHCP clients -dhcp-range=interface:enp1s0,192.168.3.100,192.168.3.150,255.255.255.0,12h -dhcp-range=interface:enp2s0,192.168.3.150,192.168.3.200,255.255.255.0,12h +dhcp-range=interface:enp1s0,192.168.194.100,192.168.194.150,255.255.255.0,12h +dhcp-range=interface:enp2s0,192.168.194.150,192.168.194.200,255.255.255.0,12h -dhcp-option=option:router,192.168.3.1 +dhcp-option=option:router,192.168.194.1 dhcp-option=option:netmask,255.255.255.0 \ No newline at end of file From 2b01eaadb808c6e225dcebfe4c427d35675a5c2c Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:09:04 -0700 Subject: [PATCH 3/9] feat: nix flake to develop on other systems --- .clangd | 1 + .gitignore | 5 +++- flake.lock | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.clangd b/.clangd index c6f6b4f..401942d 100644 --- a/.clangd +++ b/.clangd @@ -1,4 +1,5 @@ CompileFlags: + CompilationDatabase: build/ Add: - -std=gnu++17 - -I/opt/ros/jazzy/include diff --git a/.gitignore b/.gitignore index 3533592..55cb6cd 100644 --- a/.gitignore +++ b/.gitignore @@ -322,4 +322,7 @@ __marimo__/ # debug information files *.dwo -compile_commands.json \ No newline at end of file +compile_commands.json + +# NixOS stuff +.direnv \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f2052f2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,84 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nix-ros-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1774001258, + "narHash": "sha256-DoT1Dyg/8O1P980AqP5KbzVQu5iC5BarvgrUspS17VM=", + "owner": "lopsided98", + "repo": "nix-ros-overlay", + "rev": "9816ceba7fc2e340308768fdecfd7c07e8a70730", + "type": "github" + }, + "original": { + "owner": "lopsided98", + "ref": "master", + "repo": "nix-ros-overlay", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759381078, + "narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", + "type": "github" + }, + "original": { + "owner": "lopsided98", + "ref": "nix-ros", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nix-ros-overlay": "nix-ros-overlay", + "nixpkgs": [ + "nix-ros-overlay", + "nixpkgs" + ] + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..b0799d9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,77 @@ +{ + inputs = { + nix-ros-overlay.url = "github:lopsided98/nix-ros-overlay/master"; + nixpkgs.follows = "nix-ros-overlay/nixpkgs"; # IMPORTANT!!! + }; + outputs = { self, nix-ros-overlay, nixpkgs }: + nix-ros-overlay.inputs.flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ nix-ros-overlay.overlays.default ]; + }; + in { + devShells.default = pkgs.mkShell { + name = "guppy_ros"; + packages = [ + # non ros + + # build + pkgs.colcon + pkgs.cmake + pkgs.clang-tools + + # deps + pkgs.proxsuite + pkgs.python3Packages.pygame + pkgs.python3Packages.pip + + # extra + pkgs.fastfetch + + (with pkgs.rosPackages.jazzy; buildEnv { + # ros packages + paths = [ + # ros base + ros-core + ros-base + rclcpp + rclpy + + # ros msgs + std-msgs + geometry-msgs + sensor-msgs + nav-msgs + + # rqt + rqt + rqt-common-plugins + + # build + ament-cmake + ament-cmake-python + ament-lint-auto + + # launch + launch + launch-ros + launch-xml + + # deps + ros2-control + control-toolbox + ]; + }) + ]; + shellHook = '' + export ROS_DOMAIN_ID=0 + fastfetch -l ./.github/guppy.txt + ''; + }; + }); + nixConfig = { + extra-substituters = [ "https://ros.cachix.org" ]; + extra-trusted-public-keys = [ "ros.cachix.org-1:dSyZxI8geDCJrwgvCOHDoAfOm5sV1wCPjBkKL+38Rvo=" ]; + }; +} \ No newline at end of file From 888d6ebdc2f003d52001b7e78fd34bead7fe2f30 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:06:39 -0700 Subject: [PATCH 4/9] fix: add deps for vendors and sim --- flake.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flake.nix b/flake.nix index b0799d9..6eb2dc7 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,7 @@ # extra pkgs.fastfetch + pkgs.can-utils (with pkgs.rosPackages.jazzy; buildEnv { # ros packages @@ -50,6 +51,7 @@ # build ament-cmake + ament-cmake-core # vectornav_msgs ament-cmake-python ament-lint-auto @@ -61,6 +63,12 @@ # deps ros2-control control-toolbox + ros-gz-interfaces # gncea_autonomy + python-cmake-module # vecornav_msgs + marine-acoustic-msgs # waterlinked_dvl_driver + ros-gz-sim # gncea_description + ros-gz-bridge # sim + joint-state-publisher # sim ]; }) ]; From 833690d6dfab7a8e460041e6e45e585f9e4cfcab Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:11:26 -0700 Subject: [PATCH 5/9] chore: update readme with nix info --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 6f0fbcb..ed5876d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,19 @@ The included `Dockerfile` and `devcontainer.json` allow the guppy repo to be ope To interact with the Docker contaner graphically, you can visit [localhost:6080/vnc.html](https://localhost:6080/vnc.html), or connect with a VCN client to [localhost:5901](vnc://localhost:5091) +### Nix & NixOS + +The included `flake.nix` allows for development, sim, and operation to run on any x86 machine with Nix or NixOS installed. + +After cloning, simply run: +```bash +nix develop +``` +Or if using `direnv`, run: +```bash +direnv allow +``` + ## Organization The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory: - [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts. From 6e77fba9789c224d8676a20ba47f458dda7aea3e Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:12:51 -0700 Subject: [PATCH 6/9] chore: credit nix-ros-overlay --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ed5876d..db9439e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ Or if using `direnv`, run: direnv allow ``` +This is made possible using the [nix-ros-overlay](https://github.com/lopsided98/nix-ros-overlay) + ## Organization The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory: - [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts. From 3463217d7ed1bbbc19176502c7bf05e19f7d2588 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 21:08:54 -0700 Subject: [PATCH 7/9] fix: networking setup --- README.md | 13 +++++++++++++ flake.nix | 1 - 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index db9439e..a1b8540 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,19 @@ direnv allow This is made possible using the [nix-ros-overlay](https://github.com/lopsided98/nix-ros-overlay) +#### Nix Networking + +If you encounter issues, make sure to allow the UDP ports required by ROS in your `configuration.nix`: +```nix +networking.firewall = { + allowedUDPPorts = [ 7400 7401 ]; + allowedUDPPortRanges = [ + { from = 7410; to = 7500; } + ]; +}; +``` + + ## Organization The code is broken up into several ROS 2 packages, in the [`src/`](./src/) directory: - [**`guppy`**](./src/guppy/#readme): A metapackage that contains dependencies of all other packages, as well as bringup and launch scripts. diff --git a/flake.nix b/flake.nix index 6eb2dc7..f826b99 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,6 @@ }) ]; shellHook = '' - export ROS_DOMAIN_ID=0 fastfetch -l ./.github/guppy.txt ''; }; From 187cddf12b7c179f888bea0ddc0d63c806fb3348 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:18:31 -0700 Subject: [PATCH 8/9] feat: send 0 to all thrusters when in disabled state --- src/guppy_control/CMakeLists.txt | 3 ++- .../include/guppy_control/t200_interface.hpp | 8 ++++++++ src/guppy_control/package.xml | 1 + src/guppy_control/src/control_chassis_node.cpp | 14 +++++++++++++- src/guppy_control/src/t200_interface.cpp | 9 ++++++++- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/guppy_control/CMakeLists.txt b/src/guppy_control/CMakeLists.txt index 0c93b8e..319ed5e 100644 --- a/src/guppy_control/CMakeLists.txt +++ b/src/guppy_control/CMakeLists.txt @@ -16,13 +16,14 @@ if(NOT CMAKE_CXX_STANDARD) find_package(control_toolbox REQUIRED) find_package(geometry_msgs REQUIRED) find_package(nav_msgs REQUIRED) + find_package(guppy_msgs REQUIRED) find_package(eigen3_cmake_module REQUIRED) find_package(Eigen3) include_directories(include) add_executable(control_chassis src/control_chassis_node.cpp src/chassis_controller.cpp src/t200_interface.cpp) - ament_target_dependencies(control_chassis control_toolbox geometry_msgs nav_msgs Eigen3 proxsuite) + ament_target_dependencies(control_chassis control_toolbox geometry_msgs nav_msgs guppy_msgs Eigen3 proxsuite) set_source_files_properties(src/chassis_controller.cpp PROPERTIES COMPILE_OPTIONS "-O3;-DNDEBUG;-DPROXSUITE_VECTORIZE" ) diff --git a/src/guppy_control/include/guppy_control/t200_interface.hpp b/src/guppy_control/include/guppy_control/t200_interface.hpp index 97b44a9..ab2f7f7 100644 --- a/src/guppy_control/include/guppy_control/t200_interface.hpp +++ b/src/guppy_control/include/guppy_control/t200_interface.hpp @@ -27,6 +27,9 @@ class T200Interface { /* the can interface (like can0 or vcan0) */ std::string can_interface_; + + /* are thrusters enabled */ + bool enabled_; /* @brief sets up the CAN interface and socket @@ -76,6 +79,11 @@ class T200Interface { @return whether or not the operation was sucessful for all values */ bool shutdown(); + + /* + @brief sets thrusters to be enabled/disabled + */ + void set_enabled(bool enabled); }; } diff --git a/src/guppy_control/package.xml b/src/guppy_control/package.xml index 29a2c07..171fcfb 100644 --- a/src/guppy_control/package.xml +++ b/src/guppy_control/package.xml @@ -12,6 +12,7 @@ ros2_control proxsuite control_toolbox + guppy_msgs ament_lint_auto ament_lint_common diff --git a/src/guppy_control/src/control_chassis_node.cpp b/src/guppy_control/src/control_chassis_node.cpp index 0c66ce5..bb2b2a8 100644 --- a/src/guppy_control/src/control_chassis_node.cpp +++ b/src/guppy_control/src/control_chassis_node.cpp @@ -1,9 +1,11 @@ +#include #include "rclcpp/rclcpp.hpp" #include "guppy_control/chassis_controller.hpp" #include "guppy_control/t200_interface.hpp" #include "std_msgs/msg/float32.hpp" #include "std_msgs/msg/float64.hpp" +#include "guppy_msgs/msg/state.hpp" using namespace std::chrono_literals; using namespace t200_interface; @@ -38,7 +40,7 @@ class ControlChassis : public rclcpp::Node { load_params_(¶meters); // load parameters from configuration file this->param_subscriber_ = std::make_shared(this); // subscribe to parameter change event - + // parameter callback to update parameters on event auto param_callback = [this](const rcl_interfaces::msg::ParameterEvent & parameter_event) { @@ -105,6 +107,7 @@ class ControlChassis : public rclcpp::Node { // setup subscriptions odom_subscription_ = this->create_subscription("/odom",10,std::bind(&ControlChassis::odom_callback, this, std::placeholders::_1)); cmd_vel_subscription_ = this->create_subscription("/cmd_vel",10,std::bind(&ControlChassis::cmdvel_callback, this, std::placeholders::_1)); + state_subscription_ = this->create_subscription("/state", 10, std::bind(&ControlChassis::state_callback, this, std::placeholders::_1)); auto timer_callback = [this]() -> void { auto thrusts = controller->get_motor_thrusts(); @@ -139,11 +142,20 @@ class ControlChassis : public rclcpp::Node { void cmdvel_callback(geometry_msgs::msg::Twist::SharedPtr msg) { controller->update_desired_state(msg); } + + void state_callback(guppy_msgs::msg::State::SharedPtr msg) { + if (msg->state == guppy_msgs::msg::State::DISABLED) { + this->thruster_interface->set_enabled(false); + } else { + this->thruster_interface->set_enabled(true); + } + } private: rclcpp::Publisher::SharedPtr sim_motor_publishers_[8]; rclcpp::Subscription::SharedPtr odom_subscription_; rclcpp::Subscription::SharedPtr cmd_vel_subscription_; + rclcpp::Subscription::SharedPtr state_subscription_; rclcpp::TimerBase::SharedPtr timer_; std::shared_ptr param_subscriber_; diff --git a/src/guppy_control/src/t200_interface.cpp b/src/guppy_control/src/t200_interface.cpp index ab55b55..1de0304 100644 --- a/src/guppy_control/src/t200_interface.cpp +++ b/src/guppy_control/src/t200_interface.cpp @@ -36,7 +36,10 @@ bool T200Interface::write(Eigen::VectorXd throttles) { bool okay = true; for (size_t i=0; i < n; i++) { - okay = okay && send_to_can(can_ids[i], throttles[i]); + if (this->enabled_) + okay = okay && send_to_can(can_ids[i], throttles[i]); + else + okay = okay && send_to_can(can_ids[i], 0); // std::cout << can_ids[i] << "\t" << throttles[i] << "\t" << okay << std::endl; } @@ -63,4 +66,8 @@ bool T200Interface::shutdown() { return okay; } +void T200Interface::set_enabled(bool enabled) { + this->enabled_ = enabled; +} + } \ No newline at end of file From 449fd1e91f6ffa4eaea7d0575515913a8be10972 Mon Sep 17 00:00:00 2001 From: Andy Sorge <124544413+andy-sorge@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:28:38 -0700 Subject: [PATCH 9/9] feat: add rviz, include .envrc for direnv --- .envrc | 2 ++ .gitignore | 1 - flake.nix | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..2b33721 --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +use flake +source install/setup.bash \ No newline at end of file diff --git a/.gitignore b/.gitignore index 55cb6cd..0538423 100644 --- a/.gitignore +++ b/.gitignore @@ -216,7 +216,6 @@ activemq-data/ # Environments .env -.envrc .venv env/ venv/ diff --git a/flake.nix b/flake.nix index f826b99..befc9a9 100644 --- a/flake.nix +++ b/flake.nix @@ -60,6 +60,9 @@ launch-ros launch-xml + # rviz + rviz2 + # deps ros2-control control-toolbox