Skip to content

Commit

Permalink
Save work
Browse files Browse the repository at this point in the history
  • Loading branch information
rafal-gorecki committed Sep 3, 2024
1 parent 670137f commit e11b9ea
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Launch arguments are largely common to both simulation and physical robot. Howev
| 🤖🖥️ | `use_sim` | Whether simulation is used. <br/> ***bool:*** `False` |
| 🤖🖥️ | `user_led_animations_file` | Path to a YAML file with a description of the user-defined animations. <br/> ***string:*** `''` |
| 🤖🖥️ | `wheel_config_path` | Path to wheel configuration file. <br/> ***string:*** [`{wheel_type}.yaml`](./panther_description/config) |
| 🤖🖥️ | `wheel_type` | Type of wheel. If you choose a value from the preset options ('WH01', 'WH02', 'WH04'), you can ignore the 'wheel_config_path' and 'controller_config_path' parameters. For custom wheels, please define these parameters to point to files that accurately describe the custom wheels. <br/> ***string:*** `WH01` (choices: `WH01`, `WH02`, `WH04`, `custom`) |
| 🤖🖥️ | `wheel_type` | Specify the wheel type. If the selected wheel type is not 'custom', the wheel_config_path and controller_config_path arguments will be automatically adjusted and can be omitted. <br/> ***string:*** `WH01` (choices: `WH01`, `WH02`, `WH04`, `custom`) |
| 🖥️ | `x` | Initial robot position in the global 'x' axis. <br/> ***float:*** `0.0` |
| 🖥️ | `y` | Initial robot position in the global 'y' axis. <br/> ***float:***` -2.0` |
| 🖥️ | `z` | Initial robot position in the global 'z' axis. <br/> ***float:*** `0.2` |
Expand Down
2 changes: 1 addition & 1 deletion lynx_description/urdf/wheel.urdf.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,4 @@

</xacro:macro>

</robot>
</robot>
2 changes: 1 addition & 1 deletion panther_bringup/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ project(panther_bringup)

find_package(ament_cmake REQUIRED)

install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
install(DIRECTORY config launch DESTINATION share/${PROJECT_NAME})

ament_package()
16 changes: 16 additions & 0 deletions panther_bringup/config/configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# The minimal configuration file with the default values.
# namespace is optional - without this element namespace will not be provided.

# namespace:
# robot_model: panther
# init_pose: [0.0, 0.0, 0.0]
# init_rotation: [0.0, 0.0, 0.0]
# configuration:
# wheel_type: WH01

panther:
robot_model: panther
init_pose: [0.0, 0.0, 0.0]
init_rotation: [0.0, 0.0, 0.0]
configuration:
wheel_type: WH01
6 changes: 3 additions & 3 deletions panther_bringup/launch/bringup.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
PathJoinSubstitution,
)
from launch_ros.substitutions import FindPackageShare
from panther_utils.welcomeMsg import welcomeMsg
from panther_utils.messages import welcome_msg


def generate_launch_description():
Expand Down Expand Up @@ -53,7 +53,7 @@ def generate_launch_description():

serial_no = EnvironmentVariable(name="PANTHER_SERIAL_NO", default_value="----")
panther_version = EnvironmentVariable(name="PANTHER_ROBOT_VERSION", default_value="1.0")
welcome_msg = welcomeMsg(serial_no, panther_version)
welcome_info = welcome_msg(serial_no, panther_version)

controller_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
Expand Down Expand Up @@ -129,7 +129,7 @@ def generate_launch_description():
declare_disable_manager_arg,
declare_namespace_arg,
declare_use_ekf_arg,
welcome_msg,
welcome_info,
controller_launch,
system_monitor_launch,
delayed_action,
Expand Down
13 changes: 3 additions & 10 deletions panther_controller/launch/controller.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,9 @@ def generate_launch_description():
"wheel_type",
default_value="WH01",
description=(
"Type of wheel. If you choose a value from the preset options ('WH01', 'WH02',"
" 'WH04'), you can ignore the 'wheel_config_path' and 'controller_config_path'"
" parameters. For custom wheels, please define these parameters to point to files that"
" accurately describe the custom wheels."
"Specify the wheel type. If the selected wheel type is not 'custom', "
"the 'wheel_config_path' and 'controller_config_path' arguments will be "
"automatically adjusted and can be omitted."
),
choices=["WH01", "WH02", "WH04", "custom"],
)
Expand Down Expand Up @@ -219,8 +218,6 @@ def generate_launch_description():
"controller_manager",
"--controller-manager-timeout",
"10",
"--namespace",
namespace,
],
namespace=namespace,
emulate_tty=True,
Expand All @@ -235,8 +232,6 @@ def generate_launch_description():
"controller_manager",
"--controller-manager-timeout",
"10",
"--namespace",
namespace,
],
namespace=namespace,
emulate_tty=True,
Expand All @@ -259,8 +254,6 @@ def generate_launch_description():
"controller_manager",
"--controller-manager-timeout",
"10",
"--namespace",
namespace,
],
namespace=namespace,
emulate_tty=True,
Expand Down
7 changes: 3 additions & 4 deletions panther_description/launch/load_urdf.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ def generate_launch_description():
"wheel_type",
default_value="WH01",
description=(
"Type of wheel. If you choose a value from the preset options ('WH01', 'WH02',"
" 'WH04'), you can ignore the 'wheel_config_path' and 'controller_config_path'"
" parameters. For custom wheels, please define these parameters to point to files that"
" accurately describe the custom wheels."
"Specify the wheel type. If the selected wheel type is not 'custom', "
"the 'wheel_config_path' and 'controller_config_path' arguments will be "
"automatically adjusted and can be omitted."
),
choices=["WH01", "WH02", "WH04", "custom"],
)
Expand Down
6 changes: 3 additions & 3 deletions panther_gazebo/launch/spawn_robot.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
)
from launch_ros.actions import Node, SetUseSimTime
from launch_ros.substitutions import FindPackageShare
from panther_utils.welcomeMsg import welcomeMsg
from panther_utils.messages import welcome_msg


def generate_launch_description():
Expand Down Expand Up @@ -79,7 +79,7 @@ def generate_launch_description():
"Robot namespace": namespace,
"Initial pose": ["(", x, ", ", y, ", ", z, ", ", roll, ", ", pitch, ", ", yaw, ")"],
}
welcome_msg = welcomeMsg("---", "simulation", log_stats)
welcome_info = welcome_msg("---", "simulation", log_stats)

urdf_packages = PythonExpression(["'", robot, "_description'"])
add_wheel_joints = LaunchConfiguration("add_wheel_joints", default="True")
Expand Down Expand Up @@ -134,7 +134,7 @@ def generate_launch_description():
declare_pitch_arg,
declare_yaw_arg,
SetUseSimTime(True),
welcome_msg,
welcome_info,
load_urdf,
spawn_robot,
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ void PantherSystemRosInterface::UpdateMsgErrorFlags(
front_driver_state.state.fault_flag = front.GetFaultFlag().GetMessage();
front_driver_state.state.script_flag = front.GetScriptFlag().GetMessage();
front_driver_state.state.channel_2_motor_runtime_error = front.GetLeftRuntimeError().GetMessage();
front_driver_state.state.channel_1_motor_runtime_error = front.GetRightRuntimeError().GetMessage();
front_driver_state.state.channel_1_motor_runtime_error =
front.GetRightRuntimeError().GetMessage();

rear_driver_state.state.fault_flag = rear.GetFaultFlag().GetMessage();
rear_driver_state.state.script_flag = rear.GetScriptFlag().GetMessage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,15 @@ TEST_F(TestPantherSystemRosInterface, ErrorFlags)

EXPECT_TRUE(driver_state_msg_->driver_states.at(0).state.fault_flag.overheat);
EXPECT_TRUE(driver_state_msg_->driver_states.at(0).state.script_flag.encoder_disconnected);
EXPECT_TRUE(driver_state_msg_->driver_states.at(0).state.channel_2_motor_runtime_error.loop_error);
EXPECT_TRUE(
driver_state_msg_->driver_states.at(0).state.channel_2_motor_runtime_error.loop_error);
EXPECT_TRUE(
driver_state_msg_->driver_states.at(0).state.channel_1_motor_runtime_error.safety_stop_active);

EXPECT_TRUE(driver_state_msg_->driver_states.at(1).state.fault_flag.overvoltage);
EXPECT_TRUE(driver_state_msg_->driver_states.at(1).state.script_flag.loop_error);
EXPECT_TRUE(
driver_state_msg_->driver_states.at(1).state.channel_2_motor_runtime_error.forward_limit_triggered);
EXPECT_TRUE(driver_state_msg_->driver_states.at(1)
.state.channel_2_motor_runtime_error.forward_limit_triggered);
EXPECT_TRUE(driver_state_msg_->driver_states.at(1)
.state.channel_1_motor_runtime_error.reverse_limit_triggered);
}
Expand Down
3 changes: 1 addition & 2 deletions panther_manager/src/safety_manager_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ void SafetyManagerNode::Initialize()
battery_sub_ = this->create_subscription<BatteryStateMsg>(
"battery/battery_status", 10, std::bind(&SafetyManagerNode::BatteryCB, this, _1));
driver_state_sub_ = this->create_subscription<RobotDriverStateMsg>(
"hardware/robot_driver_state", 10,
std::bind(&SafetyManagerNode::RobotDriverStateCB, this, _1));
"hardware/robot_driver_state", 10, std::bind(&SafetyManagerNode::RobotDriverStateCB, this, _1));
e_stop_sub_ = this->create_subscription<BoolMsg>(
"hardware/e_stop", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
std::bind(&SafetyManagerNode::EStopCB, this, _1));
Expand Down
142 changes: 142 additions & 0 deletions panther_utils/panther_utils/arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env python3

# Copyright 2018 Open Source Robotics Foundation, Inc.
# Copyright 2024 Husarion sp. z o.o.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
from typing import Iterable, Tuple

import yaml
from launch import LaunchContext, Substitution
from launch.actions import DeclareLaunchArgument
from launch.substitutions import EnvironmentVariable, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare

# Define valid configurations for each robot model
VALID_CONFIGURATIONS = {
"panther": {"wheel_type": ["WH01", "WH02", "WH03"]},
"lynx": {"wheel_type": ["WH04"]},
}


def resolve_path(path: str | Substitution) -> str:
"""Resolve a path that might be substitutions."""
if isinstance(path, Substitution):
return path.perform(LaunchContext())
return path


def load_yaml_file(path: str) -> dict:
"""Load YAML file and return its contents."""
try:
with open(path, "r") as file:
return yaml.safe_load(file)
except yaml.YAMLError as exc:
raise ValueError(f"Error reading YAML file: {exc}") from exc


def load_robot_configuration(yaml_data: dict) -> Tuple[str, dict]:
"""Retrieve first element data if exists; otherwise, return the base structure."""
if "configuration" not in yaml_data:
namespace = next(iter(yaml_data.keys()))
return namespace, yaml_data[namespace]
return "", yaml_data


def validate_configuration(yaml_data: dict) -> None:
"""Validate the robot model and wheel type configuration."""
robot_model = yaml_data.get("robot_model", "")
configuration_data = yaml_data.get("configuration", {})
wheel_type = configuration_data.get("wheel_type", "")

if robot_model not in VALID_CONFIGURATIONS:
raise ValueError(
f"Invalid robot model '{robot_model}'. "
f"Valid models are: {', '.join(VALID_CONFIGURATIONS.keys())}"
)

valid_wheel_types = VALID_CONFIGURATIONS[robot_model]["wheel_type"]
if wheel_type not in valid_wheel_types:
raise ValueError(
f"Invalid wheel type '{wheel_type}' for {robot_model}. "
f"Valid wheel types are: {', '.join(valid_wheel_types)}"
)


def create_launch_arguments(namespace: str, yaml_data: dict) -> Iterable[DeclareLaunchArgument]:
"""Generate ROS 2 launch description based on the YAML configuration."""
x, y, z = yaml_data.get("initial_pose", [0.0, 0.0, 0.0])
roll, pitch, yaw = yaml_data.get("initial_rotation", [0.0, 0.0, 0.0])
configuration_data = yaml_data.get("configuration", {})

return [
DeclareLaunchArgument(
"namespace",
default_value=EnvironmentVariable("ROBOT_NAMESPACE", default_value=namespace),
description="Add namespace to all launched nodes.",
),
DeclareLaunchArgument(
"robot_model",
default_value=EnvironmentVariable(
"ROBOT_MODEL", default_value=yaml_data.get("robot_model", "panther")
),
description="Specify robot model type.",
),
DeclareLaunchArgument(
"wheel_type",
default_value=configuration_data["wheel_type"],
description=(
"Specify the wheel type. If the selected wheel type is not 'custom', "
"the 'wheel_config_path' and 'controller_config_path' arguments will be "
"automatically adjusted and can be omitted."
),
),
DeclareLaunchArgument(
"x", default_value=x, description="Initial robot position in the global 'x' axis."
),
DeclareLaunchArgument(
"y", default_value=y, description="Initial robot position in the global 'y' axis."
),
DeclareLaunchArgument(
"z", default_value=z, description="Initial robot position in the global 'z' axis."
),
DeclareLaunchArgument(
"roll", default_value=roll, description="Initial robot 'roll' orientation."
),
DeclareLaunchArgument(
"pitch", default_value=pitch, description="Initial robot 'pitch' orientation."
),
DeclareLaunchArgument(
"yaw", default_value=yaw, description="Initial robot 'yaw' orientation."
),
]


def declare_robot_args(path: str | Substitution) -> Iterable[DeclareLaunchArgument]:
"""Declare launch arguments based on the YAML configuration files."""
path = resolve_path(path)
yaml_data = load_yaml_file(path)
namespace, robot_config = load_robot_configuration(yaml_data)
try:
validate_configuration(robot_config)
except ValueError as error:
print(f"Validation Error: {error}")
sys.exit(1)
list_of_args = create_launch_arguments(namespace, robot_config)
return list_of_args


path = PathJoinSubstitution([FindPackageShare("panther_bringup"), "config", "configuration.yaml"])
print(declare_robot_args(path))
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def flatten(lst):
return [lst]


def welcomeMsg(
def welcome_msg(
serial_number: SomeSubstitutionsType,
robot_hw_version: SomeSubstitutionsType,
additional_stats: Dict = {},
Expand Down

0 comments on commit e11b9ea

Please sign in to comment.