Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions autonomy/teleop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Teleop
## Samples
To run rosbridge listener:
```
ros2 run rosbridge_listener rosbridge_listener_node
```

To run rosbridge publisher:

```
ros2 run rosbridge_publisher rosbridge_publisher_node
```

## Running Rosbridge Server
Rosbridge server will relay ros topics to humanoid-server. To run, use:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here could you explain the distinction between the listener and the publisher

```
ros2 launch rosbridge_server rosbridge_websocket_launch.xml
```

## Full Setup for Testing VR -> ROS
These commands were run with the autonomy/samples folder deleted, since some packages may overlap

```bash

# In humanoid repo:
cd autonomy
colcon build
source install/setup.bash
ros2 run rosbridge_listener rosbridge_listener_node

# Then, in another terminal:
cd autonomy
source install/setup.bash
ros2 launch rosbridge_server rosbridge_websocket_launch.xml

# In humanoid-server repo:
npm run dev
```

Then, connect to ws://localhost:3000 (default websocket url) and send an example hand pose message:
```
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
```
The message should appear in the terminal where you ran the rosbridge listener node

61 changes: 61 additions & 0 deletions autonomy/teleop/rosbridge_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
cmake_minimum_required(VERSION 3.8)
project(rosbridge_example)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(sample_msgs REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

add_library(rosbridge_listener_lib src/rosbridge_listener_core.cpp)
add_library(rosbridge_publisher_lib src/rosbridge_publisher_core.cpp)

target_include_directories(rosbridge_listener_lib PUBLIC include)

add_executable(rosbridge_listener_node src/rosbridge_listener_node.cpp)
add_executable(rosbridge_publisher_node src/rosbridge_publisher_node.cpp)

ament_target_dependencies(rosbridge_listener_lib rclcpp std_msgs sample_msgs)
ament_target_dependencies(rosbridge_publisher_lib rclcpp std_msgs sample_msgs)

target_link_libraries(rosbridge_listener_node rosbridge_listener_lib)
target_link_libraries(rosbridge_publisher_node rosbridge_publisher_lib)

target_include_directories(rosbridge_listener_lib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(rosbridge_publisher_lib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

target_compile_features(rosbridge_listener_node PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
target_compile_features(rosbridge_publisher_node PUBLIC c_std_99 cxx_std_17)

install(TARGETS rosbridge_listener_node
DESTINATION lib/${PROJECT_NAME})
install(TARGETS rosbridge_publisher_node
DESTINATION lib/${PROJECT_NAME})

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

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

ament_package()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef ROSBRIDGE_LISTENER_NODE_HPP_
#define ROSBRIDGE_LISTENER_NODE_HPP_

#include "rclcpp/rclcpp.hpp"

#include "sample_msgs/msg/vr_hand_pose.hpp"

#include "rosbridge_listener_core.hpp"

/**
* Implementation of a ROS2 node that listens to the "unfiltered" and "filtered"
* topics and echoes the operating frequency of the topic to the console.
*/
class ROSbridgeListenerNode : public rclcpp::Node {
public:
ROSbridgeListenerNode();

private:
std::string
hand_pose_to_string(const sample_msgs::msg::VRHandPose::SharedPtr pose);

void hand_pose_callback(const sample_msgs::msg::VRHandPose::SharedPtr msg);

rclcpp::Subscription<sample_msgs::msg::VRHandPose>::SharedPtr hand_pose_sub_;
};

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef ROSBRIDGE_PUBLISHER_CORE_HPP_
#define ROSBRIDGE_PUBLISHER_CORE_HPP_

#include "rclcpp/rclcpp.hpp"

#include "sample_msgs/msg/vr_hand_pose.hpp"

class ROSbridgePublisherCore {
public:
explicit ROSbridgePublisherCore();

std::string hand_pose_to_string(const sample_msgs::msg::VRHandPose pose);

private:
};

#endif // AGGREGATOR_CORE_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef ROSBRIDGE_PUBLISHER_NODE_HPP_
#define ROSBRIDGE_PUBLISHER_NODE_HPP_

#include "rclcpp/rclcpp.hpp"

#include "sample_msgs/msg/vr_hand_pose.hpp"

#include "rosbridge_publisher_core.hpp"

/**
* Implementation of a ROS2 node that listens to the "unfiltered" and "filtered"
* topics and echoes the operating frequency of the topic to the console.
*/
class ROSbridgePublisherNode : public rclcpp::Node {
public:
ROSbridgePublisherNode();

private:
void timer_callback();

rclcpp::TimerBase::SharedPtr timer_;

rclcpp::Publisher<sample_msgs::msg::VRHandPose>::SharedPtr hand_pose_pub_;

ROSbridgePublisherCore rosbridge_publisher_;
};

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
return LaunchDescription([
Node(
package='rosbridge_example',
executable='rosbridge_listener_node',
),
Node(
package='rosbridge_example',
executable='rosbridge_publisher_node',
),
])
22 changes: 22 additions & 0 deletions autonomy/teleop/rosbridge_example/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>rosbridge_example</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="yichen@todo.todo">yichen</maintainer>
<license>TODO: License declaration</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>sample_msgs</depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Empty file.
38 changes: 38 additions & 0 deletions autonomy/teleop/rosbridge_example/src/rosbridge_listener_node.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "rosbridge_listener_node.hpp"

#include <algorithm>
#include <string>

ROSbridgeListenerNode::ROSbridgeListenerNode() : Node("rosbridge_listener") {

hand_pose_sub_ = this->create_subscription<sample_msgs::msg::VRHandPose>(
"teleop", 10,
std::bind(&ROSbridgeListenerNode::hand_pose_callback, this,
std::placeholders::_1));
}

std::string ROSbridgeListenerNode::hand_pose_to_string(
const sample_msgs::msg::VRHandPose::SharedPtr pose) {
std::string s;
std::vector<float> positions = pose->positions;
s += "[";
for (int i = 0; i < positions.size(); i++) {
s += std::to_string(positions[i]);
s += ",";
}
s += "]";
return s;
}

void ROSbridgeListenerNode::hand_pose_callback(
const sample_msgs::msg::VRHandPose::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "Received Hand Data: %s",
hand_pose_to_string(msg).c_str());
}

int main(int argc, char **argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ROSbridgeListenerNode>());
rclcpp::shutdown();
return 0;
}
19 changes: 19 additions & 0 deletions autonomy/teleop/rosbridge_example/src/rosbridge_publisher_core.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <chrono>
#include <cmath>

#include "rosbridge_publisher_core.hpp"

ROSbridgePublisherCore::ROSbridgePublisherCore() {}

std::string ROSbridgePublisherCore::hand_pose_to_string(
const sample_msgs::msg::VRHandPose pose) {
std::string s;
std::vector<float> positions = pose.positions;
s += "[";
for (int i = 0; i < positions.size(); i++) {
s += std::to_string(positions[i]);
s += ",";
}
s += "]";
return s;
}
31 changes: 31 additions & 0 deletions autonomy/teleop/rosbridge_example/src/rosbridge_publisher_node.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "rosbridge_publisher_node.hpp"

#include <chrono>
#include <memory>

using namespace std::chrono_literals;

ROSbridgePublisherNode::ROSbridgePublisherNode()
: Node("rosbridge_publisher"),
rosbridge_publisher_(ROSbridgePublisherCore()) {

hand_pose_pub_ =
this->create_publisher<sample_msgs::msg::VRHandPose>("teleop", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&ROSbridgePublisherNode::timer_callback, this));
}

void ROSbridgePublisherNode::timer_callback() {
auto message = sample_msgs::msg::VRHandPose();
message.positions = std::vector<float>(17);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'",
rosbridge_publisher_.hand_pose_to_string(message).c_str());
hand_pose_pub_->publish(message);
}

int main(int argc, char **argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ROSbridgePublisherNode>());
rclcpp::shutdown();
return 0;
}
3 changes: 2 additions & 1 deletion autonomy/wato_msgs/sample_msgs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ set(msg_files
msg/Filtered.msg
msg/FilteredArray.msg
msg/Unfiltered.msg
msg/Metadata.msg)
msg/Metadata.msg
msg/VRHandPose.msg)
rosidl_generate_interfaces(sample_msgs
${msg_files}
DEPENDENCIES std_msgs)
Expand Down
1 change: 1 addition & 0 deletions autonomy/wato_msgs/sample_msgs/msg/VRHandPose.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
float32[] positions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be defined as positions for each finger joint ie:
float32[] middle;
float32[] index;
float32[] ring; # with a comment for the order of the joints

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also include which hand it is / if it detects both hands at the same time have this for both the left and right

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a curl metric right now, is this placeholder?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is placeholder, i haven't put too much thought into the actual format yet

current msg is just used to test websocket communication

67 changes: 67 additions & 0 deletions docker/teleop/teleop.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
ARG BASE_IMAGE=ghcr.io/watonomous/robot_base/base:humble-ubuntu22.04

################################ Source ################################
FROM ${BASE_IMAGE} AS source

WORKDIR ${AMENT_WS}/src

# Copy source code
COPY autonomy/teleop teleop
COPY autonomy/wato_msgs/sample_msgs sample_msgs

# Install rosdep if not present, update package lists
RUN apt-get update && \
apt-get install -y --no-install-recommends python3-rosdep && \
rm -rf /var/lib/apt/lists/*

# Update rosdep database (safe in containers)
RUN rosdep update

# Generate dependency list (simulated install → extract apt packages)
RUN rosdep install \
--from-paths . \
--ignore-src \
--rosdistro $ROS_DISTRO \
-y \
--simulate | \
grep "apt-get install" | \
sed 's/apt-get install -y //' > /tmp/colcon_install_list || true


################################ Dependencies ################################
FROM ${BASE_IMAGE} AS dependencies

# Copy dependency list from source stage
COPY --from=source /tmp/colcon_install_list /tmp/colcon_install_list

# Install dependencies + tools (update must be in same layer)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
$(cat /tmp/colcon_install_list) && \
rm -rf /var/lib/apt/lists/*

# Copy source code into workspace
WORKDIR ${AMENT_WS}
COPY --from=source ${AMENT_WS}/src src


################################ Build ################################
FROM dependencies AS build

WORKDIR ${AMENT_WS}

# Build ROS2 workspace
RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
colcon build \
--cmake-args -DCMAKE_BUILD_TYPE=Release \
--install-base ${WATONOMOUS_INSTALL}

# Remove source + build artifacts (keeps only install)
RUN rm -rf src build log

# Pass udev symlinks into container
ENV UDEV=1

# Entrypoint
COPY docker/wato_ros_entrypoint.sh ${AMENT_WS}/wato_ros_entrypoint.sh
ENTRYPOINT ["./wato_ros_entrypoint.sh"]
Loading
Loading