Skip to content
21 changes: 15 additions & 6 deletions workflows/robotic_ultrasound/scripts/simulation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,17 @@ Optionally, the simulator supports customization through JSON configuration file

```json
{
"probe_type": "curvilinear", // or "linear"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think // or "linear" is allowed format in JSON.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed the comment

"probe_params": {
"num_elements": 4096,
"num_elements": 256,
"opening_angle": 73.0,
"radius": 45.0,
"frequency": 2.5,
"elevational_height": 7.0,
"num_el_samples": 10
"num_el_samples": 1,
"f_num": 1.0,
"speed_of_sound": 1.54,
"pulse_duration": 2.0
Copy link
Contributor

Choose a reason for hiding this comment

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

May I know where you get these numbers?

Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are the defaults from raysim.
e.g. here for curvilinear

},
"sim_params": {
"conv_psf": true,
Expand All @@ -262,12 +266,15 @@ Optionally, the simulator supports customization through JSON configuration file

| Parameter | Description | Default Value |
|-----------|-------------|---------------|
| num_elements | Number of elements in the ultrasound probe | 4096 |
| opening_angle | Beam opening angle in degrees | 73.0 |
| radius | Radius of the ultrasound probe in mm | 45.0 |
| num_elements | Number of elements in the ultrasound probe | 256 |
| opening_angle | Beam opening angle in degrees | 73.0 (curvilinear) / 0.0 (linear) |
| radius | Radius of the ultrasound probe in mm | 45.0 (curvilinear) / 0.0 (linear) |
| frequency | Ultrasound frequency in MHz | 2.5 |
| elevational_height | Height of the elevation plane in mm | 7.0 |
| num_el_samples | Number of samples in the elevation direction | 10 |
| num_el_samples | Number of samples in the elevation direction | 1 |
| f_num | F-number (unitless) | 1.0 |
| speed_of_sound | Speed of sound in mm/us | 1.54 |
| pulse_duration | Pulse duration in cycles | 2.0 |

##### Simulation Parameters

Expand All @@ -283,6 +290,7 @@ You only need to specify the parameters you want to change - any omitted paramet

```json
{
"probe_type": "linear",
"probe_params": {
"frequency": 3.5,
"radius": 55.0
Expand Down Expand Up @@ -324,3 +332,4 @@ To see the ultrasound probe moving, please ensure the `topic_ultrasound_info` is
| --topic_out | Topic name to publish generated ultrasound data | topic_ultrasound_data |
| --config | Path to custom JSON configuration file with probe parameters and simulation parameters | None |
| --period | Period of the simulation (in seconds) | 1/30.0 (30 Hz) |
| --probe_type | Type of ultrasound probe to use ("curvilinear" or "linear") | "curvilinear" |
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class Simulator(Operator):
out_width: The width of the output image.
start_pose: The initial pose of the probe, default is [0, 0, 0, 0, 0, 0].
config_path: Path to a JSON configuration file with probe parameters.
probe_type: Type of ultrasound probe to use ("curvilinear" or "linear").
"""

def __init__(
Expand All @@ -135,13 +136,15 @@ def __init__(
out_width=500,
start_pose=np.zeros((6,)),
config_path=None,
probe_type="curvilinear",
**kwargs,
):
# Initialize all critical variables first
self.out_height = out_height
self.out_width = out_width
self.start_pose = start_pose
self.config_path = config_path
self.probe_type = probe_type
self.simulator = None
self.sim_params = None
self.probe = None
Expand Down Expand Up @@ -229,13 +232,28 @@ def add_mesh(filename, material_name):
initial_pose = rs.Pose(position, rotation)

# Load probe configuration from JSON file if provided
default_probe_params = {
"num_elements": 4096,
"opening_angle": 73.0,
"radius": 45.0,
"frequency": 2.5,
"elevational_height": 7.0,
"num_el_samples": 10,
default_curvilinear_probe_params = {
"num_elements": 256,
"opening_angle": 73.0, # degrees
"radius": 45.0, # mm
"frequency": 2.5, # MHz
"elevational_height": 7.0, # mm
"num_el_samples": 1,
"f_num": 1.0, # unitless
"speed_of_sound": 1.54, # mm/us
"pulse_duration": 2.0 # cycles
}

default_linear_probe_params = {
"num_elements": 256,
"opening_angle": 0.0, # degrees (linear array)
"radius": 0.0, # mm (linear array)
"frequency": 2.5, # MHz
"elevational_height": 7.0, # mm
"num_el_samples": 1,
"f_num": 1.0, # unitless
"speed_of_sound": 1.54, # mm/us
"pulse_duration": 2.0 # cycles
}

default_sim_params = {
Expand All @@ -244,18 +262,30 @@ def add_mesh(filename, material_name):
"t_far": 180.0,
}

probe_params = default_probe_params
# Default to curvilinear probe
probe_params = default_curvilinear_probe_params
sim_config = default_sim_params

if self.config_path:
try:
with open(self.config_path, "r") as f:
config_data = json.load(f)

# Load probe type
if "probe_type" in config_data:
self.probe_type = config_data["probe_type"]
if self.probe_type == "curvilinear":
probe_params = default_curvilinear_probe_params
elif self.probe_type == "linear":
probe_params = default_linear_probe_params
else:
print(f"Warning: Unknown probe type '{self.probe_type}'. Using curvilinear probe.")
probe_params = default_curvilinear_probe_params

# Load probe parameters
if "probe_params" in config_data:
# Use values from JSON, falling back to defaults for any missing parameters
for key in default_probe_params:
for key in probe_params:
if key in config_data["probe_params"]:
probe_params[key] = config_data["probe_params"][key]
print(f"Loaded probe configuration from {self.config_path}")
Expand All @@ -278,15 +308,31 @@ def add_mesh(filename, material_name):
print("No config file specified. Using default parameters.")

# Create ultrasound probe with configured parameters
self.probe = rs.UltrasoundProbe(
initial_pose,
num_elements=probe_params["num_elements"],
opening_angle=probe_params["opening_angle"],
radius=probe_params["radius"],
frequency=probe_params["frequency"],
elevational_height=probe_params["elevational_height"],
num_el_samples=probe_params["num_el_samples"],
)
if self.probe_type == "curvilinear":
self.probe = rs.CurvilinearProbe(
initial_pose,
num_elements_x=probe_params["num_elements"],
sector_angle=probe_params["opening_angle"],
radius=probe_params["radius"],
frequency=probe_params["frequency"],
elevational_height=probe_params["elevational_height"],
num_el_samples=probe_params["num_el_samples"],
f_num=probe_params["f_num"],
speed_of_sound=probe_params["speed_of_sound"],
pulse_duration=probe_params["pulse_duration"]
)
else: # linear
self.probe = rs.LinearArrayProbe(
initial_pose,
num_elements_x=probe_params["num_elements"],
width=60.0, # Default width for linear array
frequency=probe_params["frequency"],
elevational_height=probe_params["elevational_height"],
num_el_samples=probe_params["num_el_samples"],
f_num=probe_params["f_num"],
speed_of_sound=probe_params["speed_of_sound"],
pulse_duration=probe_params["pulse_duration"]
)

# Create simulator
try:
Expand Down Expand Up @@ -455,6 +501,7 @@ class StreamingSimulator(Application):
out_height: The height of the output image.
start_pose: The initial pose of the probe.
config_path: Path to a JSON configuration file with probe parameters.
probe_type: Type of ultrasound probe to use ("curvilinear" or "linear").
"""

def __init__(
Expand All @@ -467,6 +514,7 @@ def __init__(
start_pose=np.zeros((6,)),
period=1 / 30.0,
config_path=None,
probe_type="curvilinear",
):
super().__init__()
self.domain_id = domain_id
Expand All @@ -477,6 +525,7 @@ def __init__(
self.start_pose = start_pose
self.config_path = config_path
self.period = period
self.probe_type = probe_type

def compose(self):
"""
Expand All @@ -498,6 +547,7 @@ def compose(self):
out_width=self.out_width,
start_pose=self.start_pose,
config_path=self.config_path,
probe_type=self.probe_type,
name="simulator",
)
simulator.metadata_policy = MetadataPolicy.RAISE
Expand Down Expand Up @@ -552,6 +602,13 @@ def main():
default=1 / 30.0,
help="period of the simulation each cycle (1/frequency). Unit is second.",
)
parser.add_argument(
"--probe_type",
type=str,
choices=["curvilinear", "linear"],
default="curvilinear",
help="type of ultrasound probe to use (curvilinear or linear)",
)
args = parser.parse_args()
app = StreamingSimulator(
domain_id=args.domain_id,
Expand All @@ -561,6 +618,7 @@ def main():
out_height=args.height,
period=args.period,
config_path=args.config,
probe_type=args.probe_type,
)

app.is_metadata_enabled = True
Expand Down
Loading