mirror of
https://github.com/lopsided98/nix-ros-overlay.git
synced 2025-06-10 01:42:24 +03:00
Add basic ROS2 support to the NixOS modules.
This commit is contained in:
parent
c0077930c0
commit
6c39cd534e
7 changed files with 295 additions and 17 deletions
27
modules/common.nix
Normal file
27
modules/common.nix
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
ros1Cfg = config.services.ros;
|
||||||
|
ros2Cfg = config.services.ros2;
|
||||||
|
in {
|
||||||
|
# Interface
|
||||||
|
|
||||||
|
# Implementation
|
||||||
|
|
||||||
|
config = mkIf (ros1Cfg.enable || ros2Cfg.enable) {
|
||||||
|
environment.etc."ros/rosdep/sources.list.d/20-default.list".source = pkgs.fetchurl {
|
||||||
|
url = "https://raw.githubusercontent.com/ros/rosdistro/225c14be89fdf7ecf028b4cf85fa82032f7728e1/rosdep/sources.list.d/20-default.list";
|
||||||
|
sha256 = "0kxknc42y01pci8fxzhg84ybhgqyxqimycck27vb4b282lqfkzj7";
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users.ros = {
|
||||||
|
group = "ros";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
groups.ros = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
{ ... }: {
|
{ ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
./core.nix
|
./common.nix
|
||||||
./ros.nix
|
./ros1/core.nix
|
||||||
./nodes.nix
|
./ros1/ros.nix
|
||||||
|
./ros1/nodes.nix
|
||||||
|
./ros2/ros.nix
|
||||||
|
./ros2/nodes.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ in {
|
||||||
# FIXME: mkAfter is used to make sure the Python overlay is applied. That
|
# FIXME: mkAfter is used to make sure the Python overlay is applied. That
|
||||||
# means all other user configured Python overlays are ignored. This needs a
|
# means all other user configured Python overlays are ignored. This needs a
|
||||||
# fix in nixpkgs: https://github.com/NixOS/nixpkgs/issues/44426
|
# fix in nixpkgs: https://github.com/NixOS/nixpkgs/issues/44426
|
||||||
nixpkgs.overlays = mkAfter (singleton (import ../overlay.nix));
|
nixpkgs.overlays = mkAfter (singleton (import ../../overlay.nix));
|
||||||
|
|
||||||
services.ros = {
|
services.ros = {
|
||||||
pkgs = mkDefault (pkgs.rosPackages."${cfg.distro}".overrideScope cfg.overlays);
|
pkgs = mkDefault (pkgs.rosPackages."${cfg.distro}".overrideScope cfg.overlays);
|
||||||
|
@ -89,11 +89,6 @@ in {
|
||||||
masterUri = mkDefault "http://${cfg.hostname}:11311/";
|
masterUri = mkDefault "http://${cfg.hostname}:11311/";
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.etc."ros/rosdep/sources.list.d/20-default.list".source = pkgs.fetchurl {
|
|
||||||
url = "https://raw.githubusercontent.com/ros/rosdistro/225c14be89fdf7ecf028b4cf85fa82032f7728e1/rosdep/sources.list.d/20-default.list";
|
|
||||||
sha256 = "0kxknc42y01pci8fxzhg84ybhgqyxqimycck27vb4b282lqfkzj7";
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.variables = {
|
environment.variables = {
|
||||||
ROS_HOSTNAME = cfg.hostname;
|
ROS_HOSTNAME = cfg.hostname;
|
||||||
ROS_MASTER_URI = cfg.masterUri;
|
ROS_MASTER_URI = cfg.masterUri;
|
||||||
|
@ -106,13 +101,5 @@ in {
|
||||||
inherit paths;
|
inherit paths;
|
||||||
extraOutputsToInstall = optional config.environment.enableDebugInfo "debug";
|
extraOutputsToInstall = optional config.environment.enableDebugInfo "debug";
|
||||||
}) ];
|
}) ];
|
||||||
|
|
||||||
users = {
|
|
||||||
users.ros = {
|
|
||||||
group = "ros";
|
|
||||||
isSystemUser = true;
|
|
||||||
};
|
|
||||||
groups.ros = { };
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
172
modules/ros2/nodes.nix
Normal file
172
modules/ros2/nodes.nix
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.ros2;
|
||||||
|
|
||||||
|
commonServiceOptions = {
|
||||||
|
package = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
ROS package name containing this node or launch file. This is the ROS
|
||||||
|
name for the package, rather than the attribute name, meaning it uses
|
||||||
|
underscores rather than dashes.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
paths = mkOption {
|
||||||
|
type = types.listOf types.package;
|
||||||
|
description = ''
|
||||||
|
Additional paths to add to the environment of this node. The
|
||||||
|
<option>package</option> option will be turned into an attribute
|
||||||
|
name and automatically added to this option if valid.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
env = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
description = ''
|
||||||
|
Environment created with the ROS specific buildEnv function for
|
||||||
|
this node.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
commonServiceConfig = { name, config, ... }: {
|
||||||
|
# Try to convert package name to attribute and add it to the
|
||||||
|
# environment
|
||||||
|
paths = let
|
||||||
|
packageAttr = replaceStrings ["_"] ["-"] config.package;
|
||||||
|
in optional (hasAttr packageAttr cfg.pkgs) cfg.pkgs."${packageAttr}";
|
||||||
|
|
||||||
|
env = mkDefault (cfg.pkgs.buildEnv {
|
||||||
|
name = "ros-node-${name}-env";
|
||||||
|
inherit (config) paths;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceGenerator = execStartFn: services: mapAttrs' (name: config: nameValuePair name {
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "exec";
|
||||||
|
StateDirectory = "ros";
|
||||||
|
User = "ros";
|
||||||
|
Group = "ros";
|
||||||
|
ExecStart = execStartFn config;
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
environment = {
|
||||||
|
ROS_DOMAIN_ID = toString cfg.domainId;
|
||||||
|
ROS_HOME = "/var/lib/ros";
|
||||||
|
};
|
||||||
|
}) services;
|
||||||
|
in {
|
||||||
|
# Interface
|
||||||
|
|
||||||
|
options.services.ros2 = {
|
||||||
|
nodes = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule ({ name, config, ... }@args: {
|
||||||
|
options = commonServiceOptions // {
|
||||||
|
node = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Name of the node to launch.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
args = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Arguments to pass to the node.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
rosArgs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
ROS specific arguments to pass to the node using --ros-args
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
params = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Key-value parameters to pass to the node.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
(commonServiceConfig args)
|
||||||
|
{
|
||||||
|
paths = [ cfg.pkgs.ros2run ];
|
||||||
|
rosArgs = concatMap (k: v: [ "--param" "${k}:=${v}" ]) cfg.params;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
ROS nodes to launch at boot as systemd services.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
launchFiles = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule ({ name, config, ... }@args: {
|
||||||
|
options = commonServiceOptions // {
|
||||||
|
launchFile = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Name of the launch file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
args = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Key-value arguments to pass to the launch file
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
launchArgs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Extra command line arguments to pass to ros2 launch.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkMerge [
|
||||||
|
(commonServiceConfig args)
|
||||||
|
{ paths = with cfg.pkgs; [ cfg.pkgs.ros2launch ]; }
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
ROS launch files to start at boot as systemd services.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Implementation
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services = mkMerge [
|
||||||
|
(serviceGenerator (config: escapeShellArgs (
|
||||||
|
[ "${config.env}/bin/ros2" "run" config.package config.node ] ++
|
||||||
|
config.args ++
|
||||||
|
[ "--ros-args" ] ++ config.rosArgs ++ [ "--" ]
|
||||||
|
)) cfg.nodes)
|
||||||
|
(serviceGenerator (config: escapeShellArgs (
|
||||||
|
[ "${config.env}/bin/ros2" "launch" ] ++
|
||||||
|
config.launchArgs ++
|
||||||
|
[ config.package config.launchFile ] ++
|
||||||
|
(mapAttrsToList (n: v: "${n}:=${v}") config.args)
|
||||||
|
)) cfg.launchFiles)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
89
modules/ros2/ros.nix
Normal file
89
modules/ros2/ros.nix
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.ros2;
|
||||||
|
|
||||||
|
pkgsType = mkOptionType {
|
||||||
|
name = "ros-packages";
|
||||||
|
description = "ROS package set";
|
||||||
|
check = p: isAttrs p && hasAttr "ament-cmake" p;
|
||||||
|
};
|
||||||
|
overlayType = mkOptionType {
|
||||||
|
name = "ros-overlay";
|
||||||
|
description = "ROS package set overlay";
|
||||||
|
check = isFunction;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
# Interface
|
||||||
|
|
||||||
|
options.services.ros2 = {
|
||||||
|
enable = mkEnableOption "Robot Operating System, version 2";
|
||||||
|
|
||||||
|
distro = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "humble";
|
||||||
|
description = ''
|
||||||
|
ROS distro to use. Must be defined in distros/default.nix.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgs = mkOption {
|
||||||
|
type = pkgsType;
|
||||||
|
description = ''
|
||||||
|
ROS package set for the selected distro.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
overlays = mkOption {
|
||||||
|
type = types.listOf overlayType;
|
||||||
|
default = [];
|
||||||
|
apply = composeManyExtensions;
|
||||||
|
description = ''
|
||||||
|
Set of package overlays to apply to ROS package set for the configured
|
||||||
|
distro.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
domainId = mkOption {
|
||||||
|
type = types.ints.between 0 232;
|
||||||
|
default = 0;
|
||||||
|
description = ''
|
||||||
|
DDS Domain ID that defines the ports used for communication between
|
||||||
|
processes.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemPackages = mkOption {
|
||||||
|
default = p: [];
|
||||||
|
example = literalExample "p: with p; [ ros2cli ros2run ]";
|
||||||
|
description = ''
|
||||||
|
Packages to add to a ROS environment that will be added to the system
|
||||||
|
PATH. The provided function will be passed the package set configured by
|
||||||
|
<option>services.ros.pkgs</option>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Implementation
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# FIXME: mkAfter is used to make sure the Python overlay is applied. That
|
||||||
|
# means all other user configured Python overlays are ignored. This needs a
|
||||||
|
# fix in nixpkgs: https://github.com/NixOS/nixpkgs/issues/44426
|
||||||
|
nixpkgs.overlays = mkAfter (singleton (import ../../overlay.nix));
|
||||||
|
|
||||||
|
services.ros2.pkgs = mkDefault (pkgs.rosPackages."${cfg.distro}".overrideScope cfg.overlays);
|
||||||
|
|
||||||
|
environment.variables.ROS_DOMAIN_ID = toString cfg.domainId;
|
||||||
|
|
||||||
|
environment.systemPackages = let
|
||||||
|
paths = cfg.systemPackages cfg.pkgs;
|
||||||
|
in mkIf (length paths != 0) [ (cfg.pkgs.buildEnv {
|
||||||
|
name = "ros2-system-env";
|
||||||
|
inherit paths;
|
||||||
|
extraOutputsToInstall = optional config.environment.enableDebugInfo "debug";
|
||||||
|
}) ];
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue