mirror of
https://github.com/lopsided98/nix-ros-overlay.git
synced 2025-06-09 17:42:22 +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 = [
|
||||
./core.nix
|
||||
./ros.nix
|
||||
./nodes.nix
|
||||
./common.nix
|
||||
./ros1/core.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
|
||||
# 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));
|
||||
nixpkgs.overlays = mkAfter (singleton (import ../../overlay.nix));
|
||||
|
||||
services.ros = {
|
||||
pkgs = mkDefault (pkgs.rosPackages."${cfg.distro}".overrideScope cfg.overlays);
|
||||
|
@ -89,11 +89,6 @@ in {
|
|||
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 = {
|
||||
ROS_HOSTNAME = cfg.hostname;
|
||||
ROS_MASTER_URI = cfg.masterUri;
|
||||
|
@ -106,13 +101,5 @@ in {
|
|||
inherit paths;
|
||||
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