Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9ccfaaa
install meshes
Apr 11, 2025
08b205e
3D meshes
Apr 11, 2025
0b1c5e6
add 3D models
Apr 11, 2025
5a8645b
simple geometry, depricated
Apr 11, 2025
f2dfc12
add collision
Apr 12, 2025
15eb4c9
revert to old wire frame
Apr 12, 2025
f5975a8
use simple collision model for the base
Apr 12, 2025
835db7b
wip gazebo
Apr 12, 2025
f9ea1bc
wip
Apr 13, 2025
5bf8e90
move limb macro into separate file
Apr 13, 2025
ab75ed3
gazebo sim
Apr 14, 2025
d8a2ed3
expose topics through remappings
Apr 20, 2025
7573dc4
jooint command renamed to joint_setpoints
Apr 20, 2025
da7b64e
gazebo sim
Apr 20, 2025
9d2da38
add simulations
Apr 20, 2025
b57fb1c
remove absolute path, poential bug
May 10, 2025
5cd62b1
docker-compose env vars
May 10, 2025
6b628cc
add namespace
May 10, 2025
140281f
change mode from launch argument
May 10, 2025
ff0b5b4
commit out vel_mux
May 10, 2025
e7858bf
fix README
May 11, 2025
d784225
add namespace to support multible robots on same network
May 11, 2025
c2025d7
update reamde
May 11, 2025
eb0f8ab
move gazebo simulation to separate folder
May 24, 2025
17c60e7
remove gazebo sim launch file from core package
May 24, 2025
2375081
add forward_position_controller
May 24, 2025
a96caf2
add foot to the xacro
May 24, 2025
cdc81e6
bridge to be used for simulation
May 24, 2025
62f3932
update readme with new gazebo simulation
May 24, 2025
38ebbd6
rename root folder
May 24, 2025
80cf31b
option to publish joints angles on startup
May 25, 2025
55ba166
updates
May 25, 2025
b420fc6
use bullet
May 25, 2025
8b36fa5
revert to table configuration, until we get better motors with good l…
May 25, 2025
aaba064
fix
May 25, 2025
de463ea
add base twerk on button press
May 25, 2025
b76ef0b
add more twerk modes
May 28, 2025
adfb584
improve parameters handling and target goal value checks
May 29, 2025
2eddf1b
round time into multiples of period
May 29, 2025
ae9e355
update timing interval
Jun 3, 2025
6cb2f65
add teleop package into main bringup
Jun 3, 2025
a2c40fa
fix rviz not finding stl files
Jun 8, 2025
546969a
gait frequency and step height into yaml
Jun 8, 2025
e1b54b0
gait frequency and step height into yaml
Jun 8, 2025
bd59f4c
allow pitch control using D-pad
Jun 8, 2025
3a4f29c
add missing packages
Jul 27, 2025
0e57dee
smoothen joystick's linear and angular velocities
Sep 27, 2025
cb666d7
servo (hiwonder) motor driver
Oct 25, 2025
858dcd7
fix update rate
Oct 25, 2025
8bbb483
select motors interface from launch arg
Oct 25, 2025
a88fcc0
fix readme
Oct 25, 2025
3e55c6f
fix loading hiwonder parameters
Nov 4, 2025
995274c
various fixes
Nov 4, 2025
bd7ff4c
Merge pull request #16 from Modi1987/hiwonder_driver
Modi1987 Nov 4, 2025
f216505
fix hiwonder driver
Nov 4, 2025
997fcfc
Merge pull request #18 from Modi1987/fix/hiwonder-driver
Modi1987 Nov 4, 2025
9d61573
revert majour changes and keep hiwonder vs hobby servo conditional la…
Nov 13, 2025
22e1767
add hiwonder feature by merging develop into add-stl
Nov 13, 2025
2c017fd
remap joints setpoints
Nov 13, 2025
433df42
Remap hiwonder joints stepoints topic. Merge branch 'develop' into ad…
Nov 13, 2025
53c1eeb
remapping and adding name space
Nov 13, 2025
10785ce
add instruction on how to launch with Hiwonder motors
Nov 13, 2025
dfa3883
Merge branch 'develop' into add-stl
Nov 13, 2025
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
52 changes: 50 additions & 2 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,59 @@ RUN apt install -y wget python3-rosdep

# Init and update rosdep
RUN [ -f /etc/ros/rosdep/sources.list.d/20-default.list ] || rosdep init && \
rosdep update --rosdistro=humble
rosdep update --rosdistro=${ROS_DISTRO}

# Install ROS 2 control
RUN apt update && apt install -y \
ros-${ROS_DISTRO}-ros2-control \
ros-${ROS_DISTRO}-ros2-controllers \
ros-${ROS_DISTRO}-controller-manager \
ros-${ROS_DISTRO}-joint-state-broadcaster

# Install nav2
RUN apt install -y ros-${ROS_DISTRO}-navigation2 ros-${ROS_DISTRO}-nav2-bringup ros-${ROS_DISTRO}-rmw-cyclonedds-cpp

# Install Gazebo Classic and ROS 2 Gazebo integration
RUN apt update && apt install -y \
ros-${ROS_DISTRO}-gazebo-ros \
ros-${ROS_DISTRO}-gazebo-ros2-control \
ros-${ROS_DISTRO}-joint-state-publisher \
ros-${ROS_DISTRO}-joint-state-publisher-gui \
ros-dev-tools \
ros-${ROS_DISTRO}-xacro \
ros-${ROS_DISTRO}-gazebo-plugins \
ros-${ROS_DISTRO}-gazebo-ros-pkgs \
ros-${ROS_DISTRO}-gazebo-dev

# Install additional ROS2 utils
RUN apt-get update && apt-get install -y \
ros-${ROS_DISTRO}-rqt \
ros-${ROS_DISTRO}-rqt-common-plugins \
ros-${ROS_DISTRO}-rqt-gui \
ros-${ROS_DISTRO}-rqt-gui-py \
ros-${ROS_DISTRO}-rqt-image-view \
ros-${ROS_DISTRO}-rqt-robot-steering \
ros-${ROS_DISTRO}-rqt-tf-tree \
ros-${ROS_DISTRO}-tf2-tools \
ros-${ROS_DISTRO}-tf2-geometry-msgs \
ros-${ROS_DISTRO}-plotjuggler \
ros-${ROS_DISTRO}-plotjuggler-ros

# Install adafruit i2c librarries
RUN apt install -y \
python3-pip \
python3-setuptools \
python3-wheel \
python3-smbus \
i2c-tools

RUN pip3 install --no-cache-dir \
RPi.GPIO \
adafruit-blinka

# Set up the workspace directory
WORKDIR $COLCON_WORKSPACE/src

# Add sourcing ROS setup.bash to .bashrc
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> ~/.bashrc
RUN echo "export ROS_DOMAIN_ID=91" >> ~/.bashrc

16 changes: 13 additions & 3 deletions .devcontainer/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@ services:
COLCON_WORKSPACE: /root/ros_ws
volumes:
- ../:/root/ros_ws/src
- /dev:/dev
- /tmp/.X11-unix:/tmp/.X11-unix
environment:
- ROS_DOMAIN_ID=91
network_mode: "host"
command: ["/bin/bash", "-c", "/root/ros_ws/src/.devcontainer/entrypoint.sh"]
- DISPLAY=${DISPLAY}
- QT_X11_NO_MITSHM=1
privileged: true
devices:
- "/dev:/dev"
- /dev/bus/usb:/dev/bus/usb
- /dev/i2c-0:/dev/i2c-0
- /dev/i2c-1:/dev/i2c-1
tty: true
command: [ "/bin/bash", "-c", "/root/ros_ws/src/.devcontainer/entrypoint.sh" ]

scorpion-ros2-test:
build:
Expand All @@ -23,4 +33,4 @@ services:
environment:
- ROS_DOMAIN_ID=91
network_mode: "host"
command: ["/bin/bash", "-c", "source /opt/ros/humble/setup.bash && cd /root/ros_ws && colcon test"]
command: [ "/bin/bash", "-c", "source /opt/ros/humble/setup.bash && cd /root/ros_ws && colcon test" ]
93 changes: 85 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![Powered by Dev Containers](https://img.shields.io/badge/Powered%20by-Dev%20Containers-blue?logo=devcontainers&logoColor=white)](https://containers.dev/)


# Scorpion
# Pentapod

This is a ROS2 software for the following older project [youtube video available here.](https://youtu.be/kcvJR5mcb1o?si=lxt_06UO4189CPcX)

Expand All @@ -21,17 +21,17 @@ cd ~/my_ws/src
Clone the repo

```
git clone git@github.com:Modi1987/scorpion.git
git clone git@github.com:Modi1987/pentapod.git

cd scorpion
cd pentapod

git checkout i2c_actuators
```

There are bash scripts that will allow you to build your workspace you can do this on your PC if you want to run RVIZ simulations, or on Raspberry-pi4 if you want to run on real-robot

```
cd ~/my_ws/src/scorpion/scripts
cd ~/my_ws/src/pentapod/scripts

./setup_penta_workspace.sh
```
Expand All @@ -41,7 +41,7 @@ cd ~/my_ws/src/scorpion/scripts
besides to the previous steps to setup your workspace, to control the real-robot you will need to configure the i2c bus on Raspberry-pi 4, to do so on the Raspberry-pi 4:

```
cd ~/my_ws/src/scorpion/scripts
cd ~/my_ws/src/pentapod/scripts

./setup_i2c_on_raspberry_pi4.sh
```
Expand All @@ -64,22 +64,30 @@ Finally, you can move the robot around from external PC using keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard
```

## If you are using Hiwonder motors over serial bus

'''
ros2 launch penta_pod realhardware_bringup.launch.py motors_interface:=hiwonder
'''

## Setup as a service (automatic on boot)

To bring-up the robot automatically each time you boot the robot (without having to launch the brinup manually), run the following script on the Raspberry Pi

```
cd ~/my_ws/src/scorpion/scripts
cd ~/my_ws/src/pentapod/scripts

./setup_service_penta_bringup_on_boot.sh
```

## Run in simulation

### Using Rviz

You can run the simulation using the command

```
ros2 launch penta_pod penta_simn_rviz.launch.py
ros2 launch penta_pod penta_sim_rviz.launch.py
```

You can move the robot around using
Expand All @@ -88,6 +96,13 @@ You can move the robot around using
ros2 run teleop_twist_keyboard teleop_twist_keyboard
```

### Gazebo simulation

You can also run a gazebo simulation using

```
ros2 launch penta_gazebo_sim penta_sim_full_gazebo.launch.py
```

## Using devcontainers

Expand Down Expand Up @@ -131,4 +146,66 @@ cd ..
source install/setup.bash

ros2 launch penta_pod penta_pod.launch.py
```
```


# Swarm of robots on same network

You can create a swarm of the real robot, all of them connected to eachothers sharing the same network, each robot will be distnguished by its own namespace as the following:

For 'robot1'

```
ros2 launch penta_pod realhardware_bringup.launch.py name_space:=robot1 mode:=real

```

For 'robot2'

```
ros2 launch penta_pod realhardware_bringup.launch.py name_space:=robot2 mode:=real

```

You can check that the topics are in the namespace, same for services and actions, you can use rqt introspection to show the ndoes graph.

```
~/ros_ws# ros2 topic list
/parameter_events
/robot1/actuator_setpoint_degree
/robot1/cmd_vel
/robot1/joint_setpoints
/robot1/joy
/robot1/joy/set_feedback
/robot1/limb0/joint_setpoints
/robot1/limb0/xyz_msg
/robot1/limb1/joint_setpoints
/robot1/limb1/xyz_msg
/robot1/limb2/joint_setpoints
/robot1/limb2/xyz_msg
/robot1/limb3/joint_setpoints
/robot1/limb3/xyz_msg
/robot1/limb4/joint_setpoints
/robot1/limb4/xyz_msg
/robot1/null_space_pose
/robot2/actuator_setpoint_degree
/robot2/cmd_vel
/robot2/joint_setpoints
/robot2/joy
/robot2/joy/set_feedback
/robot2/limb0/joint_setpoints
/robot2/limb0/xyz_msg
/robot2/limb1/joint_setpoints
/robot2/limb1/xyz_msg
/robot2/limb2/joint_setpoints
/robot2/limb2/xyz_msg
/robot2/limb3/joint_setpoints
/robot2/limb3/xyz_msg
/robot2/limb4/joint_setpoints
/robot2/limb4/xyz_msg
/robot2/null_space_pose
/rosout
/tf
```

You can publish on `/robot1/cmd_vel` to control first robot, and on `robot2/cmd_vel` to control second robot and so forth. If you do not have real robot you can use `mode:=virtual` and test
13 changes: 10 additions & 3 deletions base_twerk/include/base_twerk/base_twerk_action_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class BaseTwerkActionServer {

auto execute(const std::shared_ptr<GoalHandle> goal_handle) -> void;

auto is_target_valid(const std::shared_ptr<GoalHandle> goal_handle) -> bool;

auto declare_parameters() -> void;
auto get_parameters() -> void;

Expand All @@ -52,11 +54,16 @@ class BaseTwerkActionServer {
auto calculate_twerk_pose_from_goal(
rclcpp::Time start_time, PoseStamped start_pose,
const std::shared_ptr<GoalHandle> goal_handle) -> PoseStamped;

auto quiry_current_base_pose() -> std::optional<GetCurrentBasePose::Response>;

double update_interval_millis_double_;
double max_permissible_displacement_meter_;
struct TwerkYamlConfigs {
double update_interval_millis_double_;
double max_permissible_displacement_meter_;
double min_permissible_displacement_meter_;
double max_permissible_rotation_rad_;
double min_permissible_rotation_rad_;
} twerk_yaml_configs_;

struct ReceviedPoseStamped {
rclcpp::Time timestamp;
Expand Down
25 changes: 25 additions & 0 deletions base_twerk/include/base_twerk/base_twerk_helper_funs.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef BASE_TWERK_BASE_TWERK_HELPER_FUNS_HPP_
#define BASE_TWERK_BASE_TWERK_HELPER_FUNS_HPP_

#include "math.h"

auto get_rounded_dance_time_from_goal_millis(double w, int dance_time_millis)
-> std::chrono::milliseconds {

constexpr double to_millis = 1000.0;

auto dance_period_seconds = 2 * M_PI / w;
auto dance_time_seconds = static_cast<double>(dance_time_millis) / to_millis;

auto num_periods = dance_time_seconds / dance_period_seconds;

auto rounded_periods =
std::round(num_periods) + 1; // +1 to ensure at least one period

auto rounded_dance_time_millis =
static_cast<long>(rounded_periods * dance_period_seconds * to_millis);

return std::chrono::milliseconds(rounded_dance_time_millis);
}

#endif // BASE_TWERK_BASE_TWERK_HELPER_FUNS_HPP_
22 changes: 21 additions & 1 deletion base_twerk/launch/base_twerk_action_server.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,41 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration


def generate_launch_description():

ld = LaunchDescription()

""" Launch args """
name_space_arg = DeclareLaunchArgument(
"name_space",
default_value="",
description="Robot name space",
)
ld.add_action(name_space_arg)

""" Nodes """
config = os.path.join(
get_package_share_directory("penta_description"),
"config",
"general_config.yaml",
)
ld = LaunchDescription()
ld.add_action(
Node(
package="base_twerk",
executable="base_twerk_action_server_node",
output="screen",
namespace=LaunchConfiguration("name_space"),
parameters=[config],
remappings=[
# services
("cmd_null_setpoint", "base_twerk/cmd_null_setpoint"),
("get_current_null_pose", "base_twerk/get_current_null_pose"),
# topics
],
)
)
return ld
23 changes: 22 additions & 1 deletion base_twerk/launch/null_pose_publisher.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,42 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration


def generate_launch_description():

ld = LaunchDescription()

""" Launch args """
name_space_arg = DeclareLaunchArgument(
"name_space",
default_value="",
description="Robot name space",
)
ld.add_action(name_space_arg)

""" Nodes """
config = os.path.join(
get_package_share_directory("penta_description"),
"config",
"general_config.yaml",
)
ld = LaunchDescription()
ld.add_action(
Node(
package="base_twerk",
executable="base_twerk_publisher_node",
output="screen",
namespace=LaunchConfiguration("name_space"),
parameters=[config],
remappings=[
# services
("cmd_null_setpoint", "base_twerk/cmd_null_setpoint"),
("get_current_null_pose", "base_twerk/get_current_null_pose"),
# topics
("null_space_pose", "null_space_pose")
],
)
)
return ld
Loading