Skip to content

Commit

Permalink
New function and robot improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
rbonghi committed Feb 16, 2025
1 parent 3ec7935 commit 20f0f27
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 32 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [0.1.4]

### Features

- Redesign the simulation tool
- New diagnostic docker
- Added new function in `nanosaur robot terminal` to launch a terminal from docker

## [0.1.3] - 2025-02-15

### Features
Expand Down
43 changes: 30 additions & 13 deletions src/nanosaur/robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,7 @@ def parser_robot_menu(platform, subparsers: argparse._SubParsersAction, params:
robot_name = TerminalFormatter.color_text(robot_data.name, color='green', bold=True)
parser_robot = subparsers.add_parser('robot', help=f"Manage the Nanosaur robot [{robot_name}]")
robot_subparsers = parser_robot.add_subparsers(dest='robot_type', help="Robot operations")
# Add robot display subcommand
parser_robot_display = robot_subparsers.add_parser('rviz', help="Show the robot on rviz")
parser_robot_display.set_defaults(func=robot_display)
# Add robot drive subcommand
parser_robot_drive = robot_subparsers.add_parser('drive', help="Control the robot with keyboard")
parser_robot_drive.set_defaults(func=control_keyboard)

# Add robot start subcommand
parser_robot_start = robot_subparsers.add_parser('start', help="Activate the robot")
parser_robot_start.add_argument(
Expand All @@ -103,7 +98,15 @@ def parser_robot_menu(platform, subparsers: argparse._SubParsersAction, params:
# Add robot stop subcommand
parser_robot_stop = robot_subparsers.add_parser('stop', help="Deactivate the robot")
parser_robot_stop.set_defaults(func=docker.docker_robot_stop)

# Add robot display subcommand
parser_robot_display = robot_subparsers.add_parser('rviz', help="Show the robot on rviz")
parser_robot_display.set_defaults(func=robot_display)
# Add robot drive subcommand
parser_robot_drive = robot_subparsers.add_parser('drive', help="Control the robot with keyboard")
parser_robot_drive.set_defaults(func=control_keyboard)
# Add robot terminal subcommand
parser_robot_terminal = robot_subparsers.add_parser('terminal', help="Access the robot with terminal (only docker)")
parser_robot_terminal.set_defaults(func=control_terminal)
# Add robot config subcommand
parser_config = add_robot_config_subcommands(platform, robot_subparsers, params)

Expand Down Expand Up @@ -338,14 +341,30 @@ def robot_reset(platform, params: Params, args):
return True


def control_terminal(platform, params: Params, args):
"""Control the robot using the terminal."""
# Get the robot
robot = RobotList.current_robot(params)
if robot.simulation and 'simulation_tool' not in params:
print(TerminalFormatter.color_text("No simulation tool selected. Please run simulation set first.", color='red'))
return False
# Run from docker container
docker.docker_service_run_command(platform, params, "diagnostic", ["bash"], name=f"{robot.name}-terminal")
return True


def control_keyboard(platform, params: Params, args):
"""Control the robot using the keyboard."""
# Get the robot
robot = RobotList.current_robot(params)
if robot.simulation and 'simulation_tool' not in params:
print(TerminalFormatter.color_text("No simulation tool selected. Please run simulation set first.", color='red'))
return False
# Get location starting function (host or docker)
selected_location = workspace.get_starting_location(params)
if selected_location is None:
return False
# Get the robot
robot = RobotList.current_robot(params)
# Command to run
command = f"ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap /cmd_vel:=/{robot.name}/key_vel"
# Run from local machine
if selected_location == 'host':
Expand All @@ -357,8 +376,7 @@ def control_keyboard(platform, params: Params, args):
return True
elif selected_location == 'docker':
# Run from docker container
service = 'gazebo' # TODO: Change to the correct service
docker.docker_service_run_command(platform, params, service, shlex.split(command), name=f"{robot.name}-keyboard")
docker.docker_service_run_command(platform, params, "diagnostic", shlex.split(command), name=f"{robot.name}-keyboard")
return True
else:
print(TerminalFormatter.color_text(f"Unknown debug mode: {selected_location}", color='red'))
Expand Down Expand Up @@ -386,8 +404,7 @@ def robot_display(platform, params: Params, args):
return True
elif selected_location == 'docker':
# Run from docker container
service = 'gazebo' # TODO: Change to the correct service
docker.docker_service_run_command(platform, params, service, shlex.split(command), name=f"{robot.name}-rviz")
docker.docker_service_run_command(platform, params, "diagnostic", shlex.split(command), name=f"{robot.name}-rviz")
return True
else:
print(TerminalFormatter.color_text(f"Unknown debug mode: {selected_location}", color='red'))
Expand Down
7 changes: 5 additions & 2 deletions src/nanosaur/ros.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,16 @@ def run_colcon_build(ros2_path, folder_path) -> bool:
return False


def deploy_docker_image(dockerfile_path, tag_image):
def deploy_docker_image(dockerfile_path, tag_image, platforms=None) -> bool:
try:
print(TerminalFormatter.color_text(f"Building Docker image {tag_image}", color='magenta', bold=True))
if platforms:
print(TerminalFormatter.color_text(f"- for platforms: {platforms}", color='magenta', bold=True))
docker.build(
get_nanosaur_home(),
file=dockerfile_path,
tags=tag_image
tags=tag_image,
platforms=platforms,
)
print(TerminalFormatter.color_text("Docker image built successfully", color='green'))
return True
Expand Down
21 changes: 7 additions & 14 deletions src/nanosaur/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,17 +311,10 @@ def simulation_start_debug(simulation_ws_path, simulation_tool, headless, isaac_


def simulation_start(platform, params: Params, args):
# Get the Nanosaur version
nanosaur_version = params['nanosaur_version']
ros_distro = nsv.NANOSAUR_DISTRO_MAP[nanosaur_version]['ros']
# Check if debug mode is enabled
debug_mode = None
if 'ws_debug' in params:
debug_mode = params['ws_debug']
print(TerminalFormatter.color_text(f"Default debug mode: {debug_mode}", color='yellow'))
# Get the ROS 2 installation path if available
ros2_installed = get_ros2_path(ros_distro)
debug_mode = 'docker' if ros2_installed is None else debug_mode
# Get location starting function (host or docker)
selected_location = workspace.get_starting_location(params)
if selected_location is None:
return False
# Check which simulation tool is selected
if 'simulation_tool' not in params:
print(TerminalFormatter.color_text("No simulation tool selected. Please run simulation set first.", color='red'))
Expand All @@ -335,16 +328,16 @@ def simulation_start(platform, params: Params, args):
print(TerminalFormatter.color_text("No Isaac Sim version selected. Please run simulation set first.", color='red'))
return False
# Check if the debug mode is enabled
if debug_mode == 'host':
if selected_location == 'host':
nanosaur_ws_path = workspace.get_workspace_path(params, 'ws_simulation_name')
simulator_tool = params['simulation_tool']
headless = params.get('simulation_headless', False)
return simulation_start_debug(nanosaur_ws_path, simulator_tool, headless, params.get('isaac_sim_path', None))
elif debug_mode == 'docker':
elif selected_location == 'docker':
# Run from docker container
return docker_simulator_start(platform, params, args)
else:
print(TerminalFormatter.color_text(f"Unknown debug mode: {debug_mode}", color='red'))
print(TerminalFormatter.color_text(f"Unknown debug mode: {selected_location}", color='red'))
return False


Expand Down
4 changes: 4 additions & 0 deletions src/nanosaur/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
'robot': {
'x86_64': [],
'aarch64': ['nanosaur:robot'],
},
'diagnostic': {
'x86_64': ['nanosaur:diagnostic'],
'aarch64': [],
}
}
# EOF
39 changes: 36 additions & 3 deletions src/nanosaur/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def get_starting_location(params: utilities.Params) -> str:
inquirer.List(
'location',
message="Run locally or on docker?",
choices=['host', 'docker'],
choices=['docker', 'host'],
ignore=lambda answers: debug_mode,
),
]
Expand Down Expand Up @@ -188,6 +188,9 @@ def get_selected_workspace(params, workspace_actions, args):
# Get the workspaces
workspaces = get_workspaces_path(params)
workspaces = {k: v for k, v in workspaces.items() if k in workspace_actions}
# Add extra workspace diagnostic if exist
if params['mode'] in ['maintainer', 'Raffo'] and 'diagnostic' in workspace_actions:
workspaces['diagnostic'] = workspace_actions['diagnostic']
# Check if there are any workspaces
if not workspaces:
print(TerminalFormatter.color_text("No workspaces found.", color='red'))
Expand Down Expand Up @@ -429,11 +432,29 @@ def debug_simulation(params: utilities.Params, args):
)

return False

def debug_diagnostic():
# Create the Nanosaur home folder
nanosaur_home_path = utilities.get_nanosaur_home()
nanosaur_shared_src = os.path.join(nanosaur_home_path, "shared_src")
# Set the volumes
volumes = [
(nanosaur_shared_src, '/shared_src'),
]
# Get the robot object
robot = utilities.RobotList.current_robot(params)
container_name = f"{robot.name}-diagnostic"
# diagnostic in Docker container
return docker_service_run_command(
platform, params, "diagnostic", command=['bash'],
name=container_name, volumes=volumes
)

workspace_actions = {
'developer': lambda: ros.run_docker_isaac_ros(get_workspace_path(params, 'ws_developer_name')),
'simulation': lambda: debug_simulation(params, args),
'perception': lambda: ros.run_docker_isaac_ros(get_workspace_path(params, 'ws_perception_name'))
'perception': lambda: ros.run_docker_isaac_ros(get_workspace_path(params, 'ws_perception_name')),
'diagnostic': lambda: debug_diagnostic(),
}
workspace = get_selected_workspace(params, workspace_actions, args)
if workspace is None:
Expand Down Expand Up @@ -525,6 +546,16 @@ def deploy_perception(image_name):
return False
return True

def deploy_diagnostic():
# Get the path to the simulation workspace
shared_path = get_workspace_path(params, 'shared')
# Define tag image name and dockerfile path
tag_image = f"{nanosaur_docker_user}/nanosaur:diagnostic"
dockerfile_path = f"{shared_path}/Dockerfile"
platforms = ["linux/amd64", "linux/arm64"]
# Deploy the diagnostic workspace to the docker image for both platforms
return ros.deploy_docker_image(dockerfile_path, tag_image)

# Get the deploy action
workspace_actions = {
'developer': lambda: ros.deploy_docker_isaac_ros(
Expand All @@ -534,6 +565,7 @@ def deploy_perception(image_name):
),
'simulation': lambda: deploy_simulation(args.image_name),
'perception': lambda: deploy_perception(args.image_name),
'diagnostic': lambda: deploy_diagnostic(),
}
if args.all and args.image_name is not None:
workspaces = get_workspaces_path(params)
Expand Down Expand Up @@ -573,7 +605,8 @@ def get_workspace_path(params: utilities.Params, ws_name) -> str:
'ws_developer_name': params.get('ws_developer_name', nsv.DEFAULT_WORKSPACE_DEVELOPER),
'ws_robot_name': params.get('ws_robot_name', nsv.DEFAULT_WORKSPACE_ROBOT),
'ws_simulation_name': params.get('ws_simulation_name', nsv.DEFAULT_WORKSPACE_SIMULATION),
'ws_perception_name': params.get('ws_perception_name', nsv.DEFAULT_WORKSPACE_PERCEPTION)
'ws_perception_name': params.get('ws_perception_name', nsv.DEFAULT_WORKSPACE_PERCEPTION),
'shared': os.path.join('shared_src', 'nanosaur')
}
if ws_name not in workspaces:
return None
Expand Down

0 comments on commit 20f0f27

Please sign in to comment.