Compare commits
No commits in common. "50d1ebbdccec77630f851b17ae6de94b44050df9" and "d7051fe854921c976ddb5f91a845ba90f75c2115" have entirely different histories.
50d1ebbdcc
...
d7051fe854
@ -1,75 +0,0 @@
|
|||||||
# SaltyBot Autonomous Behavior Tree Coordinator (Issue #482)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Autonomous mode state machine with 5 states:
|
|
||||||
```
|
|
||||||
idle → patrol → investigate → interact → return → idle
|
|
||||||
```
|
|
||||||
|
|
||||||
## States
|
|
||||||
|
|
||||||
### IDLE: Waiting/Charging
|
|
||||||
- Robot stationary, waiting for activation
|
|
||||||
- Timeout: 60 seconds
|
|
||||||
- Transition: Moves to PATROL when activated
|
|
||||||
|
|
||||||
### PATROL: Autonomous Patrolling
|
|
||||||
- Executes waypoint routes within geofence (#441)
|
|
||||||
- Integrates curiosity (#470) for autonomous exploration
|
|
||||||
- Monitors for person detection
|
|
||||||
- Battery check: Returns to IDLE if <20%
|
|
||||||
|
|
||||||
### INVESTIGATE: Person Investigation
|
|
||||||
- Approaches and tracks detected person (#212)
|
|
||||||
- Fallback: Navigate to last known position
|
|
||||||
- Transition: → INTERACT when person approached
|
|
||||||
|
|
||||||
### INTERACT: Social Interaction
|
|
||||||
- Face recognition and greeting
|
|
||||||
- Gesture detection and response (#454)
|
|
||||||
- Timeout: 30 seconds
|
|
||||||
- Transition: → RETURN
|
|
||||||
|
|
||||||
### RETURN: Return to Home/Dock
|
|
||||||
- Navigates back to home pose
|
|
||||||
- Nav2 recovery behaviors with retries
|
|
||||||
- Transition: → IDLE when complete
|
|
||||||
|
|
||||||
## Blackboard Variables
|
|
||||||
|
|
||||||
```python
|
|
||||||
battery_level: float # Battery percentage (0-100)
|
|
||||||
person_detected: bool # Person detection state
|
|
||||||
obstacle_state: str # 'clear' | 'near' | 'blocked'
|
|
||||||
current_mode: str # State: idle/patrol/investigate/interact/return
|
|
||||||
home_pose: PoseStamped # Home/dock location
|
|
||||||
```
|
|
||||||
|
|
||||||
## Safety Features
|
|
||||||
|
|
||||||
- **E-Stop** (#459): Highest priority, halts operation immediately
|
|
||||||
- **Battery protection**: Returns home if <20%
|
|
||||||
- **Geofence** (#441): Keeps patrol within 5m boundary
|
|
||||||
- **Obstacle avoidance**: LIDAR monitoring
|
|
||||||
|
|
||||||
## Integration with Features
|
|
||||||
|
|
||||||
- **Patrol** (#446): Waypoint routes
|
|
||||||
- **Curiosity** (#470): Exploration during patrol
|
|
||||||
- **Person Following**: Approach detected persons
|
|
||||||
- **E-Stop** (#459): Emergency override
|
|
||||||
- **Geofence** (#441): Boundary constraint
|
|
||||||
|
|
||||||
## Launch
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ros2 launch saltybot_bringup autonomous_mode.launch.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
- `autonomous_coordinator.xml`: BehaviorTree definition
|
|
||||||
- `launch/autonomous_mode.launch.py`: Full launch setup
|
|
||||||
- `saltybot_bringup/bt_nodes.py`: Custom BT node plugins
|
|
||||||
- `BEHAVIOR_TREE_README.md`: Documentation
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<root BTCPP_format="4">
|
|
||||||
<BehaviorTree ID="AutonomousCoordinator">
|
|
||||||
<ReactiveFallback name="RootFallback">
|
|
||||||
<Inverter><CheckEmergencyStop/></Inverter>
|
|
||||||
<Sequence name="AutonomousSequence">
|
|
||||||
<SetBlackboardVariable var_name="current_mode" var_value="idle"/>
|
|
||||||
<Sequence name="IdleState">
|
|
||||||
<StopRobot/>
|
|
||||||
<WaitForActivation timeout_ms="60000"/>
|
|
||||||
</Sequence>
|
|
||||||
<Sequence name="PatrolState">
|
|
||||||
<SetBlackboardVariable var_name="current_mode" var_value="patrol"/>
|
|
||||||
<CheckBatteryLevel threshold="20"/>
|
|
||||||
<PatrolWithCuriosity/>
|
|
||||||
</Sequence>
|
|
||||||
<Sequence name="InvestigateState">
|
|
||||||
<SetBlackboardVariable var_name="current_mode" var_value="investigate"/>
|
|
||||||
<FollowDetectedPerson/>
|
|
||||||
</Sequence>
|
|
||||||
<Sequence name="InteractState">
|
|
||||||
<SetBlackboardVariable var_name="current_mode" var_value="interact"/>
|
|
||||||
<RecognizeAndGreet/>
|
|
||||||
<Wait wait_duration="30"/>
|
|
||||||
</Sequence>
|
|
||||||
<Sequence name="ReturnState">
|
|
||||||
<SetBlackboardVariable var_name="current_mode" var_value="return"/>
|
|
||||||
<NavigateToHome retry_count="3"/>
|
|
||||||
<StopRobot/>
|
|
||||||
</Sequence>
|
|
||||||
</Sequence>
|
|
||||||
</ReactiveFallback>
|
|
||||||
</BehaviorTree>
|
|
||||||
</root>
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
"""Autonomous Mode Launcher (Issue #482)"""
|
|
||||||
from launch import LaunchDescription
|
|
||||||
from launch_ros.actions import Node
|
|
||||||
from launch_ros.substitutions import FindPackageShare
|
|
||||||
from launch.substitutions import PathJoinSubstitution
|
|
||||||
|
|
||||||
def generate_launch_description():
|
|
||||||
saltybot_bringup_dir = FindPackageShare("saltybot_bringup")
|
|
||||||
bt_xml_dir = PathJoinSubstitution([saltybot_bringup_dir, "behavior_trees"])
|
|
||||||
return LaunchDescription([
|
|
||||||
Node(package="saltybot_bt_executor", executable="behavior_tree_executor", name="autonomous_coordinator",
|
|
||||||
parameters=[{"bt_xml_filename": PathJoinSubstitution([bt_xml_dir, "autonomous_coordinator.xml"])}]),
|
|
||||||
Node(package="saltybot_battery_monitor", executable="battery_monitor_node", name="battery_monitor"),
|
|
||||||
Node(package="saltybot_perception", executable="person_detector", name="person_detector"),
|
|
||||||
Node(package="saltybot_lidar_avoidance", executable="obstacle_monitor", name="obstacle_monitor"),
|
|
||||||
Node(package="saltybot_follower", executable="person_follower", name="person_follower"),
|
|
||||||
Node(package="saltybot_curiosity", executable="curiosity_explorer", name="curiosity_explorer"),
|
|
||||||
Node(package="saltybot_emergency_stop", executable="emergency_stop_monitor", name="emergency_stop_monitor"),
|
|
||||||
])
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
"""Custom Behavior Tree Node Plugins (Issue #482)"""
|
|
||||||
import rclpy
|
|
||||||
from rclpy.node import Node
|
|
||||||
from py_trees import behaviour, common
|
|
||||||
from geometry_msgs.msg import Twist
|
|
||||||
from sensor_msgs.msg import BatteryState
|
|
||||||
from std_msgs.msg import Float32, Bool
|
|
||||||
|
|
||||||
class GetBatteryLevel(behaviour.Behaviour):
|
|
||||||
def __init__(self, name, node: Node):
|
|
||||||
super().__init__(name)
|
|
||||||
self.node = node
|
|
||||||
self.battery_level = 100.0
|
|
||||||
self.subscription = node.create_subscription(BatteryState, '/battery_state', self._battery_callback, 10)
|
|
||||||
def _battery_callback(self, msg):
|
|
||||||
self.battery_level = msg.percentage * 100.0
|
|
||||||
def update(self) -> common.Status:
|
|
||||||
self.blackboard.set('battery_level', self.battery_level, overwrite=True)
|
|
||||||
return common.Status.SUCCESS
|
|
||||||
|
|
||||||
class CheckBatteryLevel(behaviour.Behaviour):
|
|
||||||
def __init__(self, name, node: Node, threshold: float = 50.0):
|
|
||||||
super().__init__(name)
|
|
||||||
self.node = node
|
|
||||||
self.threshold = threshold
|
|
||||||
self.battery_level = 100.0
|
|
||||||
self.subscription = node.create_subscription(BatteryState, '/battery_state', self._battery_callback, 10)
|
|
||||||
def _battery_callback(self, msg):
|
|
||||||
self.battery_level = msg.percentage * 100.0
|
|
||||||
def update(self) -> common.Status:
|
|
||||||
return common.Status.SUCCESS if self.battery_level >= self.threshold else common.Status.FAILURE
|
|
||||||
|
|
||||||
class CheckPersonDetected(behaviour.Behaviour):
|
|
||||||
def __init__(self, name, node: Node):
|
|
||||||
super().__init__(name)
|
|
||||||
self.node = node
|
|
||||||
self.person_detected = False
|
|
||||||
self.subscription = node.create_subscription(Bool, '/person_detection/detected', self._detection_callback, 10)
|
|
||||||
def _detection_callback(self, msg):
|
|
||||||
self.person_detected = msg.data
|
|
||||||
def update(self) -> common.Status:
|
|
||||||
self.blackboard.set('person_detected', self.person_detected, overwrite=True)
|
|
||||||
return common.Status.SUCCESS if self.person_detected else common.Status.FAILURE
|
|
||||||
|
|
||||||
class StopRobot(behaviour.Behaviour):
|
|
||||||
def __init__(self, name, node: Node):
|
|
||||||
super().__init__(name)
|
|
||||||
self.node = node
|
|
||||||
self.cmd_vel_pub = node.create_publisher(Twist, '/cmd_vel', 10)
|
|
||||||
def update(self) -> common.Status:
|
|
||||||
msg = Twist()
|
|
||||||
self.cmd_vel_pub.publish(msg)
|
|
||||||
return common.Status.SUCCESS
|
|
||||||
|
|
||||||
class SetBlackboardVariable(behaviour.Behaviour):
|
|
||||||
def __init__(self, name, node: Node, var_name: str, var_value):
|
|
||||||
super().__init__(name)
|
|
||||||
self.node = node
|
|
||||||
self.var_name = var_name
|
|
||||||
self.var_value = var_value
|
|
||||||
def update(self) -> common.Status:
|
|
||||||
self.blackboard.set(self.var_name, self.var_value, overwrite=True)
|
|
||||||
return common.Status.SUCCESS
|
|
||||||
@ -283,32 +283,4 @@
|
|||||||
<origin xyz="0 ${-sensor_arm_r} 0" rpy="0 ${cam_tilt} ${-pi/2}"/>
|
<origin xyz="0 ${-sensor_arm_r} 0" rpy="0 ${cam_tilt} ${-pi/2}"/>
|
||||||
</joint>
|
</joint>
|
||||||
|
|
||||||
<!-- Pan Servo Link -->
|
|
||||||
<link name="pan_servo">
|
|
||||||
<inertial>
|
|
||||||
<mass value="0.05"/>
|
|
||||||
<inertia ixx="0.000005" ixy="0.0" ixz="0.0" iyy="0.000005" iyz="0.0" izz="0.000005"/>
|
|
||||||
</inertial>
|
|
||||||
</link>
|
|
||||||
|
|
||||||
<joint name="pan_servo_joint" type="fixed">
|
|
||||||
<parent link="sensor_head_link"/>
|
|
||||||
<child link="pan_servo"/>
|
|
||||||
<origin xyz="${sensor_arm_r} 0 ${-0.020}" rpy="0 0 0"/>
|
|
||||||
</joint>
|
|
||||||
|
|
||||||
<!-- Tilt Servo Link -->
|
|
||||||
<link name="tilt_servo">
|
|
||||||
<inertial>
|
|
||||||
<mass value="0.05"/>
|
|
||||||
<inertia ixx="0.000005" ixy="0.0" ixz="0.0" iyy="0.000005" iyz="0.0" izz="0.000005"/>
|
|
||||||
</inertial>
|
|
||||||
</link>
|
|
||||||
|
|
||||||
<joint name="tilt_servo_joint" type="fixed">
|
|
||||||
<parent link="pan_servo"/>
|
|
||||||
<child link="tilt_servo"/>
|
|
||||||
<origin xyz="0 0 0.020" rpy="0 0 0"/>
|
|
||||||
</joint>
|
|
||||||
|
|
||||||
</robot>
|
</robot>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user