Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development into master #587

Merged
merged 4 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 5 additions & 3 deletions docs/source/physical_robot_core_setup/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ On the RPi adjust the config in `/boot/config.txt` or on newer systems `/boot/fi
------------------
Setting up the RPi
------------------
**Note**: For students in the CI Group, the RPi is already set up. All RPis are flashed with the same image, so the following steps are not necessary. Additionally, there should be an IP address on the head, allowing you to SSH into it. However, be aware that there will always be ongoing development changes in the revolve2-modular-robot_physical and revolve2-robohat packages, so make sure to pip install the latest version in your virtual environment.
**Note**: For students in the CI Group, the RPi is already set up. If the heads are labeled as `flashed`, it means they are already flashed with the setup image, so the following steps are unnecessary. Additionally, the flashed heads are already connected to the *ThymioNet* Wi-Fi. However, the IP address on the head changes from time to time, so you should use the serial connection to log in and obtain the correct IP address. For instructions on how to establish a serial connection, please refer to the section below.
Also, note that ongoing development changes will continue in revolve2-modular-robot_physical and revolve2-robohat packages, so make sure to pip install the latest version in your virtual environment.

This step is the same for all types of hardware.

Expand Down Expand Up @@ -108,8 +109,8 @@ Setting up Revolve2 on the robot requires different steps, depending on the hard
* V1: :code:`pip install "revolve2-modular_robot_physical[botv1] @ git+https://github.com/ci-group/revolve2.git@<revolve_version>#subdirectory=modular_robot_physical"`.
* V2: :code:`pip install "revolve2-modular_robot_physical[botv2] @ git+https://github.com/ci-group/revolve2.git@<revolve_version>#subdirectory=modular_robot_physical"`.

For example, if you want to install the version tagged as 1.2.2, the command would be:
:code:`pip install "revolve2-modular_robot_physical[botv2] @ git+https://github.com/ci-group/[email protected].2#subdirectory=modular_robot_physical"`
For example, if you want to install the version tagged as 1.2.3, the command would be:
:code:`pip install "revolve2-modular_robot_physical[botv2] @ git+https://github.com/ci-group/[email protected].3#subdirectory=modular_robot_physical"`

#. Set up the Revolve2 physical robot daemon:
#. Create a systemd service file: :code:`sudo nano /etc/systemd/system/robot-daemon.service`
Expand All @@ -135,6 +136,7 @@ Setting up Revolve2 on the robot requires different steps, depending on the hard
#. Here, the :code:`Nice=-10` line sets a high priority for the daemon (lower values are higher priority, with -20 being the highest priority). The :code:`-l` option in the :code:`ExecStart` line tells :code:`robot-daemon` to only listen on the localhost interface. The :code:`-n localhost` option ensures that robot-daemon only runs if it can connect to localhost (preventing certain failure cases).
#. Enable and start the service: :code:`sudo systemctl daemon-reload` & :code:`sudo systemctl enable robot-daemon` & :code:`sudo systemctl start robot-daemon`.
#. Check if it is running properly using: :code:`sudo systemctl status robot-daemon`
#. If it's not running properly, check the logs using: :code:`journalctl -u robot-daemon -e`

^^^^^^^^^^^^^^^^^^^
V1 Additional Steps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def make_body() -> (
body.core_v2.right_face.bottom,
body.core_v2.right_face.bottom.attachment,
)
"""Add a camera sensor to the core."""
"""Here we add a camera sensor to the core. If you don't have a physical camera attached, uncomment this line."""
body.core.add_sensor(
CameraSensor(position=Vector3([0, 0, 0]), camera_size=(480, 640))
)
Expand Down
2 changes: 1 addition & 1 deletion experimentation/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-experimentation"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Tools for experimentation."
readme = "../README.md"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion modular_robot/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-modular-robot"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Everything for defining modular robots."
readme = "../README.md"
authors = [
Expand Down
6 changes: 3 additions & 3 deletions modular_robot_physical/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-modular-robot-physical"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Everything for physical modular robot control. This package is intended to be installed on the modular robot hardware."
readme = "../README.md"
authors = [
Expand All @@ -27,12 +27,12 @@ packages = [{ include = "revolve2" }]

[tool.poetry.dependencies]
python = "^3.10,<3.12"
revolve2-modular-robot = "1.2.2"
revolve2-modular-robot = "1.2.3"
pyrr = "^0.10.3"
typed-argparse = "^0.3.1"
pycapnp = { version = "^2.0.0b2" }
pigpio = { version = "^1.78", optional = true }
revolve2-robohat = { version = "0.6.2", optional = true }
revolve2-robohat = { version = "0.6.3", optional = true }
rpi-lgpio = { version = "0.5", optional = true }
opencv-python = "^4.10.0.84"
# cpnp-stub-generator is disabled because it depends on pycapnp <2.0.0.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import Sequence
from typing import Optional, Sequence

import numpy as np
from numpy.typing import NDArray
Expand Down Expand Up @@ -79,7 +79,7 @@ def get_imu_specific_force(self) -> Vector3:
"""

@abstractmethod
def get_camera_view(self) -> NDArray[np.uint8]:
def get_camera_view(self) -> Optional[NDArray[np.uint8]]:
"""
Get the current view from the camera.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import math
from typing import Sequence
from typing import Optional, Sequence

import numpy as np
from numpy.typing import NDArray
Expand Down Expand Up @@ -178,11 +178,18 @@ def get_imu_specific_force(self) -> Vector3:
raise RuntimeError("Could not get IMU acceleration reading!")
return Vector3(accel)

def get_camera_view(self) -> NDArray[np.uint8]:
def get_camera_view(self) -> Optional[NDArray[np.uint8]]:
"""
Get the current view from the camera.

:returns: An image captured from robohatlib.
"""
image = self.cam.get_capture_array().astype(np.uint8)
return image
try:
image = self.cam.get_capture_array()
if image is None:
print("No image captured (camera may not be available).")
return None
return image.astype(np.uint8)
except RuntimeError as e:
print(f"Runtime error encountered: {e}")
return None
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,17 @@ def _get_camera_sensor_state(
:param camera_sensor: The sensor in question.
:param sensor_readings: The sensor readings.
:return: The Sensor state.
:raises RuntimeError: If the camera image is empty.
"""
if camera_sensor is None:
return {}
else:
image = sensor_readings.cameraView
if len(image.r) == 0 and len(image.g) == 0 and len(image.b) == 0:
raise RuntimeError(
"Camera image is empty. Are you sure you have attached a camera? "
"If you don't want to get the camera state, don't add it to the body."
)
return {
UUIDKey(camera_sensor): CameraSensorStateImpl(
_capnp_to_camera_view(
Expand All @@ -343,10 +350,16 @@ def _display_camera_view(

:param camera_sensor: The sensor in question.
:param sensor_readings: The sensor readings.
:raises RuntimeError: If the camera image is empty.
"""
if camera_sensor is None:
print("No camera sensor found.")
raise RuntimeError(
"Can't display camera because there is no camera added in the body"
)
else:
image = sensor_readings.cameraView
if len(image.r) == 0 and len(image.g) == 0 and len(image.b) == 0:
raise RuntimeError("Image is emtpy so nothing can be displayed")
rgb_image = _capnp_to_camera_view(
sensor_readings.cameraView, camera_sensor.camera_size
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ def _get_sensor_readings(
pins_readings.append(value)

battery = self._physical_interface.get_battery_level()

imu_orientation = self._physical_interface.get_imu_orientation()
imu_specific_force = self._physical_interface.get_imu_specific_force()
imu_angular_rate = self._physical_interface.get_imu_angular_rate()
Expand Down Expand Up @@ -284,7 +283,7 @@ def _vector3_to_capnp(vector: Vector3) -> capnpVector3:
)

@staticmethod
def _camera_view_to_capnp(image: NDArray[np.uint8]) -> capnpImage:
def _camera_view_to_capnp(image: NDArray[np.uint8] | None) -> capnpImage:
"""
Convert an image as an NDArray into an capnp compatible Image.

Expand All @@ -294,8 +293,15 @@ def _camera_view_to_capnp(image: NDArray[np.uint8]) -> capnpImage:
:return: The capnp Image object.
"""
# Convert each channel to a list of Int32 for Cap'n Proto
return robot_daemon_protocol_capnp.Image(
r=image[:, :, 0].astype(np.int32).flatten().tolist(),
g=image[:, :, 1].astype(np.int32).flatten().tolist(),
b=image[:, :, 2].astype(np.int32).flatten().tolist(),
)
if image is None:
return robot_daemon_protocol_capnp.Image(
r=[],
g=[],
b=[],
)
else:
return robot_daemon_protocol_capnp.Image(
r=image[:, :, 0].astype(np.int32).flatten().tolist(),
g=image[:, :, 1].astype(np.int32).flatten().tolist(),
b=image[:, :, 2].astype(np.int32).flatten().tolist(),
)
6 changes: 3 additions & 3 deletions modular_robot_simulation/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-modular-robot-simulation"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Functionality to define scenes with modular robots in a terrain and simulate them."
readme = "../README.md"
authors = [
Expand All @@ -27,8 +27,8 @@ packages = [{ include = "revolve2" }]

[tool.poetry.dependencies]
python = "^3.10,<3.12"
revolve2-modular-robot = "1.2.2"
revolve2-simulation = "1.2.2"
revolve2-modular-robot = "1.2.3"
revolve2-simulation = "1.2.3"

[tool.poetry.extras]
dev = []
2 changes: 1 addition & 1 deletion simulation/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-simulation"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Physics simulation abstraction layer."
readme = "../README.md"
authors = [
Expand Down
4 changes: 2 additions & 2 deletions simulators/mujoco_simulator/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-mujoco-simulator"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: MuJoCo simulator."
readme = "../../README.md"
authors = [
Expand All @@ -28,7 +28,7 @@ packages = [{ include = "revolve2" }]

[tool.poetry.dependencies]
python = "^3.10,<3.12"
revolve2-simulation = "1.2.2"
revolve2-simulation = "1.2.3"
mujoco-python-viewer = "^0.1.3"
mujoco = "^2.2.0"
dm-control = "^1.0.3"
Expand Down
4 changes: 2 additions & 2 deletions standards/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "revolve2-standards"
version = "1.2.2"
version = "1.2.3"
description = "Revolve2: Standard tools, parameters, terrains, robots and more for simulations and experiments."
readme = "../README.md"
authors = [
Expand Down Expand Up @@ -38,7 +38,7 @@ script = "revolve2/standards/morphological_novelty_metric/_build_cmodule.py"

[tool.poetry.dependencies]
python = "^3.10,<3.12"
revolve2-modular-robot-simulation = "1.2.2"
revolve2-modular-robot-simulation = "1.2.3"
noise = "^1.2.2"
multineat = "^0.12"
sqlalchemy = "^2.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def __evaluate_cppn(

The output ranges between [0,1] and we have 4 rotations available (0, 90, 180, 270).
"""
angle = max(0, int(outputs[0] * 4 - 1e-6)) * (np.pi / 2.0)
angle = max(0, int(outputs[1] * 4 - 1e-6)) * (np.pi / 2.0)

return module_type, angle

Expand Down
Loading